You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 14:22:40