1. 概述

本文将介绍如何在 Spring 应用中配置多个缓存管理器(Cache Manager)。这种需求在实际开发中并不少见,比如某些数据需要高性能本地缓存(如 Caffeine),而另一些则适合简单内存缓存(如 ConcurrentHashMap),这就需要我们灵活地使用多个缓存管理器。

2. Spring 缓存机制回顾

Spring 的缓存功能通过注解实现,核心思想是:对相同输入的方法调用,直接返回缓存结果,避免重复执行。这在 I/O 密集型或计算昂贵的操作中非常实用。

启用缓存非常简单,只需在配置类上添加 @EnableCaching 注解:

@Configuration
@EnableCaching
public class MultipleCacheManagerConfig {}

接着就可以在方法上使用 @Cacheable 注解开启缓存:

@Cacheable(cacheNames = "customers")
public Customer getCustomerDetail(Integer customerId) {
    return customerDetailRepository.getCustomerDetail(customerId);
}

踩坑提醒:只要加了 @EnableCaching,Spring Boot 就会自动创建一个默认的缓存管理器。
⚠️ 默认使用的实现是 ConcurrentHashMap,适用于简单场景,但不具备过期、淘汰等高级特性。

3. 配置多个缓存管理器

有时候单一缓存策略无法满足业务需求。例如:

  • 用户信息:访问频繁,需高性能缓存(Caffeine)
  • 临时订单价格:数据量小、生命周期短,用内存 Map 即可

下面我们通过一个例子,展示如何在一个应用中同时使用 CaffeineCacheManagerConcurrentMapCacheManager

3.1. 使用 @Primary 注解指定默认缓存管理器

这是最简单直接的方式:定义多个 CacheManager Bean,并用 @Primary 标记其中一个作为默认。

@Configuration
@EnableCaching
public class MultipleCacheManagerConfig {

    @Bean
    @Primary
    public CacheManager cacheManager() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager("customers", "orders");
        cacheManager.setCaffeine(Caffeine.newBuilder()
          .initialCapacity(200)
          .maximumSize(500)
          .weakKeys()
          .recordStats());
        return cacheManager;
    }

    @Bean
    public CacheManager alternateCacheManager() {
        return new ConcurrentMapCacheManager("customerOrders", "orderprice");
    }
}

使用时:

  • 不指定 cacheManager 的方法 → 使用 @Primary 标记的 CaffeineCacheManager
  • 显式指定 cacheManager 的方法 → 使用对应 Bean
@Cacheable(cacheNames = "customers")
public Customer getCustomerDetail(Integer customerId) {
    return customerDetailRepository.getCustomerDetail(customerId);
}

@Cacheable(
    cacheNames = "customerOrders", 
    cacheManager = "alternateCacheManager"
)
public List<Order> getCustomerOrders(Integer customerId) {
    return customerDetailRepository.getCustomerOrders(customerId);
}

优点:配置清晰,易于理解
缺点:灵活性差,每个方法都要手动指定 cacheManager

3.2. 继承 CachingConfigurerSupport 自定义默认管理器

另一种方式是让配置类继承 CachingConfigurerSupport,并通过重写 cacheManager() 方法来指定默认缓存管理器。

@Configuration
@EnableCaching
public class MultipleCacheManagerConfig extends CachingConfigurerSupport {

    @Bean
    public CacheManager cacheManager() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager("customers", "orders");
        cacheManager.setCaffeine(Caffeine.newBuilder()
          .initialCapacity(200)
          .maximumSize(500)
          .weakKeys()
          .recordStats());
        return cacheManager;
    }

    @Bean
    public CacheManager alternateCacheManager() {
        return new ConcurrentMapCacheManager("customerOrders", "orderprice");
    }

    @Override
    public CacheManager cacheManager() {
        return cacheManager(); // 返回我们定义的 Caffeine 管理器作为默认
    }
}

