在Spring中使用多个缓存管理器

2023/05/11

1. 概述

在本教程中,我们介绍如何在Spring应用程序中配置多个缓存管理器。

2. 缓存

Spring将缓存应用于方法,这样我们的应用程序就不会为相同的输入多次执行同一方法。

在Spring应用程序中实现缓存非常容易,这可以通过在我们的配置类中添加@EnableCaching注解来完成:


@Configuration
@EnableCaching
public class MultipleCacheManagerConfig extends CachingConfigurerSupport {

}

然后我们可以通过在方法上添加@Cacheable注解来开始缓存方法的输出:


@Component
public class CustomerDetailBO {

    @Autowired
    private CustomerDetailRepository customerDetailRepository;

    @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提供。 如果类路径存在Caffeine依赖,它将由Spring自动配置,这是一个用Java 8编写的缓存库。

ConcurrentMapCacheManager使用ConcurrentHashMap实现缓存。

我们可以通过以下方式做到这一点。

3.1 使用@Primary

我们可以在配置类中创建两个CacheManager bean,然后,我们可以将一个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");
    }
}

现在,Spring Boot将使用CaffeineCacheManager作为所有方法的默认缓存管理器,直到我们显式指定cacheManager参数为“alternateCacheManager”:


@Component
public class CustomerDetailBO {

    @Autowired
    private CustomerDetailRepository customerDetailRepository;

    @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()方法,它将使用alternateCacheManager作为缓存管理器。

3.2 继承CachingConfigurerSupport

另一种方法是继承CachingConfigurerSupport类并重写cacheManager()方法,此方法返回一个bean,它将成为我们应用程序的默认缓存管理器:


@Configuration
@EnableCaching
public class MultipleCacheManagerConfig extends CachingConfigurerSupport {

    @Bean
    @Override
    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");
    }
}

请注意,我们仍然可以创建另一个名为alternateCacheManager的bean, 我们可以通过显式指定方法将这个alternateCacheManager用做缓存管理器,就像我们在上一个示例中所做的那样。

3.3 使用CacheResolver

我们可以实现CacheResolver接口并创建一个自定义的CacheResolver:

public class MultipleCacheResolver implements CacheResolver {

    private static final String ORDER_CACHE = "orders";
    private static final String ORDER_PRICE_CACHE = "orderprice";

    private final CacheManager simpleCacheManager;
    private final CacheManager caffeineCacheManager;

    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<>();
        if ("getOrderDetail".equals(context.getMethod().getName()))
            caches.add(caffeineCacheManager.getCache(ORDER_CACHE));
        else
            caches.add(simpleCacheManager.getCache(ORDER_PRICE_CACHE));
        return caches;
    }
}

在这种情况下,我们必须重写CacheResolver接口的resolveCaches()方法。

在我们的示例中,我们根据方法名称选择缓存管理器。在此之后,我们需要创建一个自定义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);
    }
}

在这里,我们在cacheResolver参数中传递了CacheResolver bean的名称。

4. 总结

在本文中,我们了解了如何在Spring Boot应用程序中启用缓存。然后,我们介绍了三种在应用程序中使用多个缓存管理器的方法。

与往常一样,本教程的完整源代码可在GitHub上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章