問題說明
在開發過程中,某個方法上進行了以下注解,將返回數據緩存起來。
@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值是否一致。