我希望在我的Web应用程序中配置多个Spring缓存管理器,并且能够在项目的不同位置使用不同的缓存管理器。有没有什么方法可以实现这个目标?
我希望在我的Web应用程序中配置多个Spring缓存管理器,并且能够在项目的不同位置使用不同的缓存管理器。有没有什么方法可以实现这个目标?
你可以有多种方式来实现这个,正确答案取决于你对缓存的使用。
如果你在90%的情况下使用CacheManager A,而只有10%的情况下使用B,我建议你创建一个默认的CacheManager
用于A(你需要通过CacheConfigurerSupport
扩展指定它),类似于:
@Configuration
@EnableCaching
public class CacheConfig extends CachingConfigurerSupport {
@Override
@Bean // not strictly necessary
public CacheManager cacheManager() { ... CacheManager A }
@Bean
public CacheManager bCacheManager() { ... CacheManager B }
}
对于需要使用另一个缓存管理器的类,您可以在顶部添加一个CacheConfig
,针对10%的情况进行处理。
@CacheConfig(cacheManager="bCacheManager")
public class MyService { /*...*/ }
如果你只需要在一个方法中使用另一个缓存管理器,你也可以在方法级别上指定。
@Cacheable(cacheNames = "books", cacheManager = "bCacheManager")
public Book findById(long id) { /*...*/ }
如果您不处于这种情况下,您需要一种方法来知道每种情况下应该使用哪个缓存管理器。您可以基于目标类型 (MyService
) 或缓存名称 (books
) 来进行判断。您需要实现一个 CacheResolver
来执行此转换。
@Configuration
@EnableCaching
public class CacheConfig extends CachingConfigurerSupport {
@Override
public CacheResolver cacheResolver() { ... }
}
查看CacheResolver
的Javadoc以获取更多详细信息。在实现中,您可能有几个CacheManager
实例(无论是作为Bean还是其他方式),根据您的逻辑内部调用它们来确定应该使用哪个管理器。
我在评论中看到你提到了“模块”。缓存实际上是基础设施问题,因此我强烈建议将该决策移到应用级别。您可以将缓存标记为“本地”并将其他缓存标记为“集群”,但最好有一种命名规则使其更容易。不要在模块级别选择缓存管理器。
这篇博客文章通过其他示例说明了这一点。
KeyGenerator
和ErrorHandler
。 - Stephane NicollCacheResolver
的一些信息。 CacheResolver
有一个方法:Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context);
这个操作的可缓存性与其所在类、方法、参数等相关。
基本形式如下:
public class CustomCacheResolver implements CacheResolver {
private final CacheManager cacheManager;
public CustomCacheResolver(CacheManager cacheManager){
this.cacheManager = cacheManager;
}
@Override
public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) {
Collection<Cache> caches = getCaches(cacheManager, context);
return caches;
}
private Collection<Cache> getCaches(CacheManager cacheManager, CacheOperationInvocationContext<?> context) {
return context.getOperation().getCacheNames().stream()
.map(cacheName -> cacheManager.getCache(cacheName))
.filter(cache -> cache != null)
.collect(Collectors.toList());
}
}
这里我仅使用一个CacheManager
以简略表述。但你可以将不同的CacheManager
绑定到CacheResolver
,并进行更精细的选择:如果类名是X
,则使用GuavaCacheManager
;否则使用EhCacheCacheManager
。
完成此步骤后,您应该注册CacheResolver
(同样,在这里可以绑定更多CacheManagers
):
@Configuration
@EnableCaching
public class CacheConfiguration extends CachingConfigurerSupport {
@Bean
@Override
public CacheManager cacheManager() {
// Desired CacheManager
}
@Bean
@Override
public CacheResolver cacheResolver() {
return new CustomCacheResolver(cacheManager());
}
}
@Cacheable
, @CachePut
, @CacheConfig
等注释中的一个中指定 CustomCacheResolver
。@Cacheable(cacheResolver="cacheResolver")
你可以像这样编写一个自定义的CacheManager bean
@Primary
@Bean("customerCacheManager")
public CacheManager cacheManager(RedisTemplate<String, Object> redisTemplate) {
Map<String, Long> expires = new HashMap<>();
expires.put("fundShareSplit", TimeUnit.SECONDS.toSeconds(60));
RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
cacheManager.setUsePrefix(true);
cacheManager.setExpires(expires);
List<String> cacheNames = Arrays.asList("fundShareSplit");
cacheManager.setCacheNames(cacheNames);
return cacheManager;
}
然后在您的注释中使用它,就像这样
@Cacheable(cacheManager = "customerCacheManager",value = "fundShareSplit",key="'smile:asset:fundSplit:fundId:'+#root.args[0]+'_localDate:'+#root.args[1]",unless="#result == null")