文章目录
1. 简单示例
配置类,这里的@EnableCaching注解可以写在配置类,也可以写在启动类;
@EnableCaching
@Configuration
public class CacheConfig {
@Primary
@Bean
@Qualifier("calculateParamCache")
public CaffeineCacheManager calculateParamCache() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(
Caffeine.newBuilder().recordStats().expireAfterWrite(300, TimeUnit.SECONDS).maximumSize(500));
cacheManager.setCacheNames(Lists.newArrayList("calculateParamCache"));
return cacheManager;
}
}
直接上重点吧,啰嗦代码就不写了:
@Service
@Slf4j
public class GirlServiceImpl extends ServiceImpl<GirlMapper, Girl> implements GrilService {
@Override
@Cacheable(cacheNames = "calculateParamCache", cacheManager = "calculateParamCache", unless = "#result == null ", key = "#id")
public Girl queryAll(Integer id) {
log.info("查询id");
return baseMapper.selectById(id);
}
}
2.参数配置介绍
2.1 expireAfterWrite
expireAfterWrite(5, TimeUnit.SECONDS)
表示第一次查询时查询数据库,然后建立缓存,此后的5秒内,数据从缓存中获取,5秒后删除该缓存,下一次调用接口查询重复之前操作;不停进行接口请求,示例如下:
需要注意的时是,缓存在这5秒内是感知不到数据库的变化的,人为修改数据库数据,查询到的还是原数据;
2.2 expireAfterAccess
expireAfterAccess(5, TimeUnit.SECONDS)
和expireAfterWrite的区别就是,expireAfterWrite以第一次请求的时间为起始时间,而expireAfterAccess以最后一次的请求时间为起始时间,最后一次请求5秒后删除该缓存;示例如下:
2.3 refreshAfterWrite
代码示例:
核心代码
@Slf4j
@Component
public class CacheDemo {
private final LoadingCache<Integer, Girl> tslInstanceCache;
private final GirlServiceImpl girlService;
public CacheDemo(GirlServiceImpl girlService) {
this.girlService = girlService;
this.tslInstanceCache = Caffeine.newBuilder()
.refreshAfterWrite(5, TimeUnit.SECONDS)
.recordStats()
.removalListener(
(RemovalListener<? super Object, ? super Object>)
(key, value, cause) -> log.info(
"cache remove, key:{}, value:{}, cause:{}",
key,
value,
cause))
.build(new tslCacheLoader());
}
//这里去查询数据库
private class tslCacheLoader implements CacheLoader<Integer, Girl> {
@Override
public @Nullable Girl load(@NonNull Integer integer) throws Exception {
log.info("start load: {}", integer);
return girlService.query(integer);
}
@Override
public @Nullable Girl reload(@NonNull Integer key, @NonNull Girl oldValue) throws Exception {
log.info("start reload: {}", key);
return girlService.query(key);
}
}
//controller层调用此方法
public Girl getInfo(Integer key) {
Girl girl = tslInstanceCache.get(key);
log.info("return:{}", JSON.toJSON(girl));
return girl;
}
}
运行结果介绍:
第一次调接口查询,调用load方法,这里是在主线程中进行;
再点一次,直接查缓存数据;
等一段时间控制台没有日志信息发现其实到了时间不会自动去刷新;
调用一次接口,发现调用reload接口,这个时间机制和expireAfterWrite一样,这里是用的ForkJoinPool线程池中线程;
补一个线程监控
手动修改一下数据库内容:
然后再去调用,发现返回的还是原来的,同时会删除原来的;
再请求一次,返回的才是正常数据;
所以使用refreshAfterWrite会有一次获取到旧值的过程!
2.4 removalListener
监听过期缓存的信息,这里使用的是com.github.benmanes.caffeine.cache的包:
public CacheDemo(GirlServiceImpl girlService) {
this.girlService = girlService;
this.tslInstanceCache = Caffeine.newBuilder()
.expireAfterWrite(5, TimeUnit.SECONDS)
.recordStats()
.removalListener((RemovalListener<? super Object, ? super Object>) (key, value, cause) -> {
log.info("cache remove, key:{}, value:{}, cause:{}", key, value, cause);
log.info("current remove thread: " + Thread.currentThread().getName());
})
.build(new tslCacheLoader());
}
发现removalListener部分其实不是在主线程中进行,额外使用了线程;
下面使用RemovalListeners.asynchronous异步处理过期缓存消息,使用的是com.google.common.cache的包,可以做一些处理,其实效果和上面差不多,可能是包的原因:
public CacheDemo(ExecutorService executorService, GirlServiceImpl girlService) {
this.girlService = girlService;
this.tslInstanceCache = CacheBuilder.newBuilder()
.expireAfterWrite(5, TimeUnit.SECONDS)
.removalListener(RemovalListeners.asynchronous((RemovalListener<Integer, Girl>) removalNotification -> {
log.info("key={},value={},reason={}", removalNotification.getKey(), removalNotification.getValue(), removalNotification.getCause());
log.info("current remove thread: " + Thread.currentThread().getName());
}, executorService))
.build(new tslCacheLoader());
}
但是上述两种的load和reload都是在主线程中进行;
需要注意的是,此处用的是expireAfterWrite,如果用refreshAfterWrite的话RemovalListeners.asynchronous好像不work,可能是都是异步调用;直接用下面的方式就可以了;
public CacheDemo(GirlServiceImpl girlService) {
this.girlService = girlService;
this.tslInstanceCache = Caffeine.newBuilder()
.refreshAfterWrite(5, TimeUnit.SECONDS)
.recordStats()
.removalListener((RemovalListener<? super Object, ? super Object>) (key, value, cause) -> {
log.info("cache remove, key:{}, value:{}, cause:{}", key, value, cause);
log.info("current remove thread: " + Thread.currentThread().getName());
})
.build(new tslCacheLoader());
}