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

环境配置:

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

一、本地缓存介绍

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

之前介绍过 Redis 这种 NoSql 作为缓存组件,它能够很好地作为分布式缓存组件提供多个服务间的缓存,但是 Redis 这种还是需要网络开销,增加时耗。

本地缓存是直接从本地内存中读取,没有网络开销,例如秒杀系统或者数据量小的缓存等,比远程缓存更合适。

二、缓存组件 Caffeine 介绍

按 Caffeine Github 文档描述,Caffeine 是基于 JAVA 8 的高性能缓存库。

并且在 Spring5 (Spring Boot 2.x) 后,Spring 官方放弃了 Guava,而使用了性能更优秀的 Caffeine 作为默认缓存组件。

1、Caffeine 性能

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

2、Caffeine 配置说明

注意:

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

3、软引用与弱引用

软引用: 如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。弱引用: 弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存//软引用Caffeine.newBuilder().softValues().build();//弱引用Caffeine.newBuilder().weakKeys().weakValues().build();

三、SpringBoot 集成 Caffeine 两种方式

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

方式一: 直接引入 Caffeine 依赖,然后使用 Caffeine 方法实现缓存。

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

关注公众号Java技术栈,回复:面试,可以获取我整理的 Spring Boot 系列面试题和答案。下面将介绍下,这两种集成方式都是如何实现的。

Spring Boot 基础就不介绍了,推荐看下这个教程:

https://github.com/javastacks/spring-boot-best-practice

四、SpringBoot 集成 Caffeine 方式一

1、Maven 引入相关依赖

4.0.0org.springframework.bootspring-boot-starter-parent2.2.2.RELEASEmydlq.clubspringboot-caffeine-cache-example-10.0.1springboot-caffeine-cache-example-1DemoprojectforSpringBootCache1.8org.springframework.bootspring-boot-starter-webcom.github.ben-manes.caffeinecaffeineorg.projectlomboklombokorg.springframework.bootspring-boot-maven-plugin

2、配置缓存配置类

