Spring Boot默认缓存管理

Spring框架支持透明地向应用程序添加缓存,以及对缓存进行管理,其管理缓存的核心是将缓存应用于操作数据的方法,从而减少操作数据的执行次数,同时不会对程序本身造成任何干扰。Spring Boot继承了Spring框架的缓存管理功能,下面将对Spring Boot内置的缓存方案进行讲解。

Spring的缓存机制将提供的缓存作用于Java 方法上,基于缓存中的可用信息,可以减少方法的执行次数。每次目标方法调用时,抽象使用缓存行为来检查执行方法,即检查执行方法是否给定了缓存的执行参数,如果是,则返回缓存结果,不执行具体方法;如果否,则执行方法,并将结果缓存后,返回给用户。

Spring的默认的缓存方案通过org.springframework.cache.Cache和org.springframework.cache.CacheManager接口来统一不同的缓存技术。

Cache接口:缓存的组件定义规范,包含缓存的各种操作集合。Spring中为Cache接口提供了各种缓存的实现:RedisCache,EhCache,ConcurrentMapCache等

CacheManager接口:缓存管理器,基于缓存名称对缓存进行管理,并制定了管理Cache的规则。 

在项目中添加某个缓存管理组件(如Redis)后,Spring Boot项目会选择并启用对应的缓存管理器。如果项目中同时添加了多个缓存组件,且没有定义类型为CacheManager的Bean组件或者名为cacheResolver的缓存解析器,Spring Boot将尝试按以下列表的顺序查找有效的缓存组件进行缓存管理。 (1)Generic (2)JCache (EhCache 3、Hazelcast、Infinispan等) (3)EhCache 2.x (4)Hazelcast (5)Infinispan (6)Couchbase (7)Redis (8)Caffeine (9)Simple

声明式缓存注解

要想使用Spring提供的默认缓存,需要对缓存进行声明,也就是标志缓存的方法及缓存策略。对于缓存声明,Spring提供了一系列的注解,使用这些注解可以实现Spring 默认的基于注解的缓存管理。

1.@EnableCaching注解

@EnableCaching是Spring框架提供的用于开启基于注解的缓存支持的注解,当配置类上使用@EnableCaching注解,会默认提供CacheManager的实现,并通过AOP将缓存行为添加到应用程序。执行操作时,会检查是否已经存在注解对应的缓存。如果找到了,就会自动创建一个代理拦截方法调用,使用缓存的Bean执行处理。

2.@Cacheable注解

@Cacheable注解用于标注可缓存的方法,通常标注的方法为数据查询方法。标注@Cacheable注解的方法在执行时,会先查询缓存,如果查询到的缓存为空,则执行该方法,并将方法的执行结果添加到缓存;如果查询到缓存数据,则不执行该方法,而是直接使用缓存数据。

@Cacheable注解提供了多个属性,用于对缓存进行相关配置。

属性名

说明

value/cacheNames

指定缓存的名称,必备属性,这两个属性二选一使用

key

指定缓存数据的key,默认使用方法参数值,可以使用SpEL表达式

keyGenerator

指定缓存数据的key的生成器,与key属性二选一使用

cacheManager

指定缓存管理器

cacheResolver

指定缓存解析器,与cacheManager属性二选一使用

condition

指定在符合某条件下进行数据缓存

unless

指定在符合某条件下不进行数据缓存

sync

指定是否使用异步缓存,默认为false

(1)value/cacheNames属性

value和cacheNames属性作用相同,用于指定缓存的名称,方法的返回结果会存放在指定名称的缓存中。这两个属于必备选项,且要二选一使用。如果@Cacheable注解只配置value或者cacheNames属性,那么属性名可以省略。

@Cacheable("book")
public Book findById(Integer id){return bookDao.findById(id).get();
}

@Cacheable注解中可以指定多个缓存的名称,以便使用多个缓存。

@Cacheable({"book","hotBook"})
public Book findById(Integer id){return bookDao.findById(id).get();
}

(2)key属性

