Spring Cache + Caffeine的整合与使用
前言
对于一些项目里需要对数据库里的某些数据一直重复请求的,且这些数据基本是固定的,在这种情况下,可以借助简单使用本地缓存来缓存这些数据。这些介绍一下Spring Cache和Caffeine的使用。
引入依赖和CacheConfig
在pom文件里面引入下面的依赖:
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
在启动类上加上@EnableCaching的注解
@EnableCaching
public class SpringBootApplication{
}
新建一个CacheConfig类
@Configuration
public class CacheConfig {
/********************************
* @function : 生成缓存管理器
* @parameter : []
* @return : org.springframework.cache.CacheManager
* @date : 2023/12/13 14:46
********************************/
@Primary
@Bean("customCacheManager")
public CacheManager customCacheManager() {
SimpleCacheManager simpleCacheManager = new SimpleCacheManager();
List<Cache> cacheList = new ArrayList<>();
cacheList.add(customCache());
simpleCacheManager.setCaches(cacheList);
return simpleCacheManager;
}
/********************************
* @function : 生成自定义缓存容器
* @parameter : []
* @return : org.springframework.cache.Cache
* @date : 2023/12/13 14:46
********************************/
public Cache customCache() {
return new CaffeineCache("customCache", Caffeine.newBuilder()
.build(), true);
}
}
这里customCache()方法我并没有设置相关过期时间和最大值,不设置会导致没有默认过期时间和最大值。如果需要设置可以参考下面的写法
public Cache customCache() {
return new CaffeineCache("customCache", Caffeine.newBuilder()
.maximumSize(100)
.initialCapacity(100)
.expireAfterWrite(10, TimeUnit.MINUTES)
.recordStats()
.build(),
true);
}
CaffeineCache参数的讲解
- "customCache": 这是缓存的名称。在应用程序中,你可以通过这个名称来获取对应的缓存实例。
Caffeine.newBuilder(): 这是创建Caffeine缓存实例的起始点。newBuilder()返回一个Caffeine构建器对象,用于配置和定制缓存的各种属性。.maximumSize(100): 这是设置缓存的最大容量,即缓存可以容纳的最大条目数。在这个例子中,缓存的最大容量被设置为100。.initialCapacity(100): 这是设置缓存的初始容量,即在缓存初始化时分配的内部数据结构的初始大小。在这个例子中,初始容量被设置为100。.expireAfterWrite(10, TimeUnit.MINUTES): 这是设置缓存项在被写入后的过期时间。在这个例子中,缓存项将在被写入后的10分钟内过期。.recordStats(): 这是启用缓存统计信息的选项。启用后,你可以从缓存实例中获取有关缓存使用情况的统计信息,例如命中率、加载次数等。
使用中,对过期策略的使用会比较重要,对于过期的策略有:
写入后过期 (
expireAfterWrite): 缓存项被写入后的一段时间内过期。可以通过以下方式配置:Caffeine.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES)
.build();
在上述示例中,缓存项将在被写入后的10分钟内过期。
访问后过期 (
expireAfterAccess): 缓存项在一段时间内没有被访问后过期。可以通过以下方式配置:Caffeine.newBuilder()
.expireAfterAccess(15, TimeUnit.MINUTES)
.build();
在上述示例中,缓存项将在最后一次访问后的15分钟内过期。
定时过期 (
expireAfter): 缓存项在指定的固定时间内过期,不考虑写入或访问。可以通过以下方式配置:Caffeine.newBuilder()
.expireAfter(1, TimeUnit.HOURS)
.build();
在上述示例中,缓存项将在创建后的1小时内过期。
这些过期定时策略可以根据具体的使用场景和需求进行组合或选择。
上面不同写法将会导致生成不同的localcache实现类,可以在build方法中看到:

进入isBounded()方法:

如果使用缓存会调用localcache的get方法,最后进入computeIfAbsent()方法,对比上面两个实现类的实现,先是BoundedLocalCache:

UnboundedLocalCache:

