«1. Обзор
В этом руководстве мы узнаем, как настроить несколько менеджеров кеша в приложении Spring.
2. Кэширование
Spring применяет кэширование к методам, чтобы наше приложение не выполняло один и тот же метод несколько раз для одних и тех же входных данных.
Реализовать кэширование в приложении Spring очень просто. Это можно сделать, добавив аннотацию @EnableCaching в наш класс конфигурации:
@Configuration
@EnableCaching
public class MultipleCacheManagerConfig {}
Затем мы можем начать кэшировать вывод метода, добавив аннотацию @Cacheable к методу:
@Cacheable(cacheNames = "customers")
public Customer getCustomerDetail(Integer customerId) {
return customerDetailRepository.getCustomerDetail(customerId);
}
Как только добавляем указанную выше конфигурацию, Spring Boot сам создает для нас менеджер кеша.
По умолчанию он использует ConcurrentHashMap в качестве базового кеша, если мы не указали другой явно.
3. Настройка нескольких менеджеров кеша
В некоторых случаях нам может понадобиться использовать более одного менеджера кеша в нашем приложении. Итак, давайте посмотрим, как мы можем сделать это в нашем приложении Spring Boot на примере.
В нашем примере мы будем использовать CaffeineCacheManager и простой ConcurrentMapCacheManager.
CaffeineCacheManager предоставляется стартером spring-boot-starter-cache. Он будет автоматически настроен Spring, если присутствует Caffeine, который представляет собой библиотеку кэширования, написанную на Java 8.
ConcurrentMapCacheManager использует реализацию кэша с использованием ConcurrentHashMap.
Мы можем сделать это следующими способами.
3.1. Используя @Primary
Мы можем создать два bean-компонента менеджеров кеша в нашем классе конфигурации. Затем мы можем сделать один bean-компонент основным:
@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");
}
}
Теперь Spring Boot будет использовать CaffeineCacheManager по умолчанию для всех методов, пока мы явно не укажем наш alterCacheManager для метода:
@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);
}
В приведенном выше примере наш приложение будет использовать CaffeineCacheManager для метода getCustomerDetail(). А для метода getCustomerOrders() он будет использовать alterCacheManager.
3.2. Расширение CachingConfigurerSupport
Еще один способ сделать это — расширить класс CachingConfigurerSupport и переопределить метод cacheManager(). Этот метод возвращает 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-компонент с именем alterCacheManager. Мы можем использовать этот alterCacheManager для метода, явно указав его, как в последнем примере.
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<Cache>();
if ("getOrderDetail".equals(context.getMethod().getName())) {
caches.add(caffeineCacheManager.getCache(ORDER_CACHE));
} else {
caches.add(simpleCacheManager.getCache(ORDER_PRICE_CACHE));
}
return caches;
}
}
В этом случае нам нужно переопределить метод resolveCaches интерфейса CacheResolver.
В нашем примере мы выбираем диспетчер кеша на основе имени метода. После этого нам нужно создать bean-компонент нашего пользовательского CacheResolver:
@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);
}
}
Здесь мы передаем имя нашего bean-компонента CacheResolver в элементе cacheResolver.
4. Заключение
В этой статье мы узнали, как включить кэширование в нашем приложении Spring Boot. Затем мы узнали три способа использования нескольких менеджеров кеша в нашем приложении.
Как всегда, код этих примеров доступен на GitHub.