SpringBoot 整合缓存Cacheable实战详细使用

前言

我知道在接口api项目中,频繁的调用接口获取数据,查询数据库是非常耗费资源的,于是就有了缓存技术,可以把一些不常更新,或者经常使用的数据,缓存起来,然后下次再请求时候,就直接从缓存中获取,不需要再去查询数据,这样可以提供程序性能,增加用户体验,也节省服务资源浪费开销,

springboot帮你我们做好了整合,有对应的场景启动器start,我们之间引入使用就好了,帮我们整合了各种缓存


            org.springframework.boot
            spring-boot-starter-cache

简介

缓存介绍

Spring 从 3.1 开始就引入了对 Cache 的支持。定义了 org.springframework.cache.Cache 和 org.springframework.cache.CacheManager 接口来统一不同的缓存技术。并支持使用 JCache(JSR-107)注解简化我们的开发。

其使用方法和原理都类似于 Spring 对事务管理的支持。Spring Cache 是作用在方法上的,其核心思想是,当我们在调用一个缓存方法时会把该方法参数和返回结果作为一个键值对存在缓存中。

Cache 和 CacheManager 接口说明

Cache 接口包含缓存的各种操作集合,你操作缓存就是通过这个接口来操作的。
Cache 接口下 Spring 提供了各种 xxxCache 的实现,比如:RedisCache、EhCache、ConcurrentMapCache

CacheManager 定义了创建、配置、获取、管理和控制多个唯一命名的 Cache。这些 Cache 存在于 CacheManager 的上下文中。

小结

每次调用需要缓存功能的方法时,Spring 会检查指定参数的指定目标方法是否已经被调用过,如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓存结果后返回给用户。下次调用直接从缓存中获取。

使用Spring缓存抽象时我们需要关注以下两点;

  1. 确定方法需要被缓存以及他们的缓存策略
  2. 从缓存中读取之前缓存存储的数据

快速开始

  1. 使用缓存我们需要开启基于注解的缓存,使用 @EnableCaching 标注在 springboot 主启动类上或者配置类上
@SpringBootApplication
@MapperScan(value = {"cn.soboys.kmall.mapper","cn.soboys.kmall.sys.mapper","cn.soboys.kmall.security.mapper"},nameGenerator = UniqueNameGenerator.class)
@ComponentScan(value =  {"cn.soboys.kmall"},nameGenerator = UniqueNameGenerator.class)
@EnableCaching  //开启缓存注解驱动,否则后面使用的缓存都是无效的
public class WebApplication {
    private static ApplicationContext applicationContext;

    public static void main(String[] args) {
        applicationContext =
                SpringApplication.run(WebApplication.class, args);
        //displayAllBeans();
    }

    /**
     * 打印所以装载的bean
     */
    public static void displayAllBeans() {
        String[] allBeanNames = applicationContext.getBeanDefinitionNames();
        for (String beanName : allBeanNames) {
            System.out.println(beanName);
        }
    }
}

或者配置类

/**
 * @author kenx
 * @version 1.0
 * @date 2021/8/17 15:05
 * @webSite https://www.soboys.cn/
 * 自定义缓存配置
 */
@Configuration
@Slf4j
@EnableCaching  //开启缓存注解驱动,否则后面使用的缓存都是无效的
public class CacheConfig {

    //自定义配置类配置keyGenerator

    @Bean("myKeyGenerator")
    public KeyGenerator keyGenerator(){
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                return method.getName()+"["+ Arrays.asList(params).toString() +"]";
            }
        };
    }
}
  1. 标注缓存注解在需要缓存的地方使用 @Cacheable注解
@CacheConfig(cacheNames = "menuCache",keyGenerator ="myKeyGenerator" )
public interface IMenuService extends IService {
    /**
     * 获取用户菜单信息
     *
     * @param username 用户名
     * @return
     */
    @Cacheable(key = "#username")
    List getUserMenus(String username);
}

@Cacheable注解有如下一些参数我们可以看到他源码

