Spring Boot中@Cacheable执行@CacheEvict后失效问题求助
问题分析
你遇到的核心问题是@Cacheable注解在处理无返回值(void)且可能抛出异常的方法时,缓存行为不符合预期:
- 首次调用有效token时,方法正常执行(无异常),Spring Cache会缓存一个
null值(因为void方法返回null),后续调用直接命中缓存,不会进入方法内部。 - 调用
@CacheEvict清除缓存后,再次调用该方法时,此时token已变为invalid,方法会抛出异常。而Spring Cache默认不会缓存抛出异常的方法结果,因此每次调用都会执行DB查询和异常抛出逻辑,无法重新生成缓存条目。
另外,void类型的方法本身就不适合用@Cacheable——缓存的核心是存储方法的计算结果,而void方法没有可复用的返回值,缓存的null本身没有业务意义,还会引发异常场景下的缓存失效问题。
解决方案
方案1:修改方法返回值,缓存业务结果(推荐)
把checkTokenExistsThrowError改成返回boolean(表示token是否有效),上层根据返回值处理异常,这样无论token有效与否,都能缓存对应的结果:
@Cacheable(key = "#token", value = "tokens") public boolean isTokenValid(String token) { return !tokenRepository.findTokenByTokenAndValid(token, false).isPresent(); } // 上层调用处处理异常 public void checkToken(String token) { if (!isTokenValid(token)) { throw buildException(TOKEN_IS_NOT_VALID_EXCEPTION); } } @CacheEvict(key = "#token", value = "tokens") public void addTokenToBlacklist(String token) { Token tokenObj = new Token(); tokenObj.setToken(token); tokenObj.setValid(false); tokenRepository.save(tokenObj); }
方案2:调整缓存逻辑,直接缓存黑名单状态
换一种思路,缓存token是否在黑名单中的状态,逻辑更清晰:
@Cacheable(key = "#token", value = "blacklistTokens") public boolean isTokenInBlacklist(String token) { return tokenRepository.findTokenByTokenAndValid(token, false).isPresent(); } public void checkTokenExistsThrowError(String token) { if (isTokenInBlacklist(token)) { throw buildException(TOKEN_IS_NOT_VALID_EXCEPTION); } } @CacheEvict(key = "#token", value = "blacklistTokens") public void addTokenToBlacklist(String token) { Token tokenObj = new Token(); tokenObj.setToken(token); tokenObj.setValid(false); tokenRepository.save(tokenObj); }
方案3:适配原有方法结构(不推荐,复杂度高)
如果必须保留原方法的void和抛出异常结构,可以自定义缓存错误处理器,让方法抛出异常时也缓存一个标记值:
// 自定义缓存错误处理器 @Component public class CacheErrorHandlerImpl implements CacheErrorHandler { @Override public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) { // 捕获方法抛出的异常,缓存标记值表示token无效 cache.put(key, Boolean.FALSE); } // 实现其他三个默认方法 @Override public void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) {} @Override public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) {} @Override public void handleCacheClearError(RuntimeException exception, Cache cache) {} } // 修改@Cacheable注解指定错误处理器 @Cacheable(key = "#token", value = "tokens", errorHandler = "cacheErrorHandlerImpl") public void checkTokenExistsThrowError(String token) { var result = tokenRepository.findTokenByTokenAndValid(token, false).isPresent(); if (result) { throw buildException(TOKEN_IS_NOT_VALID_EXCEPTION); } }
关键注意点
- Spring Cache默认不会缓存抛出异常的方法调用结果,只有方法正常返回时才会写入缓存。
- 避免为
void方法添加@Cacheable,缓存null无法区分业务状态,容易引发逻辑歧义。
内容的提问来源于stack exchange,提问作者gurkan
相关产品推荐
相关产品推荐

