Spring Boot中Caffeine缓存仅new初始化Readers时生效的原因及解决方法
问题原因分析
1. Spring缓存的核心机制限制
Spring的@Cacheable注解依赖动态代理实现——只有通过Spring容器管理的代理对象调用方法时,缓存切面才会拦截请求、处理缓存逻辑。直接调用原始对象的方法,缓存注解完全不生效。
2. Bean初始化阶段的调用时机问题
当你给Readers加上@Service和@DependsOn后,它变成了Spring容器管理的Bean。如果你的Readers在构造函数、@PostConstruct这类初始化方法里调用了BookProcessor的缓存方法,就会踩坑:
- Spring的Bean初始化流程是:实例化→属性注入→执行初始化方法→生成代理对象(如果有AOP需求,比如缓存)。也就是说,初始化阶段调用
BookProcessor时,它的缓存代理还没生成,你调用的是原始对象,自然不会触发缓存。 - 而你用
new Readers()手动创建实例时,一般是在Spring容器完全启动之后操作的,此时BookProcessor的代理对象已经就绪,调用方法时缓存切面正常工作,缓存就生效了。
3. 依赖注入的错误姿势(需排查)
如果Readers作为Spring Bean时,没有通过Spring的依赖注入(比如构造函数、Setter)获取BookProcessor,而是自己手动new BookProcessor(),那这个实例不是Spring代理的对象,@Cacheable肯定不会生效。不过根据你说的new Readers()时缓存生效,这种情况概率不高,但还是要检查一下。
无需手动new的解决方案
方案1:延迟调用到容器启动完成后
把Readers中调用缓存方法的逻辑,从初始化阶段挪到Spring容器完全启动之后。可以通过监听容器刷新事件实现:
@Service @DependsOn("bookProcessor") public class Readers { private final BookProcessor bookProcessor; // 构造函数注入(推荐的依赖注入方式) public Readers(BookProcessor bookProcessor) { this.bookProcessor = bookProcessor; } // 监听容器刷新完成事件 @EventListener(ContextRefreshedEvent.class) public void startReading() { // 此时容器已完全初始化,BookProcessor的代理对象就绪 Book book = bookProcessor.getBookById(1L); // 你的业务逻辑 } }
或者实现ApplicationListener接口:
@Service @DependsOn("bookProcessor") public class Readers implements ApplicationListener<ContextRefreshedEvent> { private final BookProcessor bookProcessor; public Readers(BookProcessor bookProcessor) { this.bookProcessor = bookProcessor; } @Override public void onApplicationEvent(ContextRefreshedEvent event) { bookProcessor.getBookById(1L); } }
方案2:确保依赖注入的是代理对象
严格通过Spring的依赖注入获取BookProcessor,绝对不要手动new。比如用构造函数注入(Spring官方推荐):
@Service @DependsOn("bookProcessor") public class Readers { private final BookProcessor bookProcessor; // 构造函数注入,Spring会自动传入BookProcessor的代理对象 public Readers(BookProcessor bookProcessor) { this.bookProcessor = bookProcessor; } // 业务方法中正常调用即可触发缓存 public void readBook(Long bookId) { Book book = bookProcessor.getBookById(bookId); // 处理逻辑 } }
方案3:初始化阶段强制获取代理对象(特殊场景用)
如果必须在初始化阶段调用缓存方法,可以通过ApplicationContext手动获取BookProcessor的代理对象:
@Service @DependsOn("bookProcessor") public class Readers implements ApplicationContextAware { private ApplicationContext context; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.context = applicationContext; } @PostConstruct public void init() { // 通过上下文获取代理对象 BookProcessor bookProcessor = context.getBean(BookProcessor.class); bookProcessor.getBookById(1L); } }
内容的提问来源于stack exchange,提问作者F Goli
相关产品推荐
相关产品推荐

