当前位置:

首页 > 编程开发 > SpringBoot怎么使用@Cacheable进行缓存与取值

SpringBoot怎么使用@Cacheable进行缓存与取值

使用@Cacheable进行缓存与取值1.@Cacheable的作用缓存使用步骤:@Cacheable这个注解,用它就是为了使用缓存的。所以我们可以先说一下缓存的使用步骤:开启基于注解的缓存,使用@EnableCaching标识在SpringBoot的主启动类上。标注缓存注解即可第一步:开启基于注解的缓存,使用@EnableCaching标注在springboot主启动类上//开启基于注解的缓存@EnableCaching@EnableRyFeignClients@SpringBootApplicatio

本文目录

    使用@Cacheable进行缓存与取值

    1. @Cacheable的作用

    缓存使用步骤:@Cacheable这个注解,用它就是为了使用缓存的。所以我们可以先说一下缓存的使用步骤:

    开启基于注解的缓存,使用 @EnableCaching 标识在 SpringBoot 的主启动类上。

    标注缓存注解即可

    第一步:开启基于注解的缓存,使用 @EnableCaching 标注在 springboot 主启动类上

    //开启基于注解的缓存
    @EnableCaching   
    @EnableRyFeignClients
    @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
    public class ZfjgAuthApplication {
        public static void main(String[] args) {
            SpringApplication.run(ZfjgAuthApplication.class, args);
        }
    }

    第二步:标注缓存注解

    @Repository
    public interface DeviceMapper {
        @Cacheable(cacheNames = "DeviceDO.deviceId")
        DeviceDO get(String deviceId);
        @CacheEvict(cacheNames = "DeviceDO.deviceId", key = "#record.deviceId")
        int insert(DeviceDO record);
    }

    注:这里使用 @Cacheable 注解就可以将运行结果缓存,以后查询相同的数据,直接从缓存中取,不需要调用方法。

    2.常用属性说明

    下面介绍一下 @Cacheable 这个注解常用的几个属性:

    • cacheNames/value:用来指定缓存组件的名字

    • key:缓存数据时使用的 key,可以用它来指定。默认是使用方法参数的值。(这个 key 你可以使用 spEL 表达式来编写)

    • keyGenerator:key 的生成器。 key 和 keyGenerator 二选一使用

    • cacheManager:可以用来指定缓存管理器。从哪个缓存管理器里面获取缓存。

    • condition:可以用来指定符合条件的情况下才缓存

    • unless:否定缓存。当 unless 指定的条件为 true ,方法的返回值就不会被缓存。当然你也可以获取到结果进行判断。(通过 #result 获取方法结果)

    • sync:是否使用异步模式。

    SpringBoot中Cacheable使用说明

    功能说明

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

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

    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Inherited;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    import org.springframework.core.annotation.AliasFor;
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Inherited
    @Documented
    public @interface Cacheable {
        @AliasFor("cacheNames")
        String[] value() default {};
        @AliasFor("value")
        String[] cacheNames() default {};
        String key() default "";
        String keyGenerator() default "";
        String cacheManager() default "";
        String cacheResolver() default "";
        String condition() default "";
        String unless() default "";
        boolean sync() default false;
    }

    1. 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 缓存,如果缓存中有结果,就不会去执行方法了。

    2. 关联多个缓存名

    其实,按照官方文档,@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;
    }

    为了直观起见,直接将 id 参数写到代码里。现在,我们来测试一下,看一下结果:

    SpringBoot怎么使用@Cacheable进行缓存与取值

    3. key & keyGenerator

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

    3.1 KeyGenerator 自动生成

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

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

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

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

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

    3.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;
        }

    SpringBoot怎么使用@Cacheable进行缓存与取值

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

    4. 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 是互斥参数,同时指定两个可能会导致异常。

    5. sync

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

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

    6. condition

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

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

    我们看一下数据库,以这两条数据为例:

      SpringBoot怎么使用@Cacheable进行缓存与取值

    我们首先定义一个带条件的缓存方法:

    @Override
        @Cacheable(value = {"menuById"}, key = "#id", condition = "#conditionValue > 1")
        public Menu findById(String id, Integer conditionValue) {
            Menu menu = this.getById(id);
            if (menu != null){
                System.out.println("menu.name = " + menu.getName());
            }
            return menu;
        }

    然后分两种情况调用(为了直观可见,直接将 id 写在代码中):

    @GetMapping("/findById/{id}")
        public Menu findById(@PathVariable("id")String id){
            Menu menu0 = menuService.findById("fe278df654adf23cf6687f64d1549c0a", 0);
            Menu menu2 = menuService.findById("fb6106721f289ebf0969565fa8361c75", 2);
            return menu0;
        }

    SpringBoot怎么使用@Cacheable进行缓存与取值

    SpringBoot怎么使用@Cacheable进行缓存与取值

    可以看到,两次请求都执行方法(因为原来缓存中都没有数据),但是只有“微服务测试2”缓存了。这说明,只有满足 condition 条件的调用,结果才会被缓存。接下来我们再请求一遍,看下结果和打印:

    SpringBoot怎么使用@Cacheable进行缓存与取值

    SpringBoot怎么使用@Cacheable进行缓存与取值

    可以看到,“微服务测试2”由于已经有了缓存,所以没有再执行方法体。而“微服务测试0”又一次执行了。

    7. unless

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

    我们先清除 redis 中的数据。然后看看 mysql 中的数据:

      SpringBoot怎么使用@Cacheable进行缓存与取值

    然后编写一个缓存方法(在该方法中,result代表方法的返回值。关于):

    @Override
        @Cacheable(value = {"menuById"}, key = "#id", unless = "#result.type == 'folder'")
        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;
        }

    SpringBoot怎么使用@Cacheable进行缓存与取值

    SpringBoot怎么使用@Cacheable进行缓存与取值

    可以看到,两次都执行了方法体(其实,unless 条件就是在方法执行完毕后调用,所以它不会影响方法的执行),但是结果只有 menu.type = ‘page’ 的缓存了,说明 unless 参数生效了。

    8. condition VS unless ?

    既然 condition 和 unless 都能决定是否进行缓存,那么同时指定这两个参数并且结果相冲突的时候,会怎么样呢?我们来试一试。

    首先清除 redis 数据,然后在缓存方法上加上 condition=“true”,如:

    @Override
        @Cacheable(value = {"menuById"}, key = "#id", condition = "true", unless = "#result.type == 'folder'")
        public Menu findById(String id) {
            Menu menu = this.getById(id);
            if (menu != null){
                System.out.println("menu.name = " + menu.getName());
            }
            return menu;
        }

    其它代码不变,我们来看一下缓存结果和打印:

    SpringBoot怎么使用@Cacheable进行缓存与取值

    SpringBoot怎么使用@Cacheable进行缓存与取值

    可以看到,虽然两次调用都执行了,但是,type=‘folder’ 的还是被排除了。说明这种情况下,unless 比 condition 优先级要高。接下来我们把 condition=“false”,再来试试,结果:

    SpringBoot怎么使用@Cacheable进行缓存与取值

    SpringBoot怎么使用@Cacheable进行缓存与取值

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

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

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

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

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

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发
    相关文章 更多
    springBoot项目常用目录有哪些
    springBoot项目常用目录有哪些

    springBoot项目常用目录springBoot项目的目录结构及名规范介绍基于SpringBoot开发时的目录结构及命名规范,通过介绍能够为你解决,在实际项目中如何规划目录结构?如何命名目录更规范?各个目录分别代表什么含义?等三个问题。目录说明servicex//项目名|-admin-ui//管理服务前端代码(一般将UI和SERVICE放到一个工程中,便于管理)|-servicex-auth//模块1|-servicex-common//模块2|-servicex-gateway//模块3|-serv

    解析SpringBoot和SpringMVC之间的异同
    解析SpringBoot和SpringMVC之间的异同

    解析SpringBoot和SpringMVC之间的异同SpringBoot和SpringMVC是Java领域中非常重要的开发框架。虽然它们都属于Spring框架的一部分,但是在使用和功能上有一些明显的区别。本文将对SpringBoot和SpringMVC进行比较,解析它们之间的异同。首先,让我们来了解一下SpringBoot。SpringBo

    探索SpringCloud和SpringBoot在微服务领域的不同应用方式
    探索SpringCloud和SpringBoot在微服务领域的不同应用方式

    近年来,随着云计算和分布式架构的兴起,微服务架构的应用越来越广泛。而SpringCloud和SpringBoot作为Java开发中的两个重要框架,对于微服务的实现起到了重要的作用。然而,很多人对于它们在微服务领域的不同应用方式还存在一定的疑惑。本文将从不同的角度探索SpringCloud和SpringBoot在微服务中的应用方式。首先,我们来了解一下Spri

    SpringBoot和SpringMVC有何区别?
    SpringBoot和SpringMVC有何区别?

    SpringBoot与SpringMVC的不同之处在哪里?SpringBoot和SpringMVC是两个非常流行的Java开发框架,用于构建Web应用程序。尽管它们经常分别被使用,但它们之间的不同之处也是很明显的。首先,SpringBoot可以被看作是一个Spring框架的扩展或者增强版。它旨在简化Spring应用程序的初始化和配置过程,以帮助开发人

    比较SpringBoot和SpringMVC的异同
    比较SpringBoot和SpringMVC的异同

    SpringBoot和SpringMVC都是Java开发中常用的框架,但它们之间有一些明显的差异。本文将探究这两个框架的特点和用途,并对它们的差异进行比较。首先,我们来了解一下SpringBoot。SpringBoot是由Pivotal团队开发的,它旨在简化基于Spring框架的应用程序的创建和部署。它提供了一种快速、轻量级的方式来构建独立的、可执行

    SpringBoot与SpringMVC的不同点是什么?
    SpringBoot与SpringMVC的不同点是什么?

    SpringBoot和SpringMVC是Java开发中常用的两个框架,它们都是由Spring框架所提供的,但在功能和使用方式上有着一些区别。本文将分别介绍SpringBoot和SpringMVC的特点和区别。一、SpringBoot的特点:简化配置:SpringBoot通过约定优于配置的原则,大大简化了项目的配置过程。它可以自动配置项目所需要的参数,开发人

    比较和选择指南:SpringCloud与SpringBoot的功能比较
    比较和选择指南:SpringCloud与SpringBoot的功能比较

    SpringCloud和SpringBoot是目前Java领域中最热门的开源框架,它们分别提供了一套完善的微服务架构和快速构建应用程序的解决方案。本文将对它们的功能进行比较,并给出选择指南,以帮助读者了解它们的优势和适用场景。SpringBoot是一个用于开发Java应用程序的框架,它提供了一个简化的开发流程,集成了大量常用的功能和组件,减少了开发者的工作量

    SpringCloud与SpringBoot:分析它们之间的相似与不同
    SpringCloud与SpringBoot:分析它们之间的相似与不同

    深入剖析SpringCloud和SpringBoot的异同引言:在构建企业级应用程序时,Spring框架已经成为开发者们的首选。它提供了一种简单而强大的方式来构建独立于平台的应用程序。Spring框架由两个核心模块组成:SpringBoot和SpringCloud。虽然它们都可以帮助开发者快速搭建应用程序,但它们的目标和用途存在一些差异。本文将深入剖析Spr

    从架构角度看SpringCloud与SpringBoot的差异
    从架构角度看SpringCloud与SpringBoot的差异

    从架构角度看SpringCloud与SpringBoot的差异引言:在当今的互联网时代,构建分布式系统已经成为了一种必要的需求。而SpringBoot和SpringCloud正是为了满足这个需求而诞生的。尽管它们都是由Spring框架所提供的解决方案,但从架构角度来看,它们存在着一些重要的差异。本文将从架构的角度出发,对SpringBoot和SpringCl

    Java API 开发中使用 SpringBoot 的利弊分析
    Java API 开发中使用 SpringBoot 的利弊分析

    JavaAPI开发中使用SpringBoot的利弊分析随着互联网的快速发展和技术的不断更新迭代,Java技术在开发中的广泛应用引起了人们的重视。而SpringBoot作为一种微服务架构的解决方案,已经逐渐成为Java开发中的热门选择。那么,在JavaAPI开发中使用SpringBoot,到底有哪些利弊呢?下面来分析一下。利:简化开发

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程
    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版
    macOS

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版
    Windows

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。