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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 12:45:26