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

Spring Boot多缓存键生成与@CacheEvict调用失效问题

问题1:生成与@Cacheable匹配的缓存键

当你在@Cacheable中指定key = "{#id, #name}"时,这个SpEL表达式会生成一个包含id和name的List<Object>对象作为缓存键(指定key属性后会跳过默认的SimpleKeyGenerator,因此直接用SimpleKey无法匹配)。以下是几种可靠的解决方案:

  • 手动构造匹配的List对象
    直接创建和SpEL表达式结果一致的List,作为驱逐的键:

    Long id = 123L;
    String name = "abc";
    List<Object> cacheKey = Arrays.asList(id, name);
    cacheManager.getCache("cache_name").evict(cacheKey);
    
  • 用SpEL解析器生成键
    通过Spring的SpEL解析器直接计算key表达式,确保和@Cacheable中的生成逻辑完全一致,适合复杂表达式场景:

    @Autowired
    private SpelExpressionParser spelParser;
    
    // 生成键
    Long id = 123L;
    String name = "abc";
    EvaluationContext context = new StandardEvaluationContext();
    context.setVariable("id", id);
    context.setVariable("name", name);
    Object cacheKey = spelParser.parseExpression("{#id, #name}").getValue(context);
    cacheManager.getCache("cache_name").evict(cacheKey);
    
  • 针对SimpleKey场景的适配
    如果你的缓存实现使用SimpleKey作为键(比如未指定key属性时的默认逻辑),需要构造参数类型完全匹配的SimpleKey:

    SimpleKey cacheKey = new SimpleKey(id, name);
    cacheManager.getCache("cache_name").evict(cacheKey);
    
问题2:同一服务内调用@CacheEvict方法无效

这是因为Spring缓存注解基于AOP代理实现,同一类内的方法调用不会经过代理对象,导致注解逻辑无法触发。以下是几种解决思路:

  • 通过代理对象调用方法
    在类中注入自身的代理对象,通过代理调用带@CacheEvict的方法:

    @Service(proxyMode = ScopedProxyMode.TARGET_CLASS)
    public class MyService {
        @Autowired
        private MyService selfProxy;
    
        public void businessMethod(Long id, String name) {
            // 通过代理对象调用缓存驱逐方法
            selfProxy.evictCache(id, name);
        }
    
        @CacheEvict(value = "cache_name", key = "{#id, #name}")
        public void evictCache(Long id, String name) {
            // 无需业务逻辑,注解会处理缓存驱逐
        }
    }
    
  • 从ApplicationContext获取代理对象
    如果不想注入自身,可以通过上下文获取代理实例:

    @Service
    public class MyService {
        @Autowired
        private ApplicationContext context;
    
        public void businessMethod(Long id, String name) {
            MyService proxy = context.getBean(MyService.class);
            proxy.evictCache(id, name);
        }
    
        @CacheEvict(value = "cache_name", key = "{#id, #name}")
        public void evictCache(Long id, String name) {
        }
    }
    
  • 直接使用CacheManager驱逐
    采用问题1中的方案,手动生成缓存键后通过CacheManager直接驱逐,无需依赖注解:

    public void businessMethod(Long id, String name) {
        List<Object> cacheKey = Arrays.asList(id, name);
        cacheManager.getCache("cache_name").evict(cacheKey);
    }
    

内容的提问来源于stack exchange,提问作者SLyHuy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 19:36:17