​@Cacheable 注解​

news2024/5/19 23:32: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.coloradmin.cn/o/988315.html

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

相关文章

14. 线性代数 - 线性方程组

文章目录 线性方程组矩阵行列式全排列和逆序数N阶行列式(非)齐次线性方程Hi,大家好。我是茶桁。 结束了「微积分」部分的学习之后我们稍作休整,今天正式开始另外一部分:「线性代数」的学习。小伙伴们放松完回来要开始紧张起来了。 我们之前说过,不管是哪一个工程学科,根…

一定要看!超好用的音频剪辑软件推荐

“有没有好用的音频剪辑软件推荐呀?最近需要剪辑一个混合音乐,用来参加学校的歌曲比赛,但是现在没有办法进行剪辑,音频现在很多杂音,根本用不了,求推荐一个好用的音频剪辑软件,谢谢啦” 随着科…

WordPress 网站 “Error Establishing a Database Connection” 建立数据库连接时出错的解决方法

WordPress 网站 “Error Establishing a Database Connection” 建立数据库连接时出错的解决方法 有事半年没管网站,今天突然访问网站居然出现了这个: 以下是解决方案: 检查数据库是否运行,重启数据库 1.检查数据库是否正常运…

在校学生如何白嫖黑群辉虚拟机和内网穿透,实现海量资源的公网访问?(小白专用)

文章目录 前言本教程解决的问题是:按照本教程方法操作后,达到的效果是前排提醒: 1 搭建群辉虚拟机1.1 下载黑群辉文件vmvare虚拟机安装包1.2 安装VMware虚拟机:1.3 解压黑 群辉虚拟机文件1.4 虚拟机初始化1.5 没有搜索到黑群辉的解…

Purple Pi OH(Debian/Ubuntu)使用python控制gpio

本文分享的是Purple Pi OH开源主板搭载Debian/Ubuntu系统如何使用python控制gpio。 Purple Pi OH作为一款兼容树莓派的开源主板,采用瑞芯微RK3566 (Cortex-A55) 四核64位超强CPU,主频最高达1.8 GHz,算力高达1Tops,支持INT8/INT16,支持Tensor…

好玩的js特效

记录一些好玩的js特效 1、鱼跳跃特效 引入jquery:https://code.jquery.com/jquery-3.7.1.min.js 源码如下&#xff1a; <!--引入jquery--> <script src"https://code.jquery.com/jquery-3.7.1.min.js"></script> <!--引入跳跃源码--> <s…

【PHP】使用TCPDF导出PDF文件

目录 一、安装TCPDF类库 二、安装字体 三、使用TCPDF导出PDF文件 目的&#xff1a;PHP通过TCPDF类库导出文件为PDF。 开发语言及类库&#xff1a;ThinkPHP、TCPDF 效果图如下 一、安装TCPDF类库 在项目根目录使用composer安装TCPDF&#xff0c;安装完成后会在vendor目录下…

深化超低时延技术合作,中科驭数助力金仕达开创极速行情新高度

近日&#xff0c;金仕达副总经理吴江带领FPGA低延时、终端和分布式团队主要负责人赴中科驭数武汉研发中心考察调研。双方深入探讨低延时技术&#xff0c;并在FPGA国产化成果、高性能开发平台等方向展开合作研讨。以此次交流为起点&#xff0c;双方将充分发挥各自优势&#xff0…

2023-简单点-编译是什么?gcc是什么?

编译目的 把一种 程序 变成 另一种更接近机器指令 编译的术语 “接近专家的最快方法第一步&#xff0c;直接了解100行业黑话” 那么来了解一下&#xff0c;编译过程中的黑话&#xff1a; 词法分析语法分析中间代码目标代码代码优化出错管理表格管理 gcc是个什么? 一种编译…

【liunx】进程的状态

进程的状态 1.进程的状态2.僵尸进程3.孤儿进程 1.进程的状态 我们或多或少了解到进程的状态分为&#xff1a; 运行&#xff0c;新建&#xff0c;就绪&#xff0c;挂起&#xff0c;阻塞&#xff0c;等待&#xff0c;停止&#xff0c;挂机&#xff0c;死亡… 首先解释一点&…

