GuavaCache中LoadingCache的使用
背景
LoadingCache是GuavaCache构建缓存实体的方法,是一个支持多线程并发读写、高性能、通用的in-heap(堆)本地缓存。
支持key不存在时按照给定的CacheLoader 的loader方法进行loading。如果有多个线程同时get一个不存在的key,那么会有一个线程负责load,其他线程阻塞wait等待。
CacheBuilder方法参数
- maximumSize(): 最大缓存上限,快达到上限或达到上限,处理了时间最长没被访问过的对象或者根据配置的被释放的对象
- expireAfterAccess():设置时间对象没有被读/写访问则对象从内存中删除,回收顺序和基于大小回收一样
- expireAfterWrite(): 设置时间对象没有被写访问则对象从内存中删除
- refreshAfterWrite():为缓存增加自动定时刷新功能。缓存项只有在被检索时才会真正刷新,即只有刷新间隔时间到了再去get(key)才会重新去执行Loading,否则就算刷新间隔时间到了也不会执行loading操作。
CacheLoader
实现自动加载缓存。可以在其中自定义load方法和reload方法,根据需求加载缓存和刷新缓存。
Cache常用方法
- get(key): 有值则返回缓存值,没有则执行load方法加载缓存。
- put(key, value): 显式地向缓存中插入值,会直接覆盖掉已有键之前映射的值。
- invalidate(key): 显式地清除个别缓存项。
- invalidateAll(keys): 批量清除缓存项。
- invalidateAll(): 清除所有缓存项。
- refresh(key): (异步)主动刷新对应缓存值 。在刷新操作进行时,缓存仍然可以向其他线程返回旧值。
示例代码
基础代码
// 实体类 @TableName("mid_user_info") @Data @Accessors(chain = true) public class UserInfoPO { /** * 主键ID */ @TableId private Long id; /** * 用户名 */ private String username; /** * 邮箱 */ private String email; /** * 电话 */ private String phone; /** * 创建时间 */ private Date createTime; /** * 更新时间 */ private Date updateTime; public UserInfoPO() { } public UserInfoPO(String username, String email, String phone) { this.username = username; this.email = email; this.phone = phone; } } // Service 实体类 @Service @Slf4j public class UserInfoService extends BaseService<UserInfoMapper, UserInfoPO> { /** * 根据参数获取实体 */ public UserInfoPO getEntityByParams(String username) { Wrapper<UserInfoPO> wrapper = new EntityWrapper<>(); wrapper.eq("username", username); return this.selectOne(wrapper); } public List<UserInfoPO> selectAll() { Wrapper<UserInfoPO> wrapper = new EntityWrapper<>(); return this.selectList(wrapper); } } // Mapper实体类 @Mapper @Component public interface UserInfoMapper extends BaseMapper<UserInfoPO> { }
核心代码
@Slf4j @Component public class UserCacheService { @Autowired UserInfoService userInfoService; /** * guava cache 缓存实体 */ LoadingCache<String, UserInfoPO> cache = CacheBuilder.newBuilder() // 缓存刷新时间【30分钟过期】 .expireAfterAccess(30, TimeUnit.MINUTES) // 设置缓存最大个数 .maximumSize(500) .build(new CacheLoader<String, UserInfoPO>() { @Override // 当本地缓存命没有中时,调用load方法获取结果并将结果缓存 public UserInfoPO load(String appKey) { log.info("获取用户详情,用户名:{}", appKey); return userInfoService.getEntityByParams(appKey); } }); /** * 对外暴露的方法 * 从缓存中取entry,没取到就走数据库 */ public UserInfoPO getUserInfo(String username) throws ExecutionException { log.info("进入 cache 获取用户信息,用户名:{}", username); return cache.get(username); } @PostConstruct public void initCache() { log.info("初始化员工缓存数据开始!"); //读取所有记录 List<UserInfoPO> userInfoList = userInfoService.selectAll(); if (CollectionUtils.isEmpty(userInfoList)) { return; } for (UserInfoPO userInfo : userInfoList) { try { this.getUserInfo(userInfo.getUsername()); } catch (Exception e) { log.error("初始化员工缓存数据异常:", e); } } log.info("初始化员工缓存数据结束!"); } }