缓存的本质是键值对存储,key用于指定唯一的标识,value用于指定缓存的数据,所以每次调用缓存方法都会转换为访问缓存的键。缓存的键通过key属性进行指定,进行数据缓存时,如果没有指定key属性,Spring Boot默认配置类SimpleKeyGenerator中的generateKey(Object... params)方法会根据方法参数生成key值。对于没有参数的方法,其key是默认创建的空参SimpleKey[]对象;对于只有一个参数的方法,其key默认是参数值;对于有多个参数的方法,其key是包含所有参数的SimpleKey对象。 

如果方法有多个参数,但是部分参数对缓存没有任何用处,通常会选择手动指定key属性的值,key属性的值可以通过SpEL表达式选择所需要的参数。

@Cacheable(cacheNames="book", key="#id")
public Book findBookById(Integer id, boolean includeUsed){return bookDao.findById(id).get();
}

Cache缓存支持的SpEL表达式及说明

参数名

位置

描述

示例

methodName

root对象

当前被调用的方法名

#root.methodName

method

root对象

当前被调用的方法

#root.method.name

target

root对象

当前被调用的目标对象实例

#root.target

targetClass

root对象

当前被调用的目标对象的类

#root.targetClass

args

root对象

当前被调用的方法的参数列表

#root.args[0]

caches

root对象

当前被调用的方法的缓存列表

#root.caches[0].name

argumentName

执行上下文

当前被调用的方法参数,可以用#参数名或者#a0、#p0的形式(0代表参数索引,从0开始)

#comment_id、#a0、#p0

result

执行上下文

当前方法执行后的返回结果

#result

(3)keyGenerator属性

keyGenerator属性与key属性本质作用相同,都是用于指定缓存数据的key,只不过keyGenerator属性指定的不是具体的key值,而是key值的生成器规则,由其中指定的生成器生成具体的key。使用时,keyGenerator属性与key属性要二者选一。关于自定义key值生成器的定义,可以参考Spring Boot默认配置类SimpleKeyGenerator的定义方式,这里不再做具体说明。

(4)cacheManager/cacheResolver属性

cacheManager和cacheResolver属性分别用于指定缓存管理器和缓存解析器,这两个属性也是二选一使用,默认情况不需要配置,对于需要使用多个缓存管理器(如Redis、Ehcache等)的应用,可以为每个操作设置一个缓存管理器或缓存解析器。 

(5)condition属性

condition属性用于对数据进行有条件的选择性存储,只有当指定条件为true时才会对查询结果进行缓存,可以使用SpEL表达式指定属性值。 

@Cacheable(cacheNames="book", condition="#id > 1")
public Book findBook(Integer id){return bookDao.findById(id).get();
}

(6)unless属性

unless属性的作用与condition属性相反,当指定的条件为true时,方法的返回值不会被缓存,也可以使用SpEL表达式指定。 

@Cacheable(cacheNames="book", unless = "#result==null")
public Book findBook(Integer id){return bookDao.findById(id).get();
}

(7)sync属性

在多线程程序中,某些操作可能会同时引用相同的参数,导致相同的对象被计算好几次,从而达不到缓存的目的。对于这种情况,可以使用sync属性,sync属性表示数据缓存过程中是否使用同步模式,默认值为false,通常不会使用该属性。 

3.@CachePut注解

@CachePut注解的作用是更新缓存数据,当需要更新缓存且不影响方法执行时,可以使用@CachePut注解,通常用在数据更新方法上。@CachePut注解的执行顺序是,先进行方法调用,然后将方法结果更新到缓存中。

@CachePut注解也提供了多个属性,这些属性与@Cacheable注解的属性完全相同。通常不建议在同一个方法同时使用@CachePut和@Cacheable注解,这两个注解关注不同的行为,@CachePut注解会强制执行方法并进行缓存更新,使用@Cacheable 注解时,如果请求能够在缓存中获取到对应的数据,就不会执行当前被@Cacheable 注解标注的方法。 