windows系统edge浏览器退出账户后还能免密登录的解决方式

edge浏览器明明退出登录了&#xff0c;还能不用输密码一键点击就能登录&#xff1b; 这是因为微软的煞笔产品经理用脚后跟想出来的方案。 解决方案&#xff1a; 去设置里的账号管理&#xff0c;注销自己的微软账号登录&#xff1b;如果你发现自己并没有登录&#xff0c;那么看…

Spring框架中bean的生命周期(理解)

1.解释Spring框架中bean的生命周期 在传统的Java应用中&#xff0c;bean的生命周期很简单。使用Java关键字new进行bean实例化&#xff0c;然后该bean就可以使用了。一旦该bean不再被使用&#xff0c;则由Java自动进行垃圾回收。 相比之下&#xff0c;Spring容器中的bean的生命…

2023什么好用的工具可以传大文件?

在当今的信息时代&#xff0c;数据已经成为了企业和个人的重要资产。无论是视频、音乐、图片、文档等各种格式的文件&#xff0c;都需要在不同的场景和需求下进行传输或分享。然而&#xff0c;随着数据量的不断增长&#xff0c;传输或分享大文件就成了一个难题。 常见的传输方式…

SpringBoot通过自定义注解实现日志打印

目录 前言&#xff1a; 正文 一.Spring AOP 1.JDK动态代理 2.Cglib动态代理 使用AOP主要的应用场景&#xff1a; SpringBoot通过自定义注解实现日志打印 一.Maven依赖 二.ControllerMethodLog.class自定义注解 三.Spring AOP切面方法的执行顺序 四.ControllerMethodL…

WireShark抓包工具的安装

1.下载安装包 在官网或者电脑应用商城都可以下载 2.安装 打开安装包&#xff0c;点击next 点击next 选择UI界面&#xff0c;两种都装上 根据习惯选择 选择安装位置点击安装 开始安装安装成功

测试用例基础

测试用例概要 测试用例要素 测试环境,操作步骤,测试数据,预期结果 测试用例好处 提高测试效率,节省测试时间 测试用例是自动化测试用例的前提 测试用例设计方法 1.基于需求的设计方法 需求文档 --> 梳理需求(掌握需求) --> 针对文档设计测试用例(基于需求设计测…

无脑013—— win11配置mmdetection实现训练自己的vol格式(xml)数据集

昨天使用cascade——rcnn 实现了MSAR 1.0的训练&#xff0c;今天来回顾一下 参考资料&#xff1a; http://t.csdn.cn/8A5WE http://t.csdn.cn/ccOZg 电脑 笔记本电脑&#xff0c;拯救者Y7000P 2018款&#xff0c;GTX1060显卡6G显存 &#xff0c;cmd输入代码nvcc-version显示的C…

[docker]笔记-portainer的安装

1、portainer是一款可视化的容器管理软件&#xff0c;利用portainer可以轻松方便的管理和创建容器。portainer本身是一个容器&#xff0c;完全免费并且具有汉化版。本文介绍portainer的安装和使用。 2、安装好容器并配置好容器环境&#xff0c;可参照https://blog.csdn.net/bl…

redis实战篇之导入黑马点评项目

1. 搭建黑马点评项目 链接&#xff1a;https://pan.baidu.com/s/1Q0AAlb4jM-5Fc0H_RYUX-A?pwd6666 提取码&#xff1a;6666 1.1 首先&#xff0c;导入SQL文件 其中的表有&#xff1a; tb_user&#xff1a;用户表 tb_user_info&#xff1a;用户详情表 tb_shop&#xff1a;商户…

使用Vagrant创建和管理本地Kubernetes(K8s)集群的步骤是什么

文章目录 步骤1&#xff1a;准备环境步骤2&#xff1a;创建Vagrantfile步骤3&#xff1a;启动虚拟机步骤4&#xff1a;安装Kubernetes步骤5&#xff1a;配置Kubernetes网络插件步骤6&#xff1a;将Worker节点加入集群步骤7&#xff1a;验证集群步骤8&#xff1a;部署应用步骤9&…