最近写一个小项目,遇到一个高频查询功能,使用redis又有点浪费,就决定采用本地缓存来实现,于是就想到了一直很火的GuavaCache,这里对使用GuavaCache做一些小结。
(在查资料的过程中,发现springBoot 2.x版本中已经移除了GuavaCache的支持,改用了Caffeine,这个需要抽空看看了)
整合起来很简单,具体如下:
LoadingCache<String, Integer> LOGIN_CACHE = CacheBuilder.newBuilder()
.maximumSize(100) // 设置缓存的最大容量
.expireAfterWrite(5, TimeUnit.SECONDS) // 测试用,5秒后失效
.concurrencyLevel(10) // 设置并发级别为10
.recordStats() // 开启缓存统计,生产不建议使用
.removalListener(notification -> log.info(notification.getKey() + " " + notification.getValue() + " 被移除,原因:" + notification.getCause()))
.build(new UserInfoLoad());
public class UserInfoLoad extends CacheLoader<String, Integer> {
@Override
public Integer load(String key) {
UserInfoExample userInfoExample = new UserInfoExample();
userInfoExample.createCriteria().andUuidEqualTo(key);
List<UserInfo> userInfos = userInfoMapper.selectByExample(userInfoExample);
if (CollectionUtils.isEmpty(userInfos)) {
return 0;
}
return userInfos.get(0).getId();
}
}
可以看到代码中我增加了removalListener,是为了验证一个问题:guavaCache只有在被触发时才会进行过期缓存清理,极端情况下,如果缓存很多以后,不再有方法触发它,那就会造成内存泄露了。不过这个极端情况几乎不会发生,设计的时候注意一下即可。
这里还遇到了第一个比较蠢得问题,我一开始将这个方法写在了service的方法内,结果每次调用都会查库,没有实现缓存的效果,反应了好一阵才想起来应该把这段代码移出方法体,哎。
然后我又想把这段逻辑做成一个静态方法类,这样所有需要获取用户信息的方法都可以调用。这样做本身是没任何问题的,但是执行的时候,会报userInfoMapper空指针异常,查询相关资料才发现原来spring不会注入静态属性。静态属性需要手动注入set方法。
private static UserInfoMapper userInfoMapper;
@Autowired
public void setUserInfoMapper(UserInfoMapper userInfoMapper) {
UserCache.userInfoMapper= userInfoMapper;
}
这样一个简单的GuavaCache实现就做好了。小型项目,或者配置不高的情况下,确实没必要安装那么多中间件,可以本地实现的话最好不过了。