⚠️ 注意:这里有两个 cacheManager() 方法,一个是 @Bean,一个是 @Override。后者是 CachingConfigurerSupport 提供的钩子方法,用于指定默认缓存管理器。

其余使用方式与 3.1 完全一致。

适用场景:你希望更精确控制 Spring 缓存的默认行为
优势:比 @Primary 更“正统”,适合复杂项目

3.3. 使用 CacheResolver 实现动态路由

如果你希望根据方法名、参数、注解等条件动态选择缓存管理器,CacheResolver 是最佳选择。

步骤一:实现自定义 CacheResolver

public class MultipleCacheResolver implements CacheResolver {
    
    private final CacheManager simpleCacheManager;
    private final CacheManager caffeineCacheManager;    
    private static final String ORDER_CACHE = "orders";    
    private static final String ORDER_PRICE_CACHE = "orderprice";
    
    public MultipleCacheResolver(CacheManager simpleCacheManager, CacheManager caffeineCacheManager) {
        this.simpleCacheManager = simpleCacheManager;
        this.caffeineCacheManager = caffeineCacheManager;
    }

    @Override
    public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) {
        Collection<Cache> caches = new ArrayList<>();
        String methodName = context.getMethod().getName();

        if ("getOrderDetail".equals(methodName)) {
            caches.add(caffeineCacheManager.getCache(ORDER_CACHE));
        } else {
            caches.add(simpleCacheManager.getCache(ORDER_PRICE_CACHE));
        }
        return caches;
    }
}

步骤二:注册 CacheResolver Bean

@Configuration
@EnableCaching
public class MultipleCacheManagerConfig extends CachingConfigurerSupport {

    @Bean
    public CacheManager cacheManager() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager("customers", "orders");
        cacheManager.setCaffeine(Caffeine.newBuilder()
          .initialCapacity(200)
          .maximumSize(500)
          .weakKeys()
          .recordStats());
        return cacheManager;
    }

    @Bean
    public CacheManager alternateCacheManager() {
        return new ConcurrentMapCacheManager("customerOrders", "orderprice");
    }

    @Bean
    public CacheResolver cacheResolver() {
        return new MultipleCacheResolver(alternateCacheManager(), cacheManager());
    }
}

步骤三:在方法中使用 cacheResolver

@Component
public class OrderDetailBO {

    @Autowired
    private OrderDetailRepository orderDetailRepository;

    @Cacheable(cacheNames = "orders", cacheResolver = "cacheResolver")
    public Order getOrderDetail(Integer orderId) {
        return orderDetailRepository.getOrderDetail(orderId);
    }

    @Cacheable(cacheNames = "orderprice", cacheResolver = "cacheResolver")
    public double getOrderPrice(Integer orderId) {
        return orderDetailRepository.getOrderPrice(orderId);
    }
}

优势

  • ✅ 完全动态控制,可基于任意逻辑选择缓存管理器
  • ✅ 解耦配置与使用,提升可维护性

缺点

  • ❌ 需要额外编码,复杂度略高
  • ❌ 调试时不如静态配置直观

4. 总结

方式 适用场景 推荐指数
@Primary 多数方法用默认缓存,少数特殊处理 ⭐⭐⭐⭐
CachingConfigurerSupport 希望精确控制默认行为 ⭐⭐⭐⭐
CacheResolver 需要动态、条件化选择缓存管理器 ⭐⭐⭐⭐⭐

📌 核心要点

  • Spring 默认会自动装配一个 ConcurrentMapCacheManager,但生产环境建议显式配置
  • 多缓存管理器的关键在于:明确默认 + 灵活指定
  • CacheResolver 是最灵活的方式,适合中大型项目或统一缓存策略封装

示例代码已托管至 GitHub:https://github.com/baeldung/spring-boot-tutorials/tree/master/spring-boot-caching


原始标题:Using Multiple Cache Managers in Spring | Baeldung