«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.