
Spring 的 @Cacheable 等缓存注解依赖 Spring AOP 代理机制生效;若在测试中手动 new MyService() 创建实例,将绕过 Spring 容器管理,导致代理失效、缓存不触发。
spring 的 `@cacheable` 等缓存注解依赖 spring aop 代理机制生效;若在测试中手动 `new myservice()` 创建实例,将绕过 spring 容器管理,导致代理失效、缓存不触发。
在 Spring 非 Boot 项目中启用声明式缓存(如 @Cacheable)时,核心前提是:被注解的方法必须由 Spring 容器管理的代理对象调用。Spring 通过 CGLIB 或 JDK 动态代理为 @Cacheable 方法织入缓存逻辑——但该代理仅作用于 Spring 托管的 Bean 实例。
问题代码中,TestConfig 的配置存在关键缺陷:
@Configuration
@EnableCaching
class TestConfig {
@Bean
MyApp myApp() {
return new MyApp(new MyService()); // ❌ 错误:手动 new,脱离容器
}
@Bean
CacheManager cacheManager() {
return new ConcurrentMapCacheManager("greeting");
}
}new MyService() 直接创建了原始对象,未经过 Spring 容器初始化,因此 @Cacheable 注解完全被忽略——即使 MyApp 本身是 Spring Bean,其内部持有的 MyService 实例仍是“裸对象”,无任何代理增强。
✅ 正确做法是:所有带切面注解(@Cacheable、@Transactional 等)的类,必须作为 @Bean 由 Spring 容器统一创建和注入。修改 TestConfig 如下:
@Configuration
@EnableCaching
@ComponentScan("com.somepkg") // 启用组件扫描,确保 MyService 被自动注册
static class TestConfig {
@Bean
MyApp myApp(MyService myService) { // ✅ 依赖注入,而非手动 new
return new MyApp(myService);
}
@Bean
CacheManager cacheManager() {
return new ConcurrentMapCacheManager("greeting");
}
}同时,确保 MyService 类被 Spring 扫描到(已用 @Component 标记),且 @ContextConfiguration 显式启用 @ComponentScan(如上所示)。若使用 @ComponentScan,可移除 @ContextConfiguration(classes = ...) 中对 TestConfig 的显式引用,改用 @ContextConfiguration(locations = "classpath:test-context.xml") 或保持当前方式但务必包含扫描。
⚠️ 注意事项:
- 不要混合手动
new和依赖注入:一旦某类需 AOP 增强,它必须 100% 由 Spring 管理生命周期; - 测试类中
@RunWith(SpringJUnit4ClassRunner.class)和@ContextConfiguration是必需的,确保 Spring TestContext 框架激活; -
ConcurrentMapCacheManager仅适用于单机测试环境,生产中建议替换为CaffeineCacheManager或分布式缓存实现; - 若
MyService有构造函数依赖,务必通过@Bean方法参数注入,而非new。
总结:Spring 缓存不是“魔法”,而是基于代理的运行时增强。让 Spring 创建每一个需要缓存的 Bean,是启用 @Cacheable 的唯一正确路径。 在测试中尤其容易因图方便而手动实例化,这恰恰是缓存失效最常见的根源。

















