前言

对于一些项目里需要对数据库里的某些数据一直重复请求的,且这些数据基本是固定的,在这种情况下,可以借助简单使用本地缓存来缓存这些数据。这些介绍一下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参数的讲解

  1. "customCache": 这是缓存的名称。在应用程序中,你可以通过这个名称来获取对应的缓存实例。
  2. Caffeine.newBuilder(): 这是创建Caffeine缓存实例的起始点。newBuilder()返回一个Caffeine构建器对象,用于配置和定制缓存的各种属性。
  3. .maximumSize(100): 这是设置缓存的最大容量,即缓存可以容纳的最大条目数。在这个例子中,缓存的最大容量被设置为100。
  4. .initialCapacity(100): 这是设置缓存的初始容量,即在缓存初始化时分配的内部数据结构的初始大小。在这个例子中,初始容量被设置为100。
  5. .expireAfterWrite(10, TimeUnit.MINUTES): 这是设置缓存项在被写入后的过期时间。在这个例子中,缓存项将在被写入后的10分钟内过期。
  6. .recordStats(): 这是启用缓存统计信息的选项。启用后,你可以从缓存实例中获取有关缓存使用情况的统计信息,例如命中率、加载次数等。

使用中,对过期策略的使用会比较重要,对于过期的策略有:

  1. 写入后过期 (expireAfterWrite): 缓存项被写入后的一段时间内过期。可以通过以下方式配置:

    Caffeine.newBuilder()
    .expireAfterWrite(10, TimeUnit.MINUTES)
    .build();

    在上述示例中,缓存项将在被写入后的10分钟内过期。

  2. 访问后过期 (expireAfterAccess): 缓存项在一段时间内没有被访问后过期。可以通过以下方式配置:

    Caffeine.newBuilder()
    .expireAfterAccess(15, TimeUnit.MINUTES)
    .build();

    在上述示例中,缓存项将在最后一次访问后的15分钟内过期。

  3. 定时过期 (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 注解为例的主要参数介绍和使用方式:

  1. value(或 cacheNames): 指定缓存的名称,可以指定一个或多个缓存。如果指定多个缓存,Spring会依次检查缓存,直到找到第一个有数据的缓存或全部检查完毕。示例:

    @Cacheable(value = "myCache")
    public String getCachedData() {
    // 方法体
    }
  2. key 指定缓存项的键。默认情况下,Spring会使用方法的参数作为键,但你也可以通过 key 属性指定自定义的缓存键。示例:

    @Cacheable(value = "myCache", key = "#param")
    public String getCachedData(String param) {
    // 方法体
    }
  3. condition 指定条件表达式,只有当条件满足时才会缓存。示例:

    @Cacheable(value = "myCache", condition = "#result != null")
    public String getCachedData() {
    // 方法体
    }
  4. unless 指定一个条件表达式,当条件为 true 时,不会将结果放入缓存。示例:

    @Cacheable(value = "myCache", unless = "#result == null")
    public String getCachedData() {
    // 方法体
    }
  5. keyGenerator 指定自定义的缓存键生成器。这个属性允许你提供一个实现了 org.springframework.cache.interceptor.KeyGenerator 接口的类,用于生成缓存键。示例:

    @Cacheable(value = "myCache", keyGenerator = "customKeyGenerator")
    public String getCachedData() {
    // 方法体
    }
  6. sync 是否启用同步模式。如果设置为 true,可以解决并发查的问题,Spring会在调用方法时锁定缓存,防止多个线程同时访问数据库。默认为 false。示例:

    @Cacheable(value = "myCache", sync = true)
    public String getCachedData() {
    // 方法体
    }

这些是 @Cacheable 注解的一些常用参数。可以根据实际需要选择合适的参数来定义缓存规则。

在Spring中,除了 @Cacheable,另外一些注解及其简要介绍:

  1. @CacheEvict 用于从缓存中移除数据。通常用于在方法执行后清空指定缓存。示例:

    @CacheEvict(value = "myCache", key = "#param")
    public void evictCache(String param) {
    // 方法体
    }
  2. @CachePut 用于将方法的返回值更新到缓存中,常用于更新缓存而不影响方法的执行。示例:

    @CachePut(value = "myCache", key = "#param")
    public String updateCache(String param) {
    // 方法体
    return updatedValue;
    }
  3. @Caching 用于将多个缓存相关的注解组合在一起,实现复杂的缓存操作。示例:

    @Caching(
    evict = {@CacheEvict(value = "cache1", key = "#param1")},
    put = {@CachePut(value = "cache2", key = "#param2")}
    )
    public String complexCacheOperation(String param1, String param2) {
    // 方法体
    }
  4. @CacheConfig 用于在类级别配置缓存的一些公共属性,避免在每个方法上都重复指定相同的缓存名称等信息。示例:

    @CacheConfig(cacheNames = "commonCache")
    public class MyService {
    @Cacheable
    public String getCachedData(String param) {
    // 方法体
    }
    }

这些注解可以单独使用,也可以结合使用,以满足不同的缓存需求。

清空缓存的方法

清空所有缓存,可以不指定 valuekey,如下所示:

@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的整合与使用的更多相关文章

  1. spring boot spring cache ehcache3.x整合

    http://blog.csdn.net/qq18998401056/article/details/53467671 **************************************** ...

  2. spring boot:使用spring cache+caffeine做进程内缓存(本地缓存)(spring boot 2.3.1)

    一,为什么要使用caffeine做本地缓存? 1,spring boot默认集成的进程内缓存在1.x时代是guava cache 在2.x时代更新成了caffeine, 功能上差别不大,但后者在性能上 ...

  3. Spring Cache缓存技术的介绍

    缓存用于提升系统的性能,特别适用于一些对资源需求比较高的操作.本文介绍如何基于spring boot cache技术,使用caffeine作为具体的缓存实现,对操作的结果进行缓存. demo场景 本d ...

  4. 【开源项目系列】如何基于 Spring Cache 实现多级缓存(同时整合本地缓存 Ehcache 和分布式缓存 Redis)

    一.缓存 当系统的并发量上来了,如果我们频繁地去访问数据库,那么会使数据库的压力不断增大,在高峰时甚至可以出现数据库崩溃的现象.所以一般我们会使用缓存来解决这个数据库并发访问问题,用户访问进来,会先从 ...

  5. 【Spring】17、spring cache 与redis缓存整合

    spring cache,基本能够满足一般应用对缓存的需求,但现实总是很复杂,当你的用户量上去或者性能跟不上,总需要进行扩展,这个时候你或许对其提供的内存缓存不满意了,因为其不支持高可用性,也不具备持 ...

  6. 史上最全面的Spring Boot Cache使用与整合

    一:Spring缓存抽象 Spring从3.1开始定义了org.springframework.cache.Cache和org.springframework.cache.CacheManager接口 ...

  7. 一个缓存使用案例:Spring Cache VS Caffeine 原生 API

    最近在学习本地缓存发现,在 Spring 技术栈的开发中,既可以使用 Spring Cache 的注解形式操作缓存,也可用各种缓存方案的原生 API.那么是否 Spring 官方提供的就是最合适的方案 ...

  8. 史上最全的Spring Boot Cache使用与整合

    一:Spring缓存抽象# Spring从3.1开始定义了org.springframework.cache.Cache和org.springframework.cache.CacheManager接 ...

  9. Spring Boot Cache使用与整合

    Spring 提供了对缓存功能的抽象:即允许绑定不同的缓存解决方案(如Caffeine.Ehcache等),但本身不直接提供缓存功能的实现.它支持注解方式使用缓存,非常方便. SpringBoot在a ...

  10. springboot 用redis缓存整合spring cache注解,使用Json序列化和反序列化。

    springboot下用cache注解整合redis并使用json序列化反序列化. cache注解整合redis 最近发现spring的注解用起来真的是很方便.随即产生了能不能吧spring注解使用r ...

随机推荐

  1. 【LaTeX】环境配置以及中文支持

    目录 网页环境 Overleaf 本地环境 TeX Live TeXstudio VSCode 安装 LaTeX Workshop 扩展 编译链配置 正向同步 反向同步 其他可选配置 中文支持 XeL ...

  2. Linux 内核音频数据传递主要流程 (下)

    来而不往非礼也.前面看到了用户空间应用程序和 DMA buffer 之间交换数据,并更新 runtime->control->appl_ptr 指针的过程,这里看一下硬件设备驱动程序在完成 ...

  3. 《最新出炉》系列初窥篇-Python+Playwright自动化测试-14-playwright操作iframe-番外篇

    1.简介 通过前边三篇的学习,想必大家已经对iframe有了一定的认识和了解,今天这一篇主要是对iframe的一些特殊情况的介绍和讲解,主要从iframe的定位.监听事件和执行js脚本三个方面进行展开 ...

  4. 我的新书《Flink大数据分析实战》出版啦

  5. 分享一个 SpringBoot + Redis 实现「查找附近的人」的小技巧

    前言 SpringDataRedis提供了十分简单的地理位置定位的功能,今天我就用一小段代码告诉大家如何实现. 正文 1.引入依赖 <dependency> <groupId> ...

  6. Go语言中匿名嵌套和类型嵌套的区别

    在Go语言中,匿名嵌套结构体和与类型同名的嵌套结构体不是完全等价的,它们有一些重要的区别.以下是它们之间的主要区别: 字段访问: 匿名嵌套结构体:当你使用匿名嵌套结构体时,内部结构体的字段可以被直接访 ...

  7. 2-MySQL基本语法

    上文说到,数据库的安装和连接,接下来将给大家讲解MySQL数据库的基本语法及数据的类型 1.基本语法 (1).查看当前所有数据库 : show databases; (2).创建数据库 create ...

  8. 【python】python开源代理ip池

    一.前言 随着互联网的不断发展,越来越多的应用需要使用高匿代理IP才能访问目标网站,而代理IP作为一种能够隐藏本机真实IP地址的工具,在网络抓取.搜索引擎排名.广告投放.反爬虫等方面有着广泛的应用场景 ...

  9. Oracle存储函数写法及调用

    1.右键导航创建函数界面 2.返回值FunctionResult可自定义,当函数有输出函数时可不传数据,但需要设置返回(当提示未限定返回长度时,如代码示例设置255长度皆可): -------使用函数 ...

  10. Jmeter-变量的嵌套使用

    场景: 有存在获取到多个登录账号,循环获取单个变量的情况. 常用方法: ${__BeanShell(vars.get("变量字段_${变量字段}"))} 取值示例: 思维扩展: 一 ...