4.@CacheEvict注解

@CacheEvict注解的作用删除缓存中的数据,通常标注在数据删除方法上。@CacheEvict注解的默认执行顺序是先进行方法调用,然后将缓存清除。 @CacheEvict注解也提供了多个属性,这些属性与@Cacheable注解的属性基本相同,除此之外,还额外提供了两个特殊属性allEntries和beforeInvocation,下面对这两个属性分别进行讲解。

(1)allEntries属性 allEntries属性表示是否清除指定缓存空间中的所有缓存数据,默认值为false,即默认只删除指定key对应的缓存数据。

@CacheEvict(cacheNames = "book",allEntries = true)
public void delById(Integer id){bookDao.deleteById(id);
}

(2)beforeInvocation属性 beforeInvocation属性表示是否在方法执行之前进行缓存清除,默认值为false,即默认在执行方法后再进行缓存清除。

@CacheEvict(cacheNames = "book",beforeInvocation = true)
public void delById(Integer id){bookDao.deleteById(id);
}

5.@Caching注解

如果不同缓存之间的条件或者键表达式不同,就需要指定相同类型的多个注解,例如需要同时指定多个@CacheEvict或@CachePut,这个时候可以使用@Caching注解。@Caching注解用于针对复杂规则的数据缓存管理,@Caching注解中允许使用多个嵌套的 @Cacheable 、@CachePut 或 @CacheEvict。在@Caching注解内部包含有Cacheable、put和evict三个属性,分别对应于@Cacheable、@CachePut和@CacheEvict三个注解。

@Caching(evict = { @CacheEvict("primary"), 
@CacheEvict(cacheNames="secondary", key="#date")})
public void delById(Integer id, Date date){bookDao.deleteById(id);
}
@Caching(cacheable={@Cacheable(cacheNames ="comment",key = "#id")},put = {@CachePut(cacheNames = "comment",key = "#result.author")})public Comment getComment(int comment_id){return commentRepository.findById(comment_id).get();}

 6.@CacheConfig注解

@CacheConfig注解使用在类上,主要用于统筹管理类中所有使用@Cacheable、@CachePut和@CacheEvict注解标注方法中的公共属性。 

@CacheConfig(cacheNames = "book")
@Service
public class BookService {@Autowiredprivate BookRepository bookRepository;@Cacheablepublic Book findById(Integer id){return  bookRepository.findById(id).get();}
}

声明式缓存注解的应用

1.创建项目

2.配置依赖

<!-- springboot默认应该有,不配应该没关系-->
<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>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-data-jpa
</artifactId>
</dependency>
<dependency>
<groupId>
mysql
</groupId>
<artifactId>
mysql-connector-java
</artifactId>
</dependency>

3.设置配置信息

spring:datasource:url:"jdbc:mysql://localhost:3306/springbootdata?characterEncoding=utf-8&serverTimezone=Asia/Shanghai"username:rootpassword:rootjpa:show-sql:true

4.创建实体类

@Entity
@Table(name="book")
public  class  Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private  Integer id; //图书编号
@Column(name="name")
private  String name; //图书名称
private  String author; //图书作者
private  String press; //图书出版社
private  String status; //图书状态public  Integer getId() {
return  id;
}public  void  setId(Integer id) {
this .id = id;
}public  String getName() {
return  name;
}public  void  setName(String name) {
this .name = name;
}public  String getPress() {
return  press;
}public  void  setPress(String press) {
this .press = press;
}public  String getAuthor() {
return  author;
}public  void  setAuthor(String author) {
this .author = author;
}public  String getStatus() {
return  status;
}public  void  setStatus(String status) {
this .status = status;
}public  Book() {
}public  Book(Integer id, String name, String author, String press, String status) {
this .id = id;
this .name = name;
this .author = author;
this .press = press;
this .status = status;
}@Override
public  String toString() {
return  "Book{" +
"id=" + id +
", name='" + name + ''' +
", author='" + author + ''' +
", press='" + press + ''' +
", status='" + status + ''' +
'
}';
}
}

