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

Spring Boot @Cacheable注解在@PostConstruct及Groovy脚本中失效问题

问题根源与解决方案

核心原因

Spring的@Cacheable注解依赖动态代理实现缓存逻辑,而代理对象的创建时机在Bean生命周期的初始化阶段之后,这直接导致了@PostConstruct阶段调用方法时缓存失效,同时也是Groovy脚本中缓存不生效的关键原因。

具体场景分析

  1. @PostConstruct调用失效的原因
    Spring Bean的完整生命周期流程为:
  • 实例化Bean → 依赖注入 → 执行@PostConstruct方法 → 生成AOP代理并替换容器中的原始Bean实例

当你在SpringTestApplication的@PostConstruct方法中调用javaStarter.invokeTestMethod()时,JavaService的代理对象尚未生成,调用的是JavaService的原始对象方法,@Cacheable的拦截逻辑根本不会被触发,自然无法生效。

  1. REST控制器调用正常的原因
    REST控制器的方法是在Spring容器完全初始化完成后,由外部HTTP请求触发调用的。此时容器中的JavaService已经是代理对象,调用标注@Cacheable的方法时,代理会先执行缓存校验逻辑,命中缓存则直接返回结果,未命中才会调用原始方法并写入缓存。

  2. Groovy脚本中的问题
    Groovy脚本中缓存未生效,本质和@PostConstruct场景一致:要么脚本在容器未完全初始化时就获取了JavaService实例(此时还是原始对象),要么脚本通过非Spring容器的方式直接实例化了JavaService,导致代理逻辑无法介入。

解决方案

1. 容器初始化完成后再调用(解决@PostConstruct场景)

实现ApplicationListener<ContextRefreshedEvent>接口,监听容器初始化完成的事件,在回调中调用目标方法:

package test.spring.springtest;

import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

@Component
public class CacheableStartupListener implements ApplicationListener<ContextRefreshedEvent> {
    private final JavaStarter javaStarter;

    public CacheableStartupListener(JavaStarter javaStarter) {
        this.javaStarter = javaStarter;
    }

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        // 仅在根容器初始化完成后执行,避免父子容器重复触发
        if (event.getApplicationContext().getParent() == null) {
            javaStarter.invokeTestMethod("test");
        }
    }
}

替换掉原SpringTestApplication中的@PostConstruct方法,运行后会看到仅第一次输出Cache is not being used,后续两次直接使用缓存结果,符合预期。

2. 确保Groovy脚本正确获取代理对象

在Groovy脚本中,必须通过Spring容器的getBean()方法获取JavaService实例,且确保脚本在容器完全初始化后执行:

// 示例Groovy脚本代码
def context = // 已初始化完成的Spring ApplicationContext实例
def javaService = context.getBean(JavaService.class)
// 此时调用的是代理对象的方法,缓存逻辑会生效
javaService.testMethod("test")

禁止直接实例化JavaService对象,也不要在容器启动过程中过早获取Bean实例。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 15:57:01