Spring中@Cacheable无法识别动态设置的cacheResolver值如何解决?
你当前的AOP方案不生效是因为Spring缓存模块对注解的解析发生在Bean初始化阶段:Spring会在应用启动时通过AnnotationCacheOperationSource扫描所有带@Cacheable的方法,提前把注解属性解析为CacheOperation元数据缓存起来,后续方法调用时直接读取预解析的元数据,不会再重复读取方法上的注解内容。你在运行时通过反射修改注解memberValues的时机太晚,修改后的内容根本不会被Spring缓存逻辑用到。
你提到所有方法的cacheResolver值都相同,直接用Spring原生提供的全局默认配置即可,不需要做任何注解hack,方案如下:
最优方案:配置全局默认CacheResolver
你只需要实现CachingConfigurer接口,重写cacheResolver方法,将你自定义的cacheableResolver设为全局默认值,所有未显式指定cacheResolver属性的@Cacheable注解都会自动使用该Resolver:
import org.springframework.cache.annotation.CachingConfigurer; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.interceptor.CacheResolver; import org.springframework.context.annotation.Configuration; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @Configuration @EnableCaching public class CacheConfiguration implements CachingConfigurer { @Autowired @Qualifier("cacheableResolver") private CacheResolver customCacheResolver; @Override public CacheResolver cacheResolver() { // 返回你自定义的CacheResolver作为全局默认 return customCacheResolver; } // 如果需要自定义CacheManager、KeyGenerator等也可以重写对应方法 }
修改后你的测试代码不需要做任何改动,@Cacheable(value = "test")会自动使用你配置的cacheableResolver处理缓存逻辑。
可选方案:自定义CacheResolver做动态分发
如果你后续需要更灵活的缓存策略,比如根据缓存名、方法参数等运行时信息选择不同的缓存源,可以直接在你自定义的CacheResolver里写分发逻辑,不需要修改任何业务代码的注解:
import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.cache.interceptor.CacheOperationInvocationContext; import org.springframework.cache.interceptor.CacheResolver; import org.springframework.stereotype.Component; import java.util.Collection; import java.util.Collections; @Component("cacheableResolver") public class CustomCacheResolver implements CacheResolver { // 注入你需要的多个CacheManager private final CacheManager defaultCacheManager; private final CacheManager remoteCacheManager; public CustomCacheResolver(CacheManager defaultCacheManager, CacheManager remoteCacheManager) { this.defaultCacheManager = defaultCacheManager; this.remoteCacheManager = remoteCacheManager; } @Override public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) { String cacheName = context.getOperation().getCacheNames().iterator().next(); // 动态判断使用哪个缓存源 if (cacheName.startsWith("remote_")) { return Collections.singleton(remoteCacheManager.getCache(cacheName)); } return Collections.singleton(defaultCacheManager.getCache(cacheName)); } }
不推荐的方案:提前修改注解属性
如果你一定要通过修改注解属性实现需求,需要把修改时机提前到Spring解析缓存注解之前,通过BeanPostProcessor在Bean初始化阶段修改方法上的@Cacheable属性,运行时AOP修改是无效的。该方案属于反射hack,依赖Spring内部实现,版本升级可能失效,因此不推荐使用。
内容的提问来源于stack exchange,提问作者futurexv