importcom.github.benmanes.caffeine.cache.Cache;importcom.github.benmanes.caffeine.cache.Caffeine;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importjava.util.concurrent.TimeUnit;@ConfigurationpublicclassCacheConfig{@BeanpublicCachecaffeineCache(){returnCaffeine.newBuilder()//设置最后一次写入或访问后经过固定时间过期.expireAfterWrite(60,TimeUnit.SECONDS)//初始的缓存空间大小.initialCapacity(100)//缓存的最大条数.maximumSize(1000).build();}}

3、定义测试的实体对象

importlombok.Data;importlombok.ToString;@Data@ToStringpublicclassUserInfo{privateIntegerid;privateStringname;privateStringsex;privateIntegerage;}

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

UserInfoService

importmydlq.club.example.entity.UserInfo;publicinterfaceUserInfoService{/***增加用户信息**@paramuserInfo用户信息*/voidaddUserInfo(UserInfouserInfo);/***获取用户信息**@paramid用户ID*@return用户信息*/UserInfogetByName(Integerid);/***修改用户信息**@paramuserInfo用户信息*@return用户信息*/UserInfoupdateUserInfo(UserInfouserInfo);/***删除用户信息**@paramid用户ID*/voiddeleteById(Integerid);}

UserInfoServiceImpl

importcom.github.benmanes.caffeine.cache.Cache;importlombok.extern.slf4j.Slf4j;importmydlq.club.example.entity.UserInfo;importmydlq.club.example.service.UserInfoService;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Service;importorg.springframework.util.StringUtils;importjava.util.HashMap;@Slf4j@ServicepublicclassUserInfoServiceImplimplementsUserInfoService{/***模拟数据库存储数据*/privateHashMapuserInfoMap=newHashMap();@AutowiredCachecaffeineCache;@OverridepublicvoidaddUserInfo(UserInfouserInfo){log.info(“create”);userInfoMap.put(userInfo.getId(),userInfo);//加入缓存caffeineCache.put(String.valueOf(userInfo.getId()),userInfo);}@OverridepublicUserInfogetByName(Integerid){//先从缓存读取caffeineCache.getIfPresent(id);UserInfouserInfo=(UserInfo)caffeineCache.asMap().get(String.valueOf(id));if(userInfo!=null){returnuserInfo;}//如果缓存中不存在,则从库中查找log.info(“get”);userInfo=userInfoMap.get(id);//如果用户信息不为空,则加入缓存if(userInfo!=null){caffeineCache.put(String.valueOf(userInfo.getId()),userInfo);}returnuserInfo;}@OverridepublicUserInfoupdateUserInfo(UserInfouserInfo){log.info(“update”);if(!userInfoMap.containsKey(userInfo.getId())){returnnull;}//取旧的值UserInfooldUserInfo=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);returnoldUserInfo;}@OverridepublicvoiddeleteById(Integerid){log.info(“delete”);userInfoMap.remove(id);//从缓存中删除caffeineCache.asMap().remove(String.valueOf(id));}}

5、测试的 Controller 类

importmydlq.club.example.entity.UserInfo;importmydlq.club.example.service.UserInfoService;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.web.bind.annotation.*;@RestController@RequestMappingpublicclassUserInfoController{@AutowiredprivateUserInfoServiceuserInfoService;@GetMapping(“/userInfo/{id}”)publicObjectgetUserInfo(@PathVariableIntegerid){UserInfouserInfo=userInfoService.getByName(id);if(userInfo==null){return”没有该用户”;}returnuserInfo;}@PostMapping(“/userInfo”)publicObjectcreateUserInfo(@RequestBodyUserInfouserInfo){userInfoService.addUserInfo(userInfo);return”SUCCESS”;}@PutMapping(“/userInfo”)publicObjectupdateUserInfo(@RequestBodyUserInfouserInfo){UserInfonewUserInfo=userInfoService.updateUserInfo(userInfo);if(newUserInfo==null){return”不存在该用户”;}returnnewUserInfo;}@DeleteMapping(“/userInfo/{id}”)publicObjectdeleteUserInfo(@PathVariableIntegerid){userInfoService.deleteById(id);return”SUCCESS”;}}

五、SpringBoot 集成 Caffeine 方式二

1、Maven 引入相关依赖

4.0.0org.springframework.bootspring-boot-starter-parent2.2.2.RELEASEmydlq.clubspringboot-caffeine-cache-example-20.0.1springboot-caffeine-cache-example-2DemoprojectforSpringBootcaffeine1.8org.springframework.bootspring-boot-starter-weborg.springframework.bootspring-boot-starter-cachecom.github.ben-manes.caffeinecaffeineorg.projectlomboklombokorg.springframework.bootspring-boot-maven-plugin

2、配置缓存配置类

@ConfigurationpublicclassCacheConfig{/***配置缓存管理器**@return缓存管理器*/@Bean(“caffeineCacheManager”)publicCacheManagercacheManager(){CaffeineCacheManagercacheManager=newCaffeineCacheManager();cacheManager.setCaffeine(Caffeine.newBuilder()//设置最后一次写入或访问后经过固定时间过期.expireAfterAccess(60,TimeUnit.SECONDS)//初始的缓存空间大小.initialCapacity(100)//缓存的最大条数.maximumSize(1000));returncacheManager;}}

3、定义测试的实体对象

@Data@ToStringpublicclassUserInfo{privateIntegerid;privateStringname;privateStringsex;privateIntegerage;}

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

服务接口:

importmydlq.club.example.entity.UserInfo;publicinterfaceUserInfoService{/***增加用户信息**@paramuserInfo用户信息*/voidaddUserInfo(UserInfouserInfo);/***获取用户信息**@paramid用户ID*@return用户信息*/UserInfogetByName(Integerid);/***修改用户信息**@paramuserInfo用户信息*@return用户信息*/UserInfoupdateUserInfo(UserInfouserInfo);/***删除用户信息**@paramid用户ID*/voiddeleteById(Integerid);}

服务实现类

importlombok.extern.slf4j.Slf4j;importmydlq.club.example.entity.UserInfo;importmydlq.club.example.service.UserInfoService;importorg.springframework.cache.annotation.CacheConfig;importorg.springframework.cache.annotation.CacheEvict;importorg.springframework.cache.annotation.CachePut;importorg.springframework.cache.annotation.Cacheable;importorg.springframework.stereotype.Service;importorg.springframework.util.StringUtils;importjava.util.HashMap;@Slf4j@Service@CacheConfig(cacheNames=”caffeineCacheManager”)publicclassUserInfoServiceImplimplementsUserInfoService{/***模拟数据库存储数据*/privateHashMapuserInfoMap=newHashMap();@Override@CachePut(key=”#userInfo.id”)publicvoidaddUserInfo(UserInfouserInfo){log.info(“create”);userInfoMap.put(userInfo.getId(),userInfo);}@Override@Cacheable(key=”#id”)publicUserInfogetByName(Integerid){log.info(“get”);returnuserInfoMap.get(id);}@Override@CachePut(key=”#userInfo.id”)publicUserInfoupdateUserInfo(UserInfouserInfo){log.info(“update”);if(!userInfoMap.containsKey(userInfo.getId())){returnnull;}//取旧的值UserInfooldUserInfo=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);//返回新对象信息returnoldUserInfo;}@Override@CacheEvict(key=”#id”)publicvoiddeleteById(Integerid){log.info(“delete”);userInfoMap.remove(id);}}

5、测试的 Controller 类

importmydlq.club.example.entity.UserInfo;importmydlq.club.example.service.UserInfoService;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.web.bind.annotation.*;@RestController@RequestMappingpublicclassUserInfoController{@AutowiredprivateUserInfoServiceuserInfoService;@GetMapping(“/userInfo/{id}”)publicObjectgetUserInfo(@PathVariableIntegerid){UserInfouserInfo=userInfoService.getByName(id);if(userInfo==null){return”没有该用户”;}returnuserInfo;}@PostMapping(“/userInfo”)publicObjectcreateUserInfo(@RequestBodyUserInfouserInfo){userInfoService.addUserInfo(userInfo);return”SUCCESS”;}@PutMapping(“/userInfo”)publicObjectupdateUserInfo(@RequestBodyUserInfouserInfo){UserInfonewUserInfo=userInfoService.updateUserInfo(userInfo);if(newUserInfo==null){return”不存在该用户”;}returnnewUserInfo;}@DeleteMapping(“/userInfo/{id}”)publicObjectdeleteUserInfo(@PathVariableIntegerid){userInfoService.deleteById(id);return”SUCCESS”;}}

参考地址:

https://www.jianshu.com/p/c72fb0c787fc
https://www.cnblogs.com/rickiyang/p/11074158.html
https://github.com/my-dlq/blog-example/tree/master/springboot/springboot-caffeine-cache-example