- 使用 Spring Initializr 创建 Spring Boot 应用程序
- 在Spring Boot中配置Cassandra
- 在 Spring Boot 上配置 Tomcat 连接池
- 将Camel消息路由到嵌入WildFly的Artemis上
在这篇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上找到。
我阅读了有关 JSR 107 缓存 (JCache) 的内容。 我很困惑:据我所知,每个 CPU 都管理其缓存内存(无需操作系统的任何帮助)。 那么,为什么我们需要 Java 缓存处理程序? (如果C
好吧,我是 jQuery 的新手。我一直在这里和那里搞乱一点点并习惯它。我终于明白了(它并不像某些人想象的那么难)。因此,鉴于此链接:http://jqueryui.com/sortable/#dis
我正在使用 Struts 2 和 Hibernate。我有一个简单的表,其中包含一个日期字段,用于存储有关何时发生特定操作的信息。这个日期值显示在我的 jsp 中。 我遇到的问题是hibernate更
我有点不确定这里发生了什么,但是我试图解释正在发生的事情,也许一旦我弄清楚我到底在问什么,就可能写一个更好的问题。 我刚刚安装了Varnish,对于我的请求时间来说似乎很棒。这是一个Magneto 2
解决 Project Euler 的问题后,我在论坛中发现了以下 Haskell 代码: fillRow115 minLength = cache where cache = ((map fill
我正试图找到一种方法来为我网络上的每台计算机缓存或存储某些 python 包。我看过以下解决方案: pypicache但它不再被积极开发,作者推荐 devpi,请参见此处:https://bitbuc
我想到的一个问题是可以从一开始就缓存网络套接字吗?在我的拓扑中,我在通过双 ISP 连接连接到互联网的 HAProxy 服务器后面有 2 个 Apache 服务器(带有 Google PageSpee
我很难说出不同缓存区域 (OS) 之间的区别。我想简要解释一下磁盘\缓冲区\交换\页面缓存。他们住在哪里?它们之间的主要区别是什么? 据我了解,页面缓存是主内存的一部分,用于存储从 I/O 设备获取的
1.题目 请你为最不经常使用(LFU)缓存算法设计并实现数据结构。 实现 LFUCache 类: LFUCache(int capacity) - 用数据结构的容量 capacity 初始化对象 in
1.题目 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类: ① LRUCache(int capacity) 以正整数作为容量 capacity
我想在访问该 View 时关闭某些页面的缓存。它适用于简单查询模型对象的页面。 好像什么时候 'django.middleware.cache.FetchFromCacheMiddleware', 启
documents为 ExePackage element state Cache属性的目的是 Whether to cache the package. The default is "yes".
我知道 docker 用图层存储每个图像。如果我在一台开发服务器上有多个用户,并且每个人都在运行相同的 Dockerfile,但将镜像存储为 user1_myapp . user2 将其存储为 use
在 Codeigniter 中没有出现缓存问题几年后,我发现了一个问题。我在其他地方看到过该问题,但没有适合我的解决方案。 例如,如果我在 View 中更改一些纯 html 文本并上传新文件并按 F5
我在 Janusgraph 文档中阅读了有关 Janusgraph Cache 的内容。关于事务缓存,我几乎没有怀疑。我在我的应用程序中使用嵌入式 janusgrah 服务器。 如果我只对例如进行读取
我想知道是否有来自终端的任何命令可以用来匹配 Android Studio 中执行文件>使缓存无效/重新启动的使用。 谢谢! 最佳答案 According to a JetBrains employe
我想制作一个 python 装饰器来内存函数。例如,如果 @memoization_decorator def add(a, b, negative=False): print "Com
我经常在 jQuery 事件处理程序中使用 $(this) 并且从不缓存它。如果我愿意的话 var $this = $(this); 并且将使用变量而不是构造函数,我的代码会获得任何显着的额外性能吗?
是的,我要说实话,我不知道varnish vcl,我可以解决一些基本问题,但是我不太清楚,这就是为什么我遇到问题了。 我正在尝试通过http请求设置缓存禁止,但是该请求不能通过DNS而是通过 Varn
在 WP 站点上加载约 4000 个并发用户时遇到此问题。 这是我的配置: F5 负载均衡器 ---> Varnish 4,8 核,32 Gb RAM ---> 9 个后端,4 个核,每个 16 RA
我是一名优秀的程序员,十分优秀!