(很全面)SpringBoot 使用 Caffeine 本地缓存

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> (很全面)SpringBoot 使用 Caffeine 本地缓存

点击上方“Java知音”,选择“置顶公众号”

技术文章第一时间送达!

作者:超级小豆丁 http://www.mydlq.club/article/56/

目录

一、本地缓存介绍

二、缓存组件 Caffeine 介绍

  • Caffeine 性能
  • Caffeine 配置说明
  • 软引用与弱引用
  • Caffeine 配置说明

    三、SpringBoot 集成 Caffeine 两种方式

    四、SpringBoot 集成 Caffeine 方式一

  • Maven 引入相关依赖
  • 配置缓存配置类
  • 定义测试的实体对象
  • 定义服务接口类和实现类
  • 测试的 Controller 类
  • 配置缓存配置类

    定义服务接口类和实现类

    五、SpringBoot 集成 Caffeine 方式二

  • Maven 引入相关依赖
  • 配置缓存配置类
  • 定义测试的实体对象
  • 定义服务接口类和实现类
  • 测试的 Controller 类
  • 配置缓存配置类

    定义服务接口类和实现类

    环境配置:

  • JDK 版本:1.8
  • Caffeine 版本:2.8.0
  • SpringBoot 版本:2.2.2.RELEASE
  • Caffeine 版本:2.8.0

    参考地址:

    https://www.jianshu.com/p/c72fb0c787fc https://www.cnblogs.com/rickiyang/p/11074158.html 博文示例项目 Github 地址:https://github.com/my-dlq/blog-example/tree/master/springboot/springboot-caffeine-cache-example

    一、本地缓存介绍

    缓存在日常开发中启动至关重要的作用,由于是存储在内存中,数据的读取速度是非常快的,能大量减少对数据库的访问,减少数据库的压力。

    之前介绍过 Redis 这种 NoSql 作为缓存组件,它能够很好的作为分布式缓存组件提供多个服务间的缓存,但是 Redis 这种还是需要网络开销,增加时耗。本地缓存是直接从本地内存中读取,没有网络开销,例如秒杀系统或者数据量小的缓存等,比远程缓存更合适。

    二、缓存组件 Caffeine 介绍

    按 Caffeine Github 文档描述,Caffeine 是基于 JAVA 8 的高性能缓存库。并且在 spring5 (springboot 2.x) 后,spring 官方放弃了 Guava,而使用了性能更优秀的 Caffeine 作为默认缓存组件。

    1、Caffeine 性能

    可以通过下图观测到,在下面缓存组件中 Caffeine 性能是其中最好的。

    (很全面)SpringBoot 使用 Caffeine 本地缓存

    2、Caffeine 配置说明

    (很全面)SpringBoot 使用 Caffeine 本地缓存

    注意:

  • weakValues 和 softValues 不可以同时使用。
  • maximumSize 和 maximumWeight 不可以同时使用。
  • expireAfterWrite 和 expireAfterAccess 同事存在时,以 expireAfterWrite 为准。
  • maximumSize 和 maximumWeight 不可以同时使用。

    3、软引用与弱引用

    软引用:如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。

    弱引用:弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存

    
    // 软引用
    Caffeine.newBuilder().softValues().build();
    
    // 弱引用
    Caffeine.newBuilder().weakKeys().weakValues().build();
    

    三、SpringBoot 集成 Caffeine 两种方式

    SpringBoot 有俩种使用 Caffeine 作为缓存的方式:

  • 方式一:直接引入 Caffeine 依赖,然后使用 Caffeine 方法实现缓存。
  • 方式二:引入 Caffeine 和 Spring Cache 依赖,使用 SpringCache 注解方法实现缓存。
  • 方式二:引入 Caffeine 和 Spring Cache 依赖,使用 SpringCache 注解方法实现缓存。

    下面将介绍下,这俩中集成方式都是如何实现的。

    四、SpringBoot 集成 Caffeine 方式一

    1、Maven 引入相关依赖

    
    ?xml version="1.0" encoding="UTF-8"?
    project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"
        modelVersion4.0.0/modelVersion
    
        parent
            groupIdorg.springframework.boot/groupId
            artifactIdspring-boot-starter-parent/artifactId
            version2.2.2.RELEASE/version
        /parent
    
        groupIdmydlq.club/groupId
        artifactIdspringboot-caffeine-cache-example-1/artifactId
        version0.0.1/version
        namespringboot-caffeine-cache-example-1/name
        descriptionDemo project for Spring Boot Cache/description
    
        properties
            java.version1.8/java.version
        /properties
    
        dependencies
            dependency
                groupIdorg.springframework.boot/groupId
                artifactIdspring-boot-starter-web/artifactId
            /dependency
            dependency
                groupIdcom.github.ben-manes.caffeine/groupId
                artifactIdcaffeine/artifactId
            /dependency
            dependency
                groupIdorg.projectlombok/groupId
                artifactIdlombok/artifactId
            /dependency
        /dependencies
    
        build
            plugins
                plugin
                    groupIdorg.springframework.boot/groupId
                    artifactIdspring-boot-maven-plugin/artifactId
                /plugin
            /plugins
        /build
    
    /project
    

    2、配置缓存配置类

    
    import com.github.benmanes.caffeine.cache.Cache;
    import com.github.benmanes.caffeine.cache.Caffeine;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import java.util.concurrent.TimeUnit;
    
    @Configuration
    public class CacheConfig {
    
        @Bean
        public CacheString, Object caffeineCache() {
            return Caffeine.newBuilder()
                    // 设置最后一次写入或访问后经过固定时间过期
                    .expireAfterWrite(60, TimeUnit.SECONDS)
                    // 初始的缓存空间大小
                    .initialCapacity(100)
                    // 缓存的最大条数
                    .maximumSize(1000)
                    .build();
        }
    
    }
    

    3、定义测试的实体对象

    
    import lombok.Data;
    import lombok.ToString;
    
    @Data
    @ToString
    public class UserInfo {
        private Integer id;
        private String name;
        private String sex;
        private Integer age;
    }
    

    4、定义服务接口类和实现类

    UserInfoService

    
    import mydlq.club.example.entity.UserInfo;
    
    public interface UserInfoService {
    
        /**
         * 增加用户信息
         *
         * @param userInfo 用户信息
         */
        void addUserInfo(UserInfo userInfo);
    
        /**
         * 获取用户信息
         *
         * @param id 用户ID
         * @return 用户信息
         */
        UserInfo getByName(Integer id);
    
        /**
         * 修改用户信息
         *
         * @param userInfo 用户信息
         * @return 用户信息
         */
        UserInfo updateUserInfo(UserInfo userInfo);
    
        /**
         * 删除用户信息
         *
         * @param id 用户ID
         */
        void deleteById(Integer id);
    
    }
    

    UserInfoServiceImpl

    
    import com.github.benmanes.caffeine.cache.Cache;
    import lombok.extern.slf4j.Slf4j;
    import mydlq.club.example.entity.UserInfo;
    import mydlq.club.example.service.UserInfoService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.util.StringUtils;
    import java.util.HashMap;
    
    @Slf4j
    @Service
    public class UserInfoServiceImpl implements UserInfoService {
    
        /**
         * 模拟数据库存储数据
         */
        private HashMapInteger, UserInfo userInfoMap = new HashMap();
    
        @Autowired
        CacheString, Object caffeineCache;
    
        @Override
        public void addUserInfo(UserInfo userInfo) {
            log.info("create");
            userInfoMap.put(userInfo.getId(), userInfo);
            // 加入缓存
            caffeineCache.put(String.valueOf(userInfo.getId()),userInfo);
        }
    
        @Override
        public UserInfo getByName(Integer id) {
            // 先从缓存读取
            caffeineCache.getIfPresent(id);
            UserInfo userInfo = (UserInfo) caffeineCache.asMap().get(String.valueOf(id));
            if (userInfo != null){
                return userInfo;
            }
            // 如果缓存中不存在,则从库中查找
            log.info("get");
            userInfo = userInfoMap.get(id);
            // 如果用户信息不为空,则加入缓存
            if (userInfo != null){
                caffeineCache.put(String.valueOf(userInfo.getId()),userInfo);
            }
            return userInfo;
        }
    
        @Override
        public UserInfo updateUserInfo(UserInfo userInfo) {
            log.info("update");
            if (!userInfoMap.containsKey(userInfo.getId())) {
                return null;
            }
            // 取旧的值
            UserInfo oldUserInfo = userInfoMap.get(userInfo.getId());
            // 替换内容
            if (!StringUtils.isEmpty(oldUserInfo.getAge())) {
                oldUserInfo.setAge(userInfo.getAge());
            }
            if (!StringUtils.isEmpty(oldUserInfo.getName())) {
                oldUserInfo.setName(userInfo.getName());
            }
            if (!StringUtils.isEmpty(oldUserInfo.getSex())) {
                oldUserInfo.setSex(userInfo.getSex());
            }
            // 将新的对象存储,更新旧对象信息
            userInfoMap.put(oldUserInfo.getId(), oldUserInfo);
            // 替换缓存中的值
            caffeineCache.put(String.valueOf(oldUserInfo.getId()),oldUserInfo);
            return oldUserInfo;
        }
    
        @Override
        public void deleteById(Integer id) {
            log.info("delete");
            userInfoMap.remove(id);
            // 从缓存中删除
            caffeineCache.asMap().remove(String.valueOf(id));
        }
    
    }
    

    5、测试的 Controller 类

    
    import mydlq.club.example.entity.UserInfo;
    import mydlq.club.example.service.UserInfoService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.*;
    
    @RestController
    @RequestMapping
    public class UserInfoController {
    
        @Autowired
        private UserInfoService userInfoService;
    
        @GetMapping("/userInfo/{id}")
        public Object getUserInfo(@PathVariable Integer id) {
            UserInfo userInfo = userInfoService.getByName(id);
            if (userInfo == null) {
                return "没有该用户";
            }
            return userInfo;
        }
    
        @PostMapping("/userInfo")
        public Object createUserInfo(@RequestBody UserInfo userInfo) {
            userInfoService.addUserInfo(userInfo);
            return "SUCCESS";
        }
    
        @PutMapping("/userInfo")
        public Object updateUserInfo(@RequestBody UserInfo userInfo) {
            UserInfo newUserInfo = userInfoService.updateUserInfo(userInfo);
            if (newUserInfo == null){
                return "不存在该用户";
            }
            return newUserInfo;
        }
    
        @DeleteMapping("/userInfo/{id}")
        public Object deleteUserInfo(@PathVariable Integer id) {
            userInfoService.deleteById(id);
            return "SUCCESS";
        }
    
    }
    

    五、SpringBoot 集成 Caffeine 方式二

    1、Maven 引入相关依赖

    
    ?xml version="1.0" encoding="UTF-8"?
    project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"
        modelVersion4.0.0/modelVersion
    
        parent
            groupIdorg.springframework.boot/groupId
            artifactIdspring-boot-starter-parent/artifactId
            version2.2.2.RELEASE/version
        /parent
    
        groupIdmydlq.club/groupId
        artifactIdspringboot-caffeine-cache-example-2/artifactId
        version0.0.1/version
        namespringboot-caffeine-cache-example-2/name
        descriptionDemo project for Spring Boot caffeine/description
    
        properties
            java.version1.8/java.version
        /properties
    
        dependencies
            dependency
                groupIdorg.springframework.boot/groupId
                artifactIdspring-boot-starter-web/artifactId
            /dependency
            dependency
                groupIdorg.springframework.boot/groupId
                artifactIdspring-boot-starter-cache/artifactId
            /dependency
            dependency
                groupIdcom.github.ben-manes.caffeine/groupId
                artifactIdcaffeine/artifactId
            /dependency
            dependency
                groupIdorg.projectlombok/groupId
                artifactIdlombok/artifactId
            /dependency
        /dependencies
    
        build
            plugins
                plugin
                    groupIdorg.springframework.boot/groupId
                    artifactIdspring-boot-maven-plugin/artifactId
                /plugin
            /plugins
        /build
    
    /project
    

    2、配置缓存配置类

    
    @Configuration
    public class CacheConfig {
    
        /**
         * 配置缓存管理器
         *
         * @return 缓存管理器
         */
        @Bean("caffeineCacheManager")
        public CacheManager cacheManager() {
            CaffeineCacheManager cacheManager = new CaffeineCacheManager();
            cacheManager.setCaffeine(Caffeine.newBuilder()
                    // 设置最后一次写入或访问后经过固定时间过期
                    .expireAfterAccess(60, TimeUnit.SECONDS)
                    // 初始的缓存空间大小
                    .initialCapacity(100)
                    // 缓存的最大条数
                    .maximumSize(1000));
            return cacheManager;
        }
    
    }
    

    3、定义测试的实体对象

    
    @Data
    @ToString
    public class UserInfo {
        private Integer id;
        private String name;
        private String sex;
        private Integer age;
    }
    

    4、定义服务接口类和实现类

    服务接口

    
    import mydlq.club.example.entity.UserInfo;
    
    public interface UserInfoService {
    
        /**
         * 增加用户信息
         *
         * @param userInfo 用户信息
         */
        void addUserInfo(UserInfo userInfo);
    
        /**
         * 获取用户信息
         *
         * @param id 用户ID
         * @return 用户信息
         */
        UserInfo getByName(Integer id);
    
        /**
         * 修改用户信息
         *
         * @param userInfo 用户信息
         * @return 用户信息
         */
        UserInfo updateUserInfo(UserInfo userInfo);
    
        /**
         * 删除用户信息
         *
         * @param id 用户ID
         */
        void deleteById(Integer id);
    
    }
    

    服务实现类

    
    import lombok.extern.slf4j.Slf4j;
    import mydlq.club.example.entity.UserInfo;
    import mydlq.club.example.service.UserInfoService;
    import org.springframework.cache.annotation.CacheConfig;
    import org.springframework.cache.annotation.CacheEvict;
    import org.springframework.cache.annotation.CachePut;
    import org.springframework.cache.annotation.Cacheable;
    import org.springframework.stereotype.Service;
    import org.springframework.util.StringUtils;
    import java.util.HashMap;
    
    @Slf4j
    @Service
    @CacheConfig(cacheNames = "caffeineCacheManager")
    public class UserInfoServiceImpl implements UserInfoService {
    
        /**
         * 模拟数据库存储数据
         */
        private HashMapInteger, UserInfo userInfoMap = new HashMap();
    
        @Override
        @CachePut(key = "#userInfo.id")
        public void addUserInfo(UserInfo userInfo) {
            log.info("create");
            userInfoMap.put(userInfo.getId(), userInfo);
        }
    
        @Override
        @Cacheable(key = "#id")
        public UserInfo getByName(Integer id) {
            log.info("get");
            return userInfoMap.get(id);
        }
    
        @Override
        @CachePut(key = "#userInfo.id")
        public UserInfo updateUserInfo(UserInfo userInfo) {
            log.info("update");
            if (!userInfoMap.containsKey(userInfo.getId())) {
                return null;
            }
            // 取旧的值
            UserInfo oldUserInfo = userInfoMap.get(userInfo.getId());
            // 替换内容
            if (!StringUtils.isEmpty(oldUserInfo.getAge())) {
                oldUserInfo.setAge(userInfo.getAge());
            }
            if (!StringUtils.isEmpty(oldUserInfo.getName())) {
                oldUserInfo.setName(userInfo.getName());
            }
            if (!StringUtils.isEmpty(oldUserInfo.getSex())) {
                oldUserInfo.setSex(userInfo.getSex());
            }
            // 将新的对象存储,更新旧对象信息
            userInfoMap.put(oldUserInfo.getId(), oldUserInfo);
            // 返回新对象信息
            return oldUserInfo;
        }
    
        @Override
        @CacheEvict(key = "#id")
        public void deleteById(Integer id) {
            log.info("delete");
            userInfoMap.remove(id);
        }
    
    }
    

    5、测试的 Controller 类

    
    import mydlq.club.example.entity.UserInfo;
    import mydlq.club.example.service.UserInfoService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.*;
    
    @RestController
    @RequestMapping
    public class UserInfoController {
    
        @Autowired
        private UserInfoService userInfoService;
    
        @GetMapping("/userInfo/{id}")
        public Object getUserInfo(@PathVariable Integer id) {
            UserInfo userInfo = userInfoService.getByName(id);
            if (userInfo == null) {
                return "没有该用户";
            }
            return userInfo;
        }
    
        @PostMapping("/userInfo")
        public Object createUserInfo(@RequestBody UserInfo userInfo) {
            userInfoService.addUserInfo(userInfo);
            return "SUCCESS";
        }
    
        @PutMapping("/userInfo")
        public Object updateUserInfo(@RequestBody UserInfo userInfo) {
            UserInfo newUserInfo = userInfoService.updateUserInfo(userInfo);
            if (newUserInfo == null){
                return "不存在该用户";
            }
            return newUserInfo;
        }
    
        @DeleteMapping("/userInfo/{id}")
        public Object deleteUserInfo(@PathVariable Integer id) {
            userInfoService.deleteById(id);
            return "SUCCESS";
        }
    
    }
    

    END

    Java面试题专栏

    (很全面)SpringBoot 使用 Caffeine 本地缓存

    我知道你 “在看(很全面)SpringBoot 使用 Caffeine 本地缓存

    原文始发于微信公众号(Java知音):

    本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

    本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

    原文链接:blog.ouyangsihai.cn >> (很全面)SpringBoot 使用 Caffeine 本地缓存


     上一篇
    Spring Boot + Vue 如此强大?竟然可以开发基于 C,S 架构的应用 Spring Boot + Vue 如此强大?竟然可以开发基于 C,S 架构的应用
    点击上方“后端技术精选”,选择“置顶公众号” 技术文章第一时间送达! 作者:xiangzhihong segmentfault.com/a/1190000021376934 前言虽然 B/S 是目前开发的主流,但是
    下一篇 
    Jdk14 都要出了,Jdk9 的新特性还不了解一下? Jdk14 都要出了,Jdk9 的新特性还不了解一下?
    来源:未读代码 Java 9 中最大的亮点是 Java 平台模块化的引入,以及模块化 JDK。但是 Java 9 还有很多其他新功能,这篇文字会将重点介绍开发人员特别感兴趣的几种功能。 1.  模块化 Java 9 中的模块化是对 J