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

Spring缓存注解自调用失效求助:@CachePut不生效问题排查

问题根源与解决方案

这问题我之前踩过坑!核心原因是Spring缓存注解依赖AOP代理实现,而同一个类内部的方法调用并不会触发AOP代理逻辑——说白了,你直接调用类里的方法时,是在操作原始对象,代理类根本没机会介入,自然@CachePut的缓存更新逻辑就没执行。

为什么跨类调用就正常?

当你把方法移到其他服务类,调用时是通过Spring容器注入的Bean(也就是代理对象)来调用方法,这时候AOP代理会拦截方法调用,执行@CachePut对应的缓存更新操作,所以缓存就生效了。

解决办法

这里给你几个常用的方案,按需选择:

1. 拆分方法到独立Bean(最推荐)

就是你已经试过的方式,把带@CachePut的方法抽成单独的Service类,符合单一职责原则,代码也更清晰。比如:

// 新的缓存操作服务类
@Service
public class CacheOperationService {
    @CachePut(value = "userCache", key = "#user.id")
    public User updateUserCache(User user) {
        // 业务逻辑:更新数据库等
        return user;
    }
}

// 原服务类
@Service
public class UserService {
    @Autowired
    private CacheOperationService cacheOpService;

    public void updateUser(User user) {
        // 通过注入的Bean调用,触发代理
        cacheOpService.updateUserCache(user);
    }
}

2. 自注入当前Bean(适合不想拆分的场景)

在当前类中注入自己的代理对象,然后通过这个代理对象调用带注解的方法,这样就能触发AOP逻辑。注意要加@Lazy避免循环依赖:

@Service
public class UserService {
    // 自注入代理对象,@Lazy解决循环依赖
    @Autowired
    @Lazy
    private UserService selfProxy;

    public void updateUser(User user) {
        // 调用代理对象的方法,触发@CachePut
        selfProxy.updateUserCache(user);
    }

    @CachePut(value = "userCache", key = "#user.id")
    public User updateUserCache(User user) {
        // 业务逻辑
        return user;
    }
}

3. 通过ApplicationContext获取代理对象

如果不想自注入,也可以从Spring容器中直接获取当前Bean的代理实例:

@Service
public class UserService implements ApplicationContextAware {
    private ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        this.context = context;
    }

    public void updateUser(User user) {
        // 获取代理对象并调用方法
        UserService proxy = context.getBean(UserService.class);
        proxy.updateUserCache(user);
    }

    @CachePut(value = "userCache", key = "#user.id")
    public User updateUserCache(User user) {
        // 业务逻辑
        return user;
    }
}

4. 改用AspectJ静态代理

如果上述动态代理的方式都不满足,你可以开启AspectJ的编译时或加载时织入,这样不需要依赖Spring的动态代理,直接在字节码层面织入缓存逻辑。需要做两步:

  • 开启AspectJ支持:在@EnableCaching注解中设置mode = AdviceMode.ASPECTJ
  • 引入AspectJ相关依赖,并配置编译时织入(比如Maven插件)或者加载时织入

不过这个方案配置稍复杂,一般推荐前两种更简单的方式。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 09:00:46