@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;
}

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

  1. cacheNames/value :指定缓存组件的名字;将方法的返回结果放在哪个缓存中,是数组的方式,可以指定 多个缓存;
  2. key :缓存数据时使用的 key,可以用它来指定。默认是使用方法参数的值。(这个 key 你可以使用 spEL 表达式来编写如 #i d;参数id的值 #a0 #p0 #root.args[0]
  3. keyGenerator :key的生成器;可以自己指定key的生成器的组件id 然后key 和 keyGenerator 二选一使用
  4. cacheManager :可以用来指定缓存管理器。从哪个缓存管理器里面获取缓存。或者cacheResolver指定获取解析器
  5. condition :可以用来指定符合条件的情况下才缓存
condition = "#id>0"
condition = "#a0>1":第一个参数的值》1的时候才进行缓存
  1. unless :否定缓存。当 unless 指定的条件为 true ,方法的返回值就不会被缓存。当然你也可以获取到结果进行判断。(通过 #result 获取方法结果)
unless = "#result == null"
unless = "#a0==2":如果第一个参数的值是2,结果不缓存;
  1. sync :是否使用异步模式。异步模式的情况下 unless不支持 默认是方法执行完,以同步的方式将方法返回的结果存在缓存中

cacheNames/value属性

用来指定缓存组件的名字,将方法的返回结果放在哪个缓存中,可以是数组的方式,支持指定多个缓存

 /**
     * 获取用户菜单信息
     *
     * @param username 用户名
     * @return
     */
    @Cacheable(cacheNames = "menuCache") 或者// @Cacheable(cacheNames = {"menuCache","neCacge"})
    List getUserMenus(String username);

如果只有一个属性,cacheNames可忽略,直接是value属性默认

key

缓存数据时使用的 key。默认使用的是方法参数的值。可以使用 spEL 表达式去编写。

Cache SpEL available metadata

名称 位置 描述 示例 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 执行上下文(avaluation context) 当前被调用的方法的参数,如findArtisan(Artisan artisan),可以通过#artsian.id获得参数 #artsian.id result 执行上下文(evaluation context) 方法执行后的返回值(仅当方法执行后的判断有效,如 unless cacheEvict的beforeInvocation=false) #result

//key = "#username" 就是参数username
 @Cacheable(key = "#username" ,cacheNames = "menuCache")
List getUserMenus(String username);

keyGenerator

key 的生成器,可以自己指定 key 的生成器,通过这个生成器来生成 key

定义一个@Bean类,将KeyGenerator添加到Spring容器

@Configuration
@Slf4j
@EnableCaching  //开启缓存注解驱动,否则后面使用的缓存都是无效的
public class CacheConfig {

    //自定义配置类配置keyGenerator

    @Bean("myKeyGenerator")
    public KeyGenerator keyGenerator(){
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                return method.getName()+"["+ Arrays.asList(params).toString() +"]";
            }
        };
    }
}

在使用指定自己的 @Cacheable(cacheNames = "menuCache",keyGenerator ="myKeyGenerator" )

注意这样放入缓存中的 key 的生成规则就按照你自定义的 keyGenerator 来生成。不过需要注意的是:@Cacheable 的属性,key 和 keyGenerator 使用的时候,一般二选一。

condition

符合条件的情况下才缓存。方法返回的数据要不要缓存,可以做一个动态判断。

 /**
     * 获取用户菜单信息
     *
     * @param username 用户名
     * @return
     */
    //判断username 用户名是kenx开头才会被缓存
    @Cacheable(key = "#username" ,condition = "#username.startsWith('kenx')")
    List getUserMenus(String username);

unless

否定缓存。当 unless 指定的条件为 true ,方法的返回值就不会被缓存。

 /**
     * 获取用户菜单信息
     *
     * @param username 用户名
     * @return
     */
    //判断username 用户名是kenx开头不会被缓存
    @Cacheable(key = "#username" ,condition = "#username.startsWith('kenx')")
    List getUserMenus(String username);

spEL 编写 key

SpringBoot 整合缓存Cacheable实战详细使用

当然我们可以全局去配置, cacheNames,keyGenerator属性通过 @CacheConfig注解可以用于抽取缓存的公共配置,然后在类加上就可以,eg:如

//全局配置,下面用到缓存方法,不配置默认使用全局的
@CacheConfig(cacheNames = "menuCache",keyGenerator ="myKeyGenerator" )
public interface IMenuService extends IService {
    /**
     * 获取用户菜单信息
     *
     * @param username 用户名
     * @return
     */
    @Cacheabl
    List getUserMenus(String username);
}

深入使用

@CachePut

@CachePut注解也是一个用来缓存的注解,不过缓存和@Cacheable有明显的区别是 即调用方法,又更新缓存数据,也就是执行方法操作之后再来同步更新缓存,所以这个主键常用于更新操作,也可以用于查询,主键属性和@Cacheable有很多类似的参看 @CachePut源码

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface CachePut {
    @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 "";
}
/**
     *  @CachePut:既调用方法,又更新缓存数据;同步更新缓存
     *  修改了数据,同时更新缓存
     */
    @CachePut(value = {"emp"}, key = "#result.id")
    public Employee updateEmp(Employee employee){
        employeeMapper.updateEmp(employee);
        LOG.info("更新{}号员工数据",employee.getId());
        return employee;
    }

@CacheEvict

清空缓存
主要属性:

  1. key:指定要清除的数据
  2. allEntries = true:指定清除这个缓存中所有的数据
  3. beforeInvocation = false:默认代表缓存清除操作是在方法执行之后执行
  4. beforeInvocation = true:代表清除缓存操作是在方法运行之前执行
@CacheEvict(value = {"emp"}, beforeInvocation = true,key="#id")
    public void deleteEmp(Integer id){
        employeeMapper.deleteEmpById(id);
        //int i = 10/0;
    }

@Caching

@Caching 用于定义复杂的缓存规则,可以集成 @Cacheable和 @CachePut

// @Caching 定义复杂的缓存规则
    @Caching(
            cacheable = {
                    @Cacheable(/*value={"emp"},*/key = "#lastName")
            },
            put = {
                    @CachePut(/*value={"emp"},*/key = "#result.id"),
                    @CachePut(/*value={"emp"},*/key = "#result.email")
            }
    )
    public Employee getEmpByLastName(String lastName){
        return employeeMapper.getEmpByLastName(lastName);
    }

