在这篇Spring Boot文章中,我们将了解如何在Spring Boot中配置多个缓存管理器应用程序。
广告
在标准的Spring Boot应用程序中,我们可能只需要一个缓存提供者,但是有很多用例,你想配置多个缓存提供者,并喜欢根据你的要求使用这些缓存。本文将介绍Spring Boot缓存API以及在Spring Boot中*配置多个缓存管理器的能力。 *有几种方法可以做到这一点,正确的方法取决于你打算如何使用缓存。本文作为一个指南,选择最适合你的要求的方法。在这篇文章中,我们将使用以下两种缓存API进行配置。
本文假设你有Spring Boot和Spring缓存API的相关知识。
让我们从设置应用程序开始。我们将使用Spring自动配置来为我们完成繁重的工作。你可以使用Spring Initializr生成应用程序的结构,也可以使用IDE生成应用程序的结构。
下面是我们的pom.xml的样子
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath />
<!-- lookup parent from repository -->
</parent>
<groupId>com.javadevjournal</groupId>
<artifactId>multiple-cache-manager</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>multiple-cache-manager</name>
<description>Multiple Cache manager with spring boot</description>
<properties>
<java.version>11</java.version>
<ehcache-version>3.9.0</ehcache-version>
<caffeine-version>2.8.6</caffeine-version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>${caffeine-version}</version>
</dependency>
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>${ehcache-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
我们在这个例子中使用Caffeine缓存和默认的ConcurrentHashMap缓存。
Spring提供了一个强大而灵活的缓存抽象。缓存抽象使得在Spring应用程序中实现缓存变得很容易。我们可以使用@EnableCachingAnnotation
启用缓存API。如果我们没有明确指定任何其他的缓存,Spring将回到ConcurrentHashMap
作为底层缓存。
@Configuration
@EnableCaching
public class MultiCacheManagerConfig{
//to enable caching for our application
}
如前所述,在Spring Boot中,有很多方法可以启用和配置多个缓存管理器。让我们看看这些选项。
如果你的应用程序在大多数情况下使用一个缓存提供者,而只想在特定情况下使用另一个缓存管理器,使用CacheConfigurerSupport
配置多个缓存管理器将为你提供更大的灵活性。
cacheManager
与@CacheConfig
或@Cacheable
注释。让我们看看如何配置它。
package com.javadevjournal.config;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
@Configuration
@EnableCaching
public class MultiCacheManagerConfig extends CachingConfigurerSupport {
public String[] cacheNames = {
"products"
};
/**
* We are using CachingConfigurerSupport to define out main caching
* provider. In our case it's Caffeine cache. This will be the default cache provider
* for our application. If we don't provide explicit cache manager, Spring Boot
* will pick this as default cache provider.
* @return
*/
@Override
@Bean // good to have but not strictly necessary
public CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCacheNames(Arrays.asList(
"customers",
"products"
));
cacheManager.setCaffeine(caffeineCacheBuilder());
return cacheManager;
}
Caffeine < Object, Object > caffeineCacheBuilder() {
return Caffeine.newBuilder()
.initialCapacity(100)
.maximumSize(500)
.expireAfterAccess(10, TimeUnit.MINUTES)
.weakKeys()
.recordStats();
}
/**
* Second cache provider which can work as fallback or will be used when invoked explicitly in the
* code base.
*/
@Bean
CacheManager alternateCacheManager() {
return new ConcurrentMapCacheManager(cacheNames);
}
}
使用Caffeine缓存,我们要配置2个缓存(客户和产品),而使用默认缓存;我们要设置产品缓存。以下是你如何在你的应用程序中使用这些缓存管理器的方法
//Class levels
@CacheConfig(cacheManager = "alternateCacheManager")
public class DefaultProductService {
}
//method levels
@Cacheable(cacheNames = "products", cacheManager = "alternateCacheManager")
@Override
public Product getProductByCode(String code) {
}
@Cacheable(cacheNames = "products")
@Override
public Product getProductByBrand(String brand) {
}
如果我们不想使用CacheConfigurerSupport
,我们可以使用@Primary annotation
标记一个Bean作为主要的。如果我们没有用@CacheConfig
或@Cacheable
注解来指定cacheManager,Spring会自动挑选主豆。
@Configuration
@EnableCaching
public class MultiCacheManagerConfig {
public String[] cacheNames = {
"products"
};
@Bean
@Primary
public CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCacheNames(Arrays.asList(
"customers",
"products"
));
cacheManager.setCaffeine(caffeineCacheBuilder());
return cacheManager;
}
Caffeine < Object, Object > caffeineCacheBuilder() {
return Caffeine.newBuilder()
.initialCapacity(100)
.maximumSize(500)
.expireAfterAccess(10, TimeUnit.MINUTES)
.weakKeys()
.recordStats();
}
@Bean
CacheManager alternateCacheManager() {
return new ConcurrentMapCacheManager(cacheNames);
}
}
CacheResolver
提供了更细化的控制。你应该考虑使用CacheResolver。
CacheResolver
与JSR-107比较吻合。作为第一步,我们需要通过扩展CacheResolver
来创建我们自定义的CacheResolver
。
package com.javadevjournal.caching;
import com.javadevjournal.service.impl.DefaultCustomerService;
import com.javadevjournal.service.impl.DefaultProductService;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
import org.springframework.cache.interceptor.CacheResolver;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
public class CustomCacheResolver implements CacheResolver {
private final CacheManager cacheManager;
private final CacheManager alternateCacheManager;
public CustomCacheResolver(final CacheManager cacheManager, CacheManager alternateCacheManager) {
this.cacheManager = cacheManager;
this.alternateCacheManager = cacheManager;
}
@Override
public Collection << ? extends Cache > resolveCaches(CacheOperationInvocationContext << ? > context) {
Collection < String > cacheNames = getCacheNames(context);
if (cacheNames == null) {
return Collections.emptyList();
}
Collection < Cache > result = new ArrayList < > (cacheNames.size());
if (context.getTarget() instanceof DefaultProductService) {
for (String cacheName: cacheNames) {
Cache cache = cacheManager.getCache(cacheName);
if (cache == null) {
throw new IllegalArgumentException("Cannot find cache named '" +
cacheName + "' for " + context.getOperation());
}
result.add(cache);
}
}
if (context.getTarget() instanceof DefaultCustomerService) {
for (String cacheName: cacheNames) {
Cache cache = alternateCacheManager.getCache(cacheName);
if (cache == null) {
throw new IllegalArgumentException("Cannot find cache named '" +
cacheName + "' for " + context.getOperation());
}
result.add(cache);
}
}
return result;
}
protected Collection < String > getCacheNames(CacheOperationInvocationContext << ? > context) {
return context.getOperation().getCacheNames();
}
}
下一步是将我们的自定义CacheResolver
定义为一个bean。我们正在使用我们的Config类来扩展CachingConfigurerSupport
。
@Configuration
@EnableCaching
public class MultiCacheManagerConfig extends CachingConfigurerSupport {
....
@Bean
@Override
public CacheResolver cacheResolver() {
return new CustomCacheResolver(cacheManager(), alternateCacheManager());
}
}
为了使用自定义的CacheResolver,我们可以用@Cacheable或其他缓存注解来传递它。
@Cacheable(cacheNames = "products", cacheResolver = "cacheResolver")
@Override
public Product getProductByBrand(String brand) {
}
为了检查多个缓存管理器是否按预期工作并返回缓存实例,让我们创建一个简单的控制器和服务类来看看工作流程的运行情况。
我们的产品控制器将有1个方法,它将使用DefaultProductService
来获取产品数据。DefaultProductService
服务将使用备用的缓存管理器来处理缓存。
@RestController
@RequestMapping("/products")
public class ProductController {
@Autowired
ProductService productService;
@Autowired
CacheManager alternateCacheManager;
@GetMapping("/product/{code}")
public Product getProductByCode(@PathVariable(value = "code") String code) {
Product product = productService.getProductByCode(code);
alternateCacheManager.getCacheNames(); // this is only for demo purpose, don't use this in real life application
return product;
}
}
@Service("productService")
public class DefaultProductService implements ProductService {
private static final Logger LOG = LoggerFactory.getLogger(DefaultProductService.class);
@Cacheable(cacheNames = "products", cacheManager = "alternateCacheManager")
@Override
public Product getProductByCode(String code) {
LOG.info("Get product by code {} ", code);
Product product = new Product();
product.setCode(code);
product.setBrand("Sony");
product.setDescription("Sony new camera");
product.setName("Sony Alpha A7S");
return product;
}
}
客户控制器将调用DefaultCustomerService
,它将回复defaulr CacheManager
来处理缓存。
@RestController
@RequestMapping("/customers")
public class CustomerController {
@Autowired
CustomerService customerService;
@Autowired
CacheManager cacheManager;
@GetMapping("/customer/{id}")
public Customer getCustomerByID(@PathVariable(value = "id") Long id) {
Customer customer = customerService.getCustomer(id);
cacheManager.getCacheNames();
return customer;
}
}
@Service
public class DefaultCustomerService implements CustomerService {
private static final Logger LOG = LoggerFactory.getLogger(DefaultCustomerService.class);
@Cacheable(cacheNames = "customers")
@Override
public Customer getCustomer(Long id) {
LOG.info("Fetching customer information for Id {} ", id);
Customer customer = new Customer();
customer.setEmail("[email protected]");
customer.setFirstName("Javadev");
customer.setLastName("Journal");
customer.setId(id);
return customer;
}
}
运行应用程序并点击以下网址。
http://localhost:8080/products/product/1
http://localhost:8080/products/product/2
http://localhost:8080/products/product/1
http://localhost:8080/customers/customer/1
http://localhost:8080/customers/customer/2
http://localhost:8080/customers/customer/1
对于#1,#2,#4和#5,你会看到日志语句,而对于其他的,将没有日志语句,因为数据将从缓存中提供。这就是你的日志可能的样子。
2020-10-21 16:57:48.611 INFO 99215 --- [nio-8080-exec-1] c.j.service.impl.DefaultProductService : Get product by code 1
2020-10-21 16:57:53.314 INFO 99215 --- [nio-8080-exec-2] c.j.service.impl.DefaultProductService : Get product by code 2
2020-10-21 16:58:46.810 INFO 99215 --- [nio-8080-exec-6] c.j.service.impl.DefaultCustomerService : Fetching customer information for Id 1
2020-10-21 16:58:56.264 INFO 99215 --- [nio-8080-exec-7] c.j.service.impl.DefaultCustomerService : Fetching customer information for Id 2
为了更好地理解,这里有一些屏幕截图
我们将Caffeine缓存配置为同时处理产品和客户缓存,但在这个例子中我们只使用了客户缓存。
在这篇文章中,我们看到了如何使用Spring缓存在Spring Boot中配置多个缓存管理器。我们看到了在Spring中处理多个缓存管理器的下列选项。
CacheConfigurerSupport
进行配置。@Primary
注解。CacheResolver
。一如既往,本文的源代码可以在GitHub上找到。
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
内容来源于网络,如有侵权,请联系作者删除!