​@Cacheable 注解​

news/2024/4/15 23:01:27

1. 功能说明

  @Cacheable 注解在方法上,表示该方法的返回结果是可以缓存的。也就是说,该方法的返回结果会放在缓存中,以便于以后使用相同的参数调用该方法时,会返回缓存中的值,而不会实际执行该方法。

  注意,这里强调了一点:参数相同。这一点应该是很容易理解的,因为缓存不关心方法的执行逻辑,它能确定的是:对于同一个方法,如果参数相同,那么返回结果也是相同的。但是如果参数不同,缓存只能假设结果是不同的,所以对于同一个方法,你的程序运行过程中,使用了多少种参数组合调用过该方法,理论上就会生成多少个缓存的 key(当然,这些组合的参数指的是与生成 key 相关的)。下面来了解一下 @Cacheable 的一些参数:

2. cacheNames & value

  @Cacheable 提供两个参数来指定缓存名:value、cacheNames,二者选其一即可。这是 @Cacheable 最简单的用法示例:

@Override@Cacheable("menu")public Menu findById(String id) {Menu menu = this.getById(id);if (menu != null){System.out.println("menu.name = " + menu.getName());}return menu;}

  在这个例子中,findById 方法与一个名为 menu 的缓存关联起来了。调用该方法时,会检查 menu 缓存,如果缓存中有结果,就不会去执行方法了。

3. 关联多个缓存名

  其实,按照官方文档,@Cacheable 支持同一个方法关联多个缓存。这种情况下,当执行方法之前,这些关联的每一个缓存都会被检查,而且只要至少其中一个缓存命中了,那么这个缓存中的值就会被返回。示例:

@Override@Cacheable({"menu", "menuById"})public Menu findById(String id) {Menu menu = this.getById(id);if (menu != null){System.out.println("menu.name = " + menu.getName());}return menu;}---------@GetMapping("/findById/{id}")public Menu findById(@PathVariable("id")String id){Menu menu0 = menuService.findById("fe278df654adf23cf6687f64d1549c0a");Menu menu2 = menuService.findById("fb6106721f289ebf0969565fa8361c75");return menu0;}

4. key & keyGenerator

  一个缓存名对应一个被注解的方法,但是一个方法可能传入不同的参数,那么结果也就会不同,这应该如何区分呢?这就需要用到 key 。在 spring 中,key 的生成有两种方式:显式指定和使用 keyGenerator 自动生成。

  4.1. KeyGenerator 自动生成

    当我们在声明 @Cacheable 时不指定 key 参数,则该缓存名下的所有 key 会使用 KeyGenerator 根据参数 自动生成。spring 有一个默认的 SimpleKeyGenerator ,在 spring boot 自动化配置中,这个会被默认注入。生成规则如下:

      a. 如果该缓存方法没有参数,返回 SimpleKey.EMPTY ;

      b. 如果该缓存方法有一个参数,返回该参数的实例 ;

      c. 如果该缓存方法有多个参数,返回一个包含所有参数的 SimpleKey ;

    默认的 key 生成器要求参数具有有效的 hashCode() 和 equals() 方法实现。另外,keyGenerator 也支持自定义, 并通过 keyGenerator 来指定。关于 KeyGenerator 这里不做详细介绍,有兴趣的话可以去看看源码,其实就是使用 hashCode 进行加乘运算。跟 String 和 ArrayList 的 hash 计算类似。

  4.2. 显式指定 key

    相较于使用 KeyGenerator 生成,spring 官方更推荐显式指定 key 的方式,即指定 @Cacheable 的 key 参数。

    即便是显式指定,但是 key 的值还是需要根据参数的不同来生成,那么如何实现动态拼接呢?SpEL(Spring Expression Language,Spring 表达式语言) 能做到这一点。下面是一些使用 SpEL 生成 key 的例子。

@Override@Cacheable(value = {"menuById"}, key = "#id")public Menu findById(String id) {Menu menu = this.getById(id);if (menu != null){System.out.println("menu.name = " + menu.getName());}return menu;}@Override@Cacheable(value = {"menuById"}, key = "'id-' + #menu.id")public Menu findById(Menu menu) {return menu;}@Override@Cacheable(value = {"menuById"}, key = "'hash' + #menu.hashCode()")public Menu findByHash(Menu menu) {return menu;}

注意:官方说 key 和 keyGenerator 参数是互斥的,同时指定两个会导致异常