@CacheConfig

@CacheConfig注解可以用于抽取缓存的公共配置,然后在类加上就可以

//全局配置,下面用到缓存方法,不配置默认使用全局的
@CacheConfig(cacheNames = "menuCache",keyGenerator ="myKeyGenerator" )
public interface IMenuService extends IService {
    /**
     * 获取用户菜单信息
     *
     * @param username 用户名
     * @return
     */
    @Cacheable(key = "#username" )
    List getUserMenus(String username);
}

参考

  1. SpringBoot之缓存使用教程
  2. 缓存入门

Original: https://www.cnblogs.com/kenx/p/15155865.html
Author: kenx
Title: SpringBoot 整合缓存Cacheable实战详细使用

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/608322/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • spring boot拦截器WebMvcConfigurerAdapter,以及高版本的替换方案(转)

    文章转自 http://blog.51cto.com/12066352/2093750 最近项目采用spring icloud,用的spring boot版本是1.5.x的,spr…

    Java 2023年5月30日
    051
  • Java 基础常见知识点&面试题总结(上),2022 最新版!| JavaGuide

    你好,我是 Guide。秋招即将到来,我对 JavaGuide 的内容进行了重构完善,公众号同步一下最新更新,希望能够帮助你。 基础概念与常识 Java 语言有哪些特点? 简单易学…

    Java 2023年6月9日
    073
  • roketmq安装和运行

    软件下载: 链接:https://pan.baidu.com/s/1CRFQyQrVsKQHFTkU5m3-Hg提取码:gejx复制这段内容后打开百度网盘手机App,操作更方便哦 …

    Java 2023年5月30日
    062
  • Android Calendar 系统日历提醒、日程同步系统

    安卓往系统中添加日程提醒,吭比较多。 首先有个需求(仿制 ios 日历),为什么仿制ios呢?这个得问产品了,我只是一个搬砖的程序员 (*´艸`) 捂嘴 大致有 日期,时间,重复设…

    Java 2023年6月7日
    084
  • Docker实用篇

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    Java 2023年6月13日
    067
  • vue实现录音功能js-audio-recorder带波浪图

    实现效果:可得到三种录音数据, pcm,wav,mp3 等 官方api入口: 点我 (网不好的童鞋可以看最下面的 api 截图) 官方案例入口: 点我 cnpm i js-audi…

    Java 2023年6月8日
    094
  • [研究]SpringBoot-MybatisPlus-Dynamic(多数据源)

    特性 1. 支持 数据源分组 ,适用于多种场景 纯粹多库 读写分离 一主多从 混合模式。 2. 支持数据库敏感配置信息 加密 ENC()。 3. 支持每个数据库独立初始化表结构sc…

    Java 2023年6月9日
    075
  • 4、反射的概念

    动态语言: 是一类在运行时可以改变其结构构的语言· 例如新的函数、对象、甚至代码可以被 引进, 已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代 码可以根据某些条件…

    Java 2023年6月8日
    076
  • Nginx alias root 和 try_files 基本使用

    请求都用域名 root Syntax: root path; Default: root html; Context: http, server, location, if in …

    Java 2023年5月30日
    060
  • 3.Eureka核心特性及面试点

    Eureka核心特性 服务注册 Eureka Client在第一次心跳时向Eureka Server注册 服务续约(renew) Eureka Client通过发送心跳进行续约 默…

    Java 2023年6月8日
    060
  • 细讲红黑树

    写在前面 红黑树也是一棵二叉查找树,既然有了AVL树为什么还需要红黑树呢? 之前在了平衡二叉树AVL实现中讲到了为什么使用平衡二叉树AVL( 解决二叉查找树退化为类似链表的问题),…

    Java 2023年6月5日
    079
  • Spring的AOP底层解析

    AOP原理的前置知识 动态代理在Spring中的应用: 1.AOP 2.注解@Lazy (2)Spring中针对动态代理的封装 1.ProxyFactory (1)介绍 基于两种动…

    Java 2023年6月16日
    067
  • 《SpringBoot官网文档:2.1.5》

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    Java 2023年6月6日
    051
  • 从零开始实现放置游戏(九)——实现后台管理系统(7)地图选择控件

    前面做了地图怪物的添加,删除,查询等功能。但添加怪物的时候,需要选择怪物所在地图。前几张的源代码中,我忘了把这部分改回去,所以如果想要成功添加,需要自己改一下html界面,手动填写…

    Java 2023年6月5日
    083
  • 运用Spring Aop,一个注解实现日志记录

    运用Spring Aop,一个注解实现日志记录 1. 介绍 我们都知道Spring框架的两大特性分别是 IOC (控制反转)和 AOP (面向切面),这个是每一个Spring学习视…

    Java 2023年6月8日
    085
  • Oracle总结一

    1.1 数据 数据是描述事物的符号,它有多种表现形式:文本,图形,音频,视频。计算机处理数据的基本单位是字节。 1.2 数据库(Database, 简称DB) 同粮库,车库类似,数…

    Java 2023年6月8日
    071
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球