Spring中@CachePut启动时无法填充缓存的问题排查
可能的原因
1. @PostConstruct 执行时机过早,缓存切面未生效
Spring 的 @PostConstruct 会在当前 Bean 初始化完成后立即执行,但此时整个 Spring 上下文的 AOP 缓存切面可能还未完全织入到 ClientImpl 的代理对象中。这时候调用 updateCache 方法,@CachePut 注解并未实际生效,数据没有被写入缓存。而后续调度器触发 updateCache 时,代理对象已完全初始化,@CachePut 正常工作,所以后续调用 queryWithCache 能命中缓存。
2. 缓存键生成策略导致键不匹配
如果项目自定义了 KeyGenerator,且逻辑中包含方法名作为键的一部分,会导致 queryWithCache 和 updateCache 两个无参方法生成的缓存键不一致,首次调用 queryWithCache 时无法命中 updateCache 写入的缓存项。(默认的 SimpleKeyGenerator 不会包含方法名,此情况仅针对自定义键生成器)
3. 代理类型不匹配导致缓存注解未被拦截
ClientImpl 实现了两个接口,Spring 默认生成 JDK 动态代理。若缓存切面在织入时,未正确识别 ClientCacheManager 接口方法上的 @CachePut 注解,会导致 updateCache 调用时未触发缓存写入操作。
对应的解决办法
1. 改用上下文刷新事件触发缓存初始化
替换 @PostConstruct,通过监听 ContextRefreshedEvent 确保在 Spring 上下文完全初始化(所有 AOP 切面织入完成)后再填充缓存:
@Component public class CacheInitializer implements ApplicationListener<ContextRefreshedEvent> { private static final Logger log = LoggerFactory.getLogger(CacheInitializer.class); @Autowired private ClientCacheManager clientCacheManager; @Override public void onApplicationEvent(ContextRefreshedEvent event) { // 避免父子上下文重复触发 if (event.getApplicationContext().getParent() == null) { log.debug("Startup data loading"); this.clientCacheManager.updateCache(); log.debug("Startup data loaded"); } } }
2. 验证缓存键一致性
若使用自定义 KeyGenerator,检查其逻辑是否为两个无参方法生成相同的键。可通过日志打印验证:
// 在 updateCache 和 queryWithCache 方法中添加日志 @Autowired private KeyGenerator keyGenerator; @Autowired private CacheManager cacheManager; // 示例:在 updateCache 中打印键 Method method = ClientCacheManager.class.getMethod("updateCache"); Object key = keyGenerator.generate(this, method, new Object[]{}); log.debug("Cache key for updateCache: {}", key);
3. 强制使用 CGLIB 代理
在缓存配置类中开启 proxyTargetClass = true,强制 Spring 使用 CGLIB 代理,确保实现类上的注解能被正确拦截:
@Configuration @EnableCaching(proxyTargetClass = true) public class CacheConfig { // 缓存管理器相关配置 }
内容的提问来源于stack exchange,提问作者SJuan76