5.创建Repository接口

@Repository
public  interface  BookRepository extends  JpaRepository<Book,Integer> {
}

 6.创建Service接口和实现类

public  interface  BookService {
public  Book findById(Integer id);
public  Book updateById(Integer id,String name);
public  void  delById(Integer id);
}
@Service
@CacheConfig(cacheNames = "book")
@Transactional
public  class  BookServiceImpl implements  BookService{
@Autowired
private  BookRepository bookRepository;@Cacheable(key = "#id")
public  Book findById(Integer id){
//根据id查找图书信息
return  bookRepository.findById(id).get();
}
@CachePut(key = "#id")
public  Book updateById(Integer id,String name){
Book book=this .findById(id);
book.setName(name);
//更新图书信息
return  bookRepository.save(book);
}
@CacheEvict(key = "#id")
public  void  delById(Integer id){
//根据id删除图书信息
bookRepository.deleteById(id);
}
}

 7.创建控制器类

@RestController
@RequestMapping("book")
public  class  BookController {
@Autowired
private  BookService bookService;@RequestMapping("/findById/{id}")
public  Book findById(@PathVariable Integer id){
//根据id查询图书信息
return  bookService.findById(id);
}
@RequestMapping("/editById/{id}/{name}")
public  Book editById(@PathVariable Integer id,@PathVariable String name){
//根据id修改图书的名称
return  bookService.updateById(id,name);
}
@RequestMapping("/delById/{id}")
public  void  delById(@PathVariable Integer id){
//根据id删除图书信息
bookService.delById(id);
}
}

8.在启动类上开启缓存

@SpringBootApplication
@EnableCaching
public  class  Chapter06Application {
public  static  void  main(String[] args) {
SpringApplication.run(Chapter06Application.class , args);
}
}

9.测试缓存效果

启动项目,在浏览器中访问http://localhost:8080/book/findById/3,查询图书信息,控制台输出信息。

查询图书信息后,浏览器中查询到图书信息。

再次在浏览器中访问http://localhost:8080/book/findById/3,查询id为3的图书信息,控制台输出信息。

在浏览器中访问http://localhost:8080/book/editById/3/西游释厄传,将id为3的图书名称更新为“西游释厄传”,此时控制台输出信息。

更新图书信息后,浏览器中查询图书信息。

浏览器中访问http://localhost:8080/book/delById/3,删除id为3的图书信息,控制台输出信息。

在浏览器中再次访问http://localhost:8080/book/findById/3,查询id为3的图书信息,控制台输出信息。

动手试一试 Spring Boot默认缓存管理 

