问题说明
在开发过程中,某个方法上进行了以下注解,将返回数据缓存起来。
@Cacheable(
value = CacheKey.DEPT_ALL,
unless="#result==null")
由于业务需要,在另一个Class中,调用某个方法时需要清除这个缓存,于是注解上:
@CacheEvict(
value = CacheKey.DEPT_ALL
)
然而在调用缓存清除的方法时,发现缓存并没有清除,注解没生效。
问题排查
看网上有说法说对于缓存操作要放到同一个Java文件中,不能分开到两个类。但是这个说法一看感觉就不正确,于是进行源码分析排查。
定位到spring-boot-starter-data-redis的核心类 org.springframework.cache.interceptor.CacheInterceptor
可以排查到针对注解方法实现的操作位于org.springframework.cache.interceptor.CacheAspectSupport#execute()
方法中
代码片段:
// Collect any explicit @CachePuts
collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests);
// Process any collected put requests, either from @CachePut or a @Cacheable miss
for (CachePutRequest cachePutRequest : cachePutRequests) {
cachePutRequest.apply(cacheValue);
}
// Process any late evictions
processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);
这里分别处理了缓存写入和缓存失效的相关操作。
分别断点查看调用缓存写入和缓存失效时操作的Redis Key值,发现缓存写入时,Key是"SimpleKey";而缓存失效时,Key是我方法传入的一堆参数。问题产生的原因就已经明确了,是因为写入和销毁的key值不一致,导致缓存没有销毁成功。
问题修复
查看@CacheEvict
的文档里面对Key值说明:
Default is "", meaning all method parameters are considered as a key, unless a custom keyGenerator has been set.
不进行配置时,默认是将方法的所有参数作为key。
修复方案也比较简单了,由于这里对key值没有要求,是固定的缓存key。将注解的key加一个固定字符串即可。
@Cacheable(
value = CacheKey.DEPT_USER_COUNT,
unless="#result==null",
key="'ALL'")
@CacheEvict(value = CacheKey.DEPT_USER_COUNT, key = "'ALL'")
总结:注解@CacheEvict失效,首先排查Key值与写入的Key值是否一致。