5. cacheManager & cacheResolver

  CacheManager,缓存管理器是用来管理(检索)一类缓存的。通常来讲,缓存管理器是与缓存组件类型相关联的。我们知道,spring 缓存抽象的目的是为使用不同缓存组件类型提供统一的访问接口,以向开发者屏蔽各种缓存组件的差异性。那么  CacheManager 就是承担了这种屏蔽的功能。spring 为其支持的每一种缓存的组件类型提供了一个默认的 manager,如:RedisCacheManager 管理 redis 相关的缓存的检索、EhCacheManager 管理 ehCache 相关的缓等。

  CacheResolver,缓存解析器是用来管理缓存管理器的,CacheResolver 保持一个 cacheManager 的引用,并通过它来检索缓存。CacheResolver 与 CacheManager 的关系有点类似于 KeyGenerator 跟 key。spring 默认提供了一个 SimpleCacheResolver,开发者可以自定义并通过 @Bean 来注入自定义的解析器,以实现更灵活的检索。

  大多数情况下,我们的系统只会配置一种缓存,所以我们并不需要显式指定 cacheManager 或者 cacheResolver。但是 spring 允许我们的系统同时配置多种缓存组件,这种情况下,我们需要指定。指定的方式是使用 @Cacheable 的 cacheManager 或者 cacheResolver 参数。

  注意:按照官方文档,cacheManager 和 cacheResolver 是互斥参数,同时指定两个可能会导致异常。

6. sync

  是否同步,true/false。在一个多线程的环境中,某些操作可能被相同的参数并发地调用,这样同一个 value 值可能被多次计算(或多次访问 db),这样就达不到缓存的目的。针对这些可能高并发的操作,我们可以使用 sync 参数来告诉底层的缓存提供者将缓存的入口锁住,这样就只能有一个线程计算操作的结果值,而其它线程需要等待,这样就避免了 n-1 次数据库访问。

  sync = true 可以有效的避免缓存击穿的问题。

7. condition

  调用前判断,缓存的条件。有时候,我们可能并不想对一个方法的所有调用情况进行缓存,我们可能想要根据调用方法时候的某些参数值,来确定是否需要将结果进行缓存或者从缓存中取结果。比如当我根据年龄查询用户的时候,我只想要缓存年龄大于 35 的查询结果。那么 condition 能实现这种效果。

  condition 接收一个结果为 true 或 false 的表达式,表达式同样支持 SpEL 。如果表达式结果为 true,则调用方法时会执行正常的缓存逻辑(查缓存-有就返回-没有就执行方法-方法返回不空就添加缓存);否则,调用方法时就好像该方法没有声明缓存一样(即无论传入了什么参数或者缓存中有些什么值,都会执行方法,并且结果不放入缓存)

8. unless

  执行后判断,不缓存的条件。unless 接收一个结果为 true 或 false 的表达式,表达式支持 SpEL。当结果为 true 时,不缓存

可以看到,两次调用的结果都没有缓存。说明在这种情况下,condition 比 unless 的优先级高。总结起来就是:

    condition 不指定相当于 true,unless 不指定相当于 false

    当 condition = false,一定不会缓存;

    当 condition = true,且 unless = true,不缓存;

    当 condition = true,且 unless = false,缓存;


http://www.ppmy.cn/news/1098518.html

相关文章

js中如何判断一个对象是否为空对象?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 使用 Object.keys()⭐ 使用 for...in 循环⭐ 使用 JSON.stringify()⭐ 使用 ES6 的 Object.getOwnPropertyNames()⭐ 写在最后 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带…

Java认识异常(超级详细)

目录 异常的概念和体系结构 异常的概念 异常的体系结构 异常的分类 1.编译时异常 2.运行时异常 异常的处理 防御式编程 LBYL EAFP 异常的抛出 异常的捕获 异常声明throws try-catch捕获并处理 finally 异常的处理流程 异常的概念和体系结构 异常的概念 在Java中…

简单聊聊Innodb崩溃恢复那些事

简单聊聊Innodb崩溃恢复那些事 Buffer Pool 整体架构Buffer Pool 管理策略redo 日志redo log fileMini-TransactionCheckPoint部分写出问题 崩溃恢复 undo日志整体结构日志格式记录格式purge回滚 总结额外参考 本文想用简单精炼的语言将Innodb崩溃恢复那些事情好好拾到拾到&…

MYSQL的系统数据表空间,用户数据表空间,系统临时表空间,用户临时表空间详解

系统数据表空间: 系统表空间可以有一个或多个数据文件。默认情况下,会在数据目录中创建一个名为ibdata1的系统表空间数据文件 。系统表空间数据文件的大小和数量由innodb_data_file_path启动选项定义。 mysql> show variables like %innodb_data%; ------------…

工具 | XShell的学习与使用

工具 | XShell的学习与使用 时间:2023年9月8日09:03:29 文章目录 工具 | XShell的学习与使用1.下载2.安装 1.下载 1.官网XSHELL - NetSarang Website 2.免费版下载:家庭/学校免费 - NetSarang Website (xshell.com) 3.https://cdn.netsarang.net/de06d10…

GO语言网络编程(并发编程)并发介绍,Goroutine

