如何在spring cache java中配置多个缓存管理器

Posted

技术标签:

【中文标题】如何在spring cache java中配置多个缓存管理器【英文标题】:How to have multiple cache manager configuration in spring cache java 【发布时间】:2016-11-28 22:34:44 【问题描述】:

我想在我的 web 应用程序中配置多个 spring 缓存管理器,并且我可以在项目的不同位置使用不同的缓存管理器。有没有办法做到这一点。

【问题讨论】:

到目前为止你发现了什么? 我已经在项目的一个模块中配置了 EHCacheManager,现在我想在另一个模块中使用 RedisCacheManager,但是 spring 不允许在单个应用程序上下文中拥有 2 个 CacheManager 类型的 bean。然后我在我的配置类中实现了 CachingConfigurer 以避免这个问题。但我最终在我的ApplicationContext 中只有type-EHCacheManager 上的CacheManager bean。但我的要求是创建两个 cacheManagers bean,并且我应该能够在不同的模块中使用它们。我听说过 CompositeCacheManager,不确定这是否有帮助。非常感谢 【参考方案1】:

有几种方法可以做到这一点,正确的答案取决于你对缓存的使用。

你有一个“主”缓存管理器

如果您将 CacheManager A 用于 90% 的用例,而 B 用于 10%,我建议为 A 创建一个默认的 CacheManager(您需要通过 CacheConfigurerSupport 扩展名指定它),有些东西喜欢:

@Configuration
@EnableCaching
public class CacheConfig extends CachingConfigurerSupport 

    @Override
    @Bean // not strictly necessary
    public CacheManager cacheManager()  ... CacheManager A 

    @Bean
    public CacheManager bCacheManager()  ... CacheManager B 

然后对于 10% 的用例,您在需要使用其他缓存管理器的类的顶部添加 CacheConfig

@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 或不作为 bean),您将根据您的逻辑在内部调用它们以确定应该使用哪个管理器。

我在评论中看到您指的是“模块”。缓存实际上是一个基础设施问题,所以我强烈建议您在应用程序级别移动该决定。您可以将缓存标记为“本地”,而将其他标记为“集群”。但是您可能应该对名称进行某种命名以使其更容易。不要在模块级别选择缓存管理器。

this blog post 用其他例子说明了这一点。

【讨论】:

Stephane,我正在使用这种方法来扩展 Spring Boot 默认配置,但这似乎是解决我的问题的一种原始方法,你能建议我一个更好的方法来解决my problem 吗?谢谢 当心:在 spring-boot 1.5 中扩展 CachingConfigurerSupport 时,您将覆盖一些方法(默认 keyGenerator 和 errorHandler),这会使您的缓存无用。我使用了相同的方法而没有扩展该类。只需声明 bean 就足以让它工作。 您不必重写这些方法。否则,将使用默认的 KeyGeneratorErrorHandler 您好,感谢您的回答。除了 Spring 创建的默认缓存管理器之外,是否有可能创建一个缓存管理器?实际上,我让 Spring 使用 ehcache 自动配置 JCacheManager,我会保留默认的,而不必重新定义它。当我创建自己的缓存管理器时,由于@ConditionalOnMissingBean 而不再创建默认的缓存管理器。 请不要在评论中提问。答案是否定的,如果您创建自己的,自动配置将按原样退出。也就是说,您不必将其创建为 bean。【参考方案2】:

正如@Stephane Nicoll 解释的那样,您有多种选择。 我将尝试提供有关自定义CacheResolver 的一些信息。 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。但是您可以将不同的CacheManagers 绑定到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")

您可以查看here 以获取代码示例。

【讨论】:

【参考方案3】:

您可以像这样编写客户 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")

【讨论】:

以上是关于如何在spring cache java中配置多个缓存管理器的主要内容,如果未能解决你的问题,请参考以下文章

如何在基于xml的spring配置中为hibernate.javax.cache.uri属性指定相对路径

SpringBoot&Caffeine 灵活支持多个缓存配置策略

20springcloud如何使用spring-cache

20springcloud如何使用spring-cache

java 、spring配置问题,下面的配置是啥意思啊??

java 、spring配置问题,下面的配置是啥意思啊?