下面这个并不会去检查是否过期。
使用示范
在MVC的使用,可以将缓存的注解标识于service层:
@Service
@Slf4j
@CacheConfig(cacheNames = "customCache", cacheManager = "customCacheManager")
public class CalDataInitServiceImpl implements ICalDataInitService {
@Cacheable(key = "#root.methodName + #sendCardName")
public int getSlotCount(String sendCardName) {
..方法体
return calCard.getSlotCount();
}
...
@CachePut(key = "#param")
public String updateCache(String param) {
// 对数据库更新某个值
return updatedValue;
}
@CacheEvict(key = "#param")
public void evictCache(String param) {
// 对数据库删除某个值
}
}
使用到的注解解析:
@Cacheable 是Spring框架中用于声明缓存规则的注解之一。它通常用于标记在方法上,以指示Spring在执行方法前先检查缓存,如果缓存中已有数据,则直接返回缓存中的数据,而不执行方法体。如果缓存中没有数据,则执行方法体,并将方法的返回值存入缓存。
以下是 以@Cacheable 注解为例的主要参数介绍和使用方式:
value(或cacheNames): 指定缓存的名称,可以指定一个或多个缓存。如果指定多个缓存,Spring会依次检查缓存,直到找到第一个有数据的缓存或全部检查完毕。示例:@Cacheable(value = "myCache")
public String getCachedData() {
// 方法体
}
key: 指定缓存项的键。默认情况下,Spring会使用方法的参数作为键,但你也可以通过key属性指定自定义的缓存键。示例:@Cacheable(value = "myCache", key = "#param")
public String getCachedData(String param) {
// 方法体
}
condition: 指定条件表达式,只有当条件满足时才会缓存。示例:@Cacheable(value = "myCache", condition = "#result != null")
public String getCachedData() {
// 方法体
}
unless: 指定一个条件表达式,当条件为 true 时,不会将结果放入缓存。示例:@Cacheable(value = "myCache", unless = "#result == null")
public String getCachedData() {
// 方法体
}
keyGenerator: 指定自定义的缓存键生成器。这个属性允许你提供一个实现了org.springframework.cache.interceptor.KeyGenerator接口的类,用于生成缓存键。示例:@Cacheable(value = "myCache", keyGenerator = "customKeyGenerator")
public String getCachedData() {
// 方法体
}
sync: 是否启用同步模式。如果设置为 true,可以解决并发查的问题,Spring会在调用方法时锁定缓存,防止多个线程同时访问数据库。默认为 false。示例:@Cacheable(value = "myCache", sync = true)
public String getCachedData() {
// 方法体
}
这些是 @Cacheable 注解的一些常用参数。可以根据实际需要选择合适的参数来定义缓存规则。
在Spring中,除了 @Cacheable,另外一些注解及其简要介绍:
@CacheEvict: 用于从缓存中移除数据。通常用于在方法执行后清空指定缓存。示例:@CacheEvict(value = "myCache", key = "#param")
public void evictCache(String param) {
// 方法体
}
@CachePut: 用于将方法的返回值更新到缓存中,常用于更新缓存而不影响方法的执行。示例:@CachePut(value = "myCache", key = "#param")
public String updateCache(String param) {
// 方法体
return updatedValue;
}
@Caching: 用于将多个缓存相关的注解组合在一起,实现复杂的缓存操作。示例:@Caching(
evict = {@CacheEvict(value = "cache1", key = "#param1")},
put = {@CachePut(value = "cache2", key = "#param2")}
)
public String complexCacheOperation(String param1, String param2) {
// 方法体
}
@CacheConfig: 用于在类级别配置缓存的一些公共属性,避免在每个方法上都重复指定相同的缓存名称等信息。示例:@CacheConfig(cacheNames = "commonCache")
public class MyService {
@Cacheable
public String getCachedData(String param) {
// 方法体
}
}
这些注解可以单独使用,也可以结合使用,以满足不同的缓存需求。
清空缓存的方法
清空所有缓存,可以不指定 value 和 key,如下所示:
@CacheEvict(allEntries = true)
public void evictAllCaches() {
// 方法体
}
在这个例子中,allEntries = true 表示清空所有缓存。
如果你想根据某个条件来判断是否清空缓存,可以使用 condition 属性,例如:
@CacheEvict(value = "myCache", key = "#param", condition = "#param != 'noEviction'")
public void evictCacheConditionally(String param) {
// 方法体
}
在上述例子中,只有当 param 不等于 'noEviction' 时才会执行缓存清空操作。
除了 @CacheEvict,在一些特定场景下,@CachePut 也可以被用来“清空”缓存,因为它将方法的返回值放入缓存,如果返回值为 null,相当于移除缓存项。这种方式通常在更新操作时使用。
注意事项
如下图代码所示,如果在updateCache方法又调用了同个类里面的getSlotCount()方法,是不会使用到缓存的,这是因为缓存的实现是通过AOP实现,在同个类里面调用方法,实际是通过this来调,不会调用到代理对象,因此相当于@Cacheable注解在这种情况是不生效的。
@Service
@Slf4j
@CacheConfig(cacheNames = "customCache", cacheManager = "customCacheManager")
public class CalDataInitServiceImpl implements ICalDataInitService {
@Cacheable(key = "#root.methodName + #sendCardName")
public int getSlotCount(String sendCardName) {
..方法体
return calCard.getSlotCount();
}
...
@CachePut(key = "#param")
public String updateCache(String param) {
getSlotCount("xx");
// 对数据库更新某个值
return updatedValue;
}
}
Spring Cache + Caffeine的整合与使用的更多相关文章
- spring boot spring cache ehcache3.x整合
http://blog.csdn.net/qq18998401056/article/details/53467671 **************************************** ...
- spring boot:使用spring cache+caffeine做进程内缓存(本地缓存)(spring boot 2.3.1)
一,为什么要使用caffeine做本地缓存? 1,spring boot默认集成的进程内缓存在1.x时代是guava cache 在2.x时代更新成了caffeine, 功能上差别不大,但后者在性能上 ...
- Spring Cache缓存技术的介绍
缓存用于提升系统的性能,特别适用于一些对资源需求比较高的操作.本文介绍如何基于spring boot cache技术,使用caffeine作为具体的缓存实现,对操作的结果进行缓存. demo场景 本d ...
- 【开源项目系列】如何基于 Spring Cache 实现多级缓存(同时整合本地缓存 Ehcache 和分布式缓存 Redis)
一.缓存 当系统的并发量上来了,如果我们频繁地去访问数据库,那么会使数据库的压力不断增大,在高峰时甚至可以出现数据库崩溃的现象.所以一般我们会使用缓存来解决这个数据库并发访问问题,用户访问进来,会先从 ...
- 【Spring】17、spring cache 与redis缓存整合
spring cache,基本能够满足一般应用对缓存的需求,但现实总是很复杂,当你的用户量上去或者性能跟不上,总需要进行扩展,这个时候你或许对其提供的内存缓存不满意了,因为其不支持高可用性,也不具备持 ...
- 史上最全面的Spring Boot Cache使用与整合
一:Spring缓存抽象 Spring从3.1开始定义了org.springframework.cache.Cache和org.springframework.cache.CacheManager接口 ...
- 一个缓存使用案例:Spring Cache VS Caffeine 原生 API
最近在学习本地缓存发现,在 Spring 技术栈的开发中,既可以使用 Spring Cache 的注解形式操作缓存,也可用各种缓存方案的原生 API.那么是否 Spring 官方提供的就是最合适的方案 ...
- 史上最全的Spring Boot Cache使用与整合
一:Spring缓存抽象# Spring从3.1开始定义了org.springframework.cache.Cache和org.springframework.cache.CacheManager接 ...
- Spring Boot Cache使用与整合
Spring 提供了对缓存功能的抽象:即允许绑定不同的缓存解决方案(如Caffeine.Ehcache等),但本身不直接提供缓存功能的实现.它支持注解方式使用缓存,非常方便. SpringBoot在a ...
- springboot 用redis缓存整合spring cache注解,使用Json序列化和反序列化。
springboot下用cache注解整合redis并使用json序列化反序列化. cache注解整合redis 最近发现spring的注解用起来真的是很方便.随即产生了能不能吧spring注解使用r ...
随机推荐
- AI绘画StableDiffusion实操教程:月光下的美人(含高清图片)
本教程收集于:AIGC从入门到精通教程汇总 今天继续分享AI绘画实操教程,如何用lora包生成超真实好看质感超强的人物图片. 放大高清图已放到教程包内,需要的可以自取. 出图效果: 更多图片资源访问查 ...
- 《SQL与数据库基础》13. 视图
目录 视图 简介 语法 检查选项 视图更新 视图作用 本文以 MySQL 为例 视图 简介 视图(View)是一种虚拟存在的表.视图中的数据并不在数据库中实际存在,行和列数据来自定义视图的查询中使用的 ...
- 谁家面试往死里问 Swagger 啊?
大家好,我是小富- 前言 说个挺奇葩的事,有个老铁给我发私信吐槽了一下它的面试经历,他去了个国企单位面试,然后面试官跟他就Swagger的问题聊了半个多小时.额- 面试嘛这些都不稀奇,总能遇到是千奇百 ...
- 如何调用api接口获取到商品数据
要调用API接口获取商品数据,需要进行以下步骤: 确定API接口 首先需要确定要使用的API接口,可以通过搜索引擎或者相关文档来查找适合的API接口.以淘宝开放平台为例,可以使用淘宝的商品信息查询AP ...
- Hadoop环境安装与配置
1.基础操作系统环境安装(略) 2.JDK的安装与配置 当前各大数据软件如Hadoop等,仍然停留在Java 8上,在本实验选用的是Java 8.在自己的Linux系统中,jdk可以使用如下命令进行一 ...
- 一款国产开源 Web 防火墙神器!
随着开源 Web 框架和各种建站工具的兴起,搭建网站已经是一件成本非常低的事情,但是网站的安全性很少有人关注,以至于 WAF 这个品类也鲜为人知. 一.WAF 是什么? WAF 是 Web 应用防火墙 ...
- 在阿里云上部署Solid服务器
1.Solid是什么? Solid(中文文档)是一个令人兴奋的新项目,由万维网发明者 Tim Berners-Lee 爵士在麻省理工学院启动. 该项目旨在从根本上改变 Web 应用程序的中心化趋势, ...
- springboot、jvm调优(设置运行的参数)
1.工具 jdk自带的工具位置: 找到窗口->应用程序 2.问题和方式 在SpringBoot项目中,调优主要通过配置文件和配置JVM的参数的方式进行. 2.1 springboot修改配置文件 ...
- Solution -「洛谷 P4689」「YunoOI 2016」这是我自己的发明
Description Link. 给一个树,\(n\) 个点,有点权,初始根是 1. \(m\) 个操作,种类如下: 1 x 将树根换为 \(x\). 2 x y 给出两个点 \(x,y\),从 \ ...
- 探索Lighthouse性能分数计算背后的奥秘
作为开发我们都知道,页面性能很重要,一个性能良好的页面可以给用户带来非常好的用户体验.那么,怎么能知道自己写的页面性能是好是坏呢? Lighthouse 是Chrome提供给开发者用来测量页面性能的工 ...