GO语言网络编程(并发编程)并发介绍,Goroutine 1、并发介绍 进程和线程 A. 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。 B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更…

国内CRM软件系统厂商排名

我们知道CRM软件成为了企业管理中不可或缺的一部分,目前国内CRM厂商排名是怎样的呢?经过评估名列前茅的分别是Zoho CRM、Salesforce CRM、Microsoft Dynamics 、SAP CRM、HubSpot CRM。 1.Zoho Zoho CRM凭借先进的技术和创新的解决方案,帮…

1.1 计算机网络在信息时代中的作用

思维导图: 正文: 我的理解: 这段话是一本书或课程的第一章简介,它的目的是为读者或学生提供一个关于计算机网络基础知识的框架或大纲。 首先,它强调了这章是整本书的一个概览,会先介绍计算机网络在信息时…

IDEA中debug调试模拟时显示不全(不显示null)的解决

IDEA中debug调试模拟时显示不全(不显示null)的解决 1、在IDEA中找到File(文件)->Settings(设置) 2、依次找到以下内容进行设置(原版、汉化版): 打开Build, Executio…

【Java】反射 之 动态代理

动态代理 我们来比较Java的class 和interface的区别: 可以实例化class(非abstract); 不能实例化interface。 所有interface类型的变量总是通过某个实例向上转型并赋值给接口类型变量的: CharSequence cs new Strin…

【nlp-with-transformers】|Transformers中的generate函数解析

今天社群中的小伙伴面试遇到了一个问题,如何保证生成式语言模型在同样的输入情况下可以保证同样的输出。 这里面造成问题的因素有两个方面: 一个方面是在forward过程中参数的计算出现了差异,这种情况一般发生在游戏显卡中,游戏显…

Edge被2345浏览器劫持 解决方法

Edge 被 hao123 劫持解决方法_edge被hao123锁定改不了_小子宝丁的博客-CSDN博客

Android文件关联

用户需求:Android在系统文件夹找到一个文件想发送自己开发的app进行处理该怎么办? 这时候可以采用两个Activity,一个Activity用作Launcher,一个用于处理发送的文件;具体Activity intent-filter该怎么写了?可以参考下面的代码: <intent-filter><action androi…

grep正则说明

基础正则表达式 grep 工具&#xff0c;以前介绍过。 grep -[acinv] 搜索内容串 filename -a 以文本文件方式搜索 -c 计算找到的符合行的次数 -i 忽略大小写 -n 顺便输出行号 -v 反向选择&#xff0c;即找 没有搜索字符串的行 其中搜索串可以是正则表达式! shell 里面…

目标检测YOLO实战应用案例100讲-SAR图像多尺度舰船目标检测(续)

目录 3.3.2 相似超像素聚合 3.4 实验分析 3.4.1 实验方案设计 3.4.2 实验结果分析

运行报错(二)cat: write error: Broken pipe

报错现象 运行一些脚本&#xff0c;其中包含 cat 与 grep 流水线化的命令&#xff0c;例如: cat file.txt | grep "pattern"报错&#xff1a;cat: write error: Broken pipe 报错原因 原因是管道在cat还有一些数据要读取时被grep关闭了。信号 SIGPIPE 被 cat 捕获…

信息化发展31

IT 治理的管理层次 IT 治理要保证总体战略目标能够从上而下贯彻执行&#xff0c; IT 治理的管理层次大致可分为三层&#xff1a; 最高管理层、执行管理层、业务与服务执行层。 1 &#xff09;最高管理层的主要职责包括&#xff1a; 证实口战略与业务战略是否一致&#xff1b; …

【Linux从入门到精通】通信 | 共享内存(System V)

本篇文章接着上篇文章通信 | 管道通信&#xff08;匿名管道 & 命名管道&#xff09;进行讲解。本篇文章的中点内容是共享内存。 文章目录 一、初识与创建共享内存 1、1 什么是共享内存 1、2 共享内存函数 1、2、1 创建共享内存 shmget 1、2、2 ftok 生成 key 1、2、3 获取共…

数据结构算法刷题:背包问题

整数和是p&#xff0c;负数和是s-p&#xff0c;那么target p - (s-p)&#xff0c;求出p (st)//2 class Solution: def findTargetSumWays(self, nums: List[int], target: int) -> int: target sum(nums) if target < 0 or target % 2: #target 一定是偶数而且是大于…

2023数学建模国赛B题完整论文来啦!(含一二问求解代码及三四问仿真模拟代码)

大家好呀&#xff0c;从昨天发布赛题一直到现在&#xff0c;总算完成了全国大学生数学建模竞赛B题完整的成品论文。 本论文可以保证原创&#xff0c;保证高质量。绝不是随便引用一大堆模型和代码复制粘贴进来完全没有应用糊弄人的垃圾半成品论文。 说实话团队通宵一直到现在做…
最新文章