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 即可
下面我们通过一个例子,展示如何在一个应用中同时使用 CaffeineCacheManager
和 ConcurrentMapCacheManager
。
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