测试发生一万次请求需要的时间(jdk11以上)

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.IntStream;public class HttpRequestBenchmark {public static void main(String[] args) throws Exception {String url = "http://localhost:8081/get/1"; // 替换为你要请求的URLint numRequests = 10000; // 请求次数HttpClient client = HttpClient.newHttpClient();HttpRequest request = HttpRequest.newBuilder().uri(new URI(url)).build();long startTime = System.nanoTime();// 使用CompletableFuture来异步发送请求,并等待所有请求完成CompletableFuture<Void>[] futures = IntStream.range(0, numRequests).mapToObj(i -> CompletableFuture.runAsync(() -> {try {HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());// 在这里可以处理响应,例如检查状态码等System.out.println("xiangying------------------");// 这里只是简单发送请求并忽略响应内容} catch (Exception e) {e.printStackTrace();}})).toArray(CompletableFuture[]::new);// 等待所有请求完成CompletableFuture.allOf(futures).join();long endTime = System.nanoTime();Duration duration = Duration.ofNanos(endTime - startTime);System.out.println("发送" + numRequests + "次HTTP请求需要的时间:" + duration.toMillis() + "毫秒");}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/78060.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数模学习:一,层次分析法

基本定位&#xff1a; 适用于解决评价&#xff0c;选择类问题&#xff08;数值不确定&#xff0c;需要自己结合资料数据等自己填写&#xff09;。 引入&#xff1a; 若要解决选择类的问题&#xff0c;打分的方式最为常用——即采用权重表&#xff1a; 指标权重选择1选择2..…

模板偏特化 (Partial Specialization)

C 模板偏特化 (Partial Specialization) 模板偏特化允许为模板的部分参数或特定类型模式提供定制实现&#xff0c;是 静态多态&#xff08;Static Polymorphism&#xff09; 的核心机制之一。以下通过代码示例和底层原理&#xff0c;全面解析模板偏特化的实现规则、匹配优先级…

sql 根据时间范围获取每日,每月,年月的模版数据

1&#xff1a;获取每日模版数据&#xff08;参数也支持跨年&#xff09; SELECT a.selected_date cdate FROM(SELECT adddate(1970-01-01,t4.i * 10000 t3.i * 1000 t2.i * 100 t1.i * 10 t0.i) selected_dateFROM( SELECT 0 i UNION SELECT 1 UNION SELECT 2 UNION SELEC…

windows上的RagFlow+ollama知识库本地部署

一、 docker的安装与部署 1. 下载Docker Desktop 访问Docker官网并下载适用于Windows的Docker Desktop安装程序。 RagFlow对docker的要求: Docker ≥ 24.0.0 & Docker Compose ≥ v2.26. docker 下载地址: https://www.docker.com/ Get Docker | Docker Docs 如下图所…

多模态大语言模型arxiv论文略读(三十四)

SHIELD : An Evaluation Benchmark for Face Spoofing and Forgery Detection with Multimodal Large Language Models ➡️ 论文标题&#xff1a;SHIELD : An Evaluation Benchmark for Face Spoofing and Forgery Detection with Multimodal Large Language Models ➡️ 论文…

Unity InputSystem触摸屏问题

最近把Unity打包后的windows软件放到windows触摸屏一体机上测试&#xff0c;发现部分屏幕触摸点击不了按钮&#xff0c;测试了其他应用程序都正常。 这个一体机是这样的&#xff0c;一个电脑机箱&#xff0c;外接一个可以触摸的显示屏&#xff0c;然后UGUI的按钮就间歇性点不了…

AI打开潘多拉魔盒?当深度伪造成为虚假信息的核动力引擎

引言&#xff1a;虚假信息——数字时代的“隐形武器” 在人工智能&#xff08;AI&#xff09;与社交媒体深度融合的今天&#xff0c;虚假信息&#xff08;Disinformation&#xff09;已成为全球社会面临的最严峻挑战之一。 source: Gartner.(2024). 2025 Top Strategic Techno…

MySQL的图形管理工具-MySQL Workbench的下载安装及使用【保姆级】

MySQL的图形管理工具-MySQL Workbench的下载安装及使用 下载安装使用Workbench 创建数据库Workbench 创建数据表数据表中的增删改增加数据 删除数据修改数据 下载 MySQL的图形管理工具有很多&#xff0c;常用的有MySQL Workbench、phpMyAdmin和Navicat等软件。我选择了MySQL W…

Spring Security认证流程

认证是Spring Security的核心功能之一&#xff0c;Spring Security所提供的认证可以更好地保护系统的隐私数据与资源&#xff0c;只有当用户的身份合法后方可访问该系统的资源。Spring Security提供了默认的认证相关配置&#xff0c;开发者也可以根据自己实际的环境进行自定义身…

程序员鱼皮最新项目-----AI超级智能体教程(一)

文章目录 1.前言1.什么是AI大模型2.什么是多模态3.阿里云百炼平台介绍3.1文本调试展示3.2阿里云和dashscope的关系3.3平台智能体应用3.4工作流的创建3.5智能体编排应用 1.前言 最近鱼皮大佬出了一套关于这个AI 的教程&#xff0c;关注鱼皮大佬很久了&#xff0c;鱼皮大佬确实在…

《Pinia 从入门到精通》Vue 3 官方状态管理 -- 进阶使用篇

《Pinia 从入门到精通》Vue 3 官方状态管理 – 基础入门篇 《Pinia 从入门到精通》Vue 3 官方状态管理 – 进阶使用篇 《Pinia 从入门到精通》Vue 3 官方状态管理 – 插件扩展篇 目录 Store 的模块化设计4.1 多模块结构设计✅ 推荐目录结构&#xff08;中大型项目&#xff09; …

西甲001:奥萨苏纳VS塞维利亚

西甲001&#xff1a;奥萨苏纳VS塞维利亚 奥萨苏纳主场强势力擒塞维利亚 奥萨苏纳中场核心蒙卡约纳上轮联赛早段伤退&#xff0c;本轮将由巴勃罗-伊瓦涅斯顶替首发。当家射手布迪米尔状态爆棚&#xff0c;近两轮斩获3球&#xff0c;本赛季联赛已轰入18球创生涯新高&#xff0c;将…

C语言编程--15.四数之和

题目&#xff1a; 给你一个由 n 个整数组成的数组 nums &#xff0c;和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] &#xff08;若两个四元组元素一一对应&#xff0c;则认为两个四元组重复&#xff09;&…

2025.04.23【探索工具】| STEMNET:高效数据排序与可视化的新利器

文章目录 1. STEMNET工具简介2. STEMNET的安装方法3. STEMNET常用命令 1. STEMNET工具简介 在生物信息学领域&#xff0c;分析和处理大规模数据集是研究者们面临的日常挑战。STEMNET工具应运而生&#xff0c;旨在提供一个强大的平台&#xff0c;用于探索和分析单细胞RNA测序&a…

Day-3 应急响应实战

应急响应实战一&#xff1a;Web入侵与数据泄露分析 1. Web入侵核心原理 ​​漏洞利用路径​​ 未授权访问&#xff1a;弱口令&#xff08;如空密码/默认口令&#xff09;、目录遍历漏洞代码注入攻击&#xff1a;JSP/ASP木马、PHP一句话木马&#xff08;利用eval($_POST[cmd])&…

两段文本比对,高亮出差异部分

用法一:computed <div class"card" v-if"showFlag"><div class"info">*红色背景为已删除内容&#xff0c;绿色背景为新增内容</div><el-form-item label"与上季度比对&#xff1a;"><div class"comp…

Python中的 for 与 迭代器

文章目录 一、for 循环的底层机制示例&#xff1a;手动模拟 for 循环 二、可迭代对象 vs 迭代器关键区别&#xff1a; 三、for 循环的典型应用场景1. 遍历序列类型2. 遍历字典3. 结合 range() 生成数字序列4. 遍历文件内容 四、迭代器的自定义实现示例&#xff1a;生成斐波那契…

Pytest教程:为什么Pytest要用插件模式?

目录 一、历史背景:测试框架的局限性与Pytest的设计哲学 1.1 早期测试框架的困境 1.2 Pytest的模块化设计 二、横向对比:插件机制如何让Pytest脱颖而出 2.1 与Unittest/Nose的对比 2.2 插件模式的架构优势 三、插件模式的核心优势解析 3.1 可扩展性:从单元测试到全链…

【深度】如何通过MCP实现多智能体之间的协同

来源&#xff1a;腾讯技术工程、infoQ、原力注入 自 OpenAI 于 2023 年发布函数调用功能以来&#xff0c;我一直在思考如何构建一个开放的智能体与工具使用生态系统。随着基础模型愈发智能化&#xff0c;智能体与外部工具、数据和 API 的交互能力却日益碎片化&#xff1a;开发…

NVIDIA自动驾驶安全与技术读后感

ll在阅读了 NVIDIA 自动驾驶安全报告后&#xff0c;我对该公司致力于推进自动驾驶汽车&#xff08;AV&#xff09;技术、同时优先考虑安全和标准化的承诺印象深刻。它揭示了 NVIDIA 在功能安全、法规合规性以及与全球标准组织合作方面的严谨态度。    报告中最引人注目的部分…