Spring Cache使用

一、概述

Spring Cache 并不是一种Cache实现的技术,Spring Cache是一种缓存实现的通用技术,是基于

Spring提供的Cache框架,让开发者更容易将缓存的实现快速的键入自己的项目中,简化了代码中对缓存的操作。

Spring从3.1开始定义了 org.springframework.cache.Cache 和 org.springframework.cache.CacheManager接口来统一不同的缓存技术并支持使用

JCache(JSR-107) 注解形式简化开发

2、Spring Cache 核心接口

     1)org.springframework.cache.Cache

          Cache接口定义了缓存的组件规范,包含缓存的各种集合操作;Spring提供了Cache接

          口的各种实现类(XXXCache),如:RedisCache、ConcurrentMapCache等

          Cache接口常用方法介绍:

//获取缓存中间件名称,如:redis
String getName(); 
//获取缓存中所有条目
Object getNativeCache();
//根据key从缓存中取数据
@Nullable
Cache.ValueWrapper get(Object var1);
//根据key从缓存中取数据,并将value转换为指定类型
@Nullable
<T> T get(Object var1, @Nullable Class<T> var2);

//根据key从缓存中取数据,若key的值存在,则直接返回,若key的值不存在则从线程Callable中获取结果并将结果保存到缓存中,最后将结果返回
@Nullable
<T> T get(Object var1, Callable<T> var2);

void put(Object var1, @Nullable Object var2);//向缓存中添加数据
//若缓存中key的值不存在则把key-value 保存到缓存中,否则返回缓存中key的值
@Nullable
default Cache.ValueWrapper putIfAbsent(Object key, @Nullable Object value) {
    Cache.ValueWrapper existingValue = this.get(key);
    if (existingValue == null) {
        this.put(key, value);
    }

    return existingValue;
}

//从缓存中删除键对应的值
void evict(Object var1);

//清空缓存中所有数据
void clear();

     2)org.springframework.cache.CacheManager

           缓存管理器,管理各种Cache缓存

           CacheManager 接口常用方法介绍:

public interface CacheManager {
    

    //获取指定的Cache
    @Nullable
    Cache getCache(String var1);

    
    //获取所有的Cache名称
    Collection<String> getCacheNames();
}

二、Spring Cache 使用步骤

        以Spring Cache 整合Redis为例记录一次Spring Cache 使用步骤:

        1、引入依赖       

              1)引入Spring Cache依赖    

                    <dependency>
                          <groupId>org.springframework.boot</groupId>
                          <artifactId>spring-boot-starter-cache</artifactId>
                          <version>2.2.6.RELEASE</version>
                    </dependency>

              2)因为我们使用的缓存是Redis,所以还需要引入Spring boot 整合redis的依赖

                    <!-- 引入spring-redis 一来-->
                    <dependency>
                            <groupId>org.springframework.boot</groupId>
                            <artifactId>spring-boot-starter-data-redis</artifactId>
                            <exclusions>
                            <!--
                                   排除 spring data redis 中的默认的lettuce(spring data redis依赖在2.0

                                   版本后默认的客户端是lettuce),
                                   因为 lettuce 客户端有个bug会导致链接不释放,从而导致内存溢出
                                   把redis客户端替换成jedis来解决这个bug
                             -->
                                  <exclusion>
                                          <groupId>io.lettuce</groupId>
                                          <artifactId>lettuce-core</artifactId>
                                  </exclusion>
                            </exclusions>
                     </dependency>
                    <!--导入jedis -->
                   <dependency>
                           <groupId>redis.clients</groupId>
                           <artifactId>jedis</artifactId>
                    </dependency>

       2、编写配置

            1) 编写Spring Cache 配置之前,我们要先了解Spring Cache 自动配置了哪些东西?

             Spring Cache自动配置类是 CacheAutoConfiguration,CacheAutoConfiguration中

            通过子类 CacheConfigurationImportSelector 加载多种配置类,如下图所示:

                    

            我们这次使用的是Redis对应的配置文件RedisCacheConfiguration,RedisCacheConfiguration主要作用是实例化Redis的缓存管理器 RedisCacheManager,RedisCacheConfiguration结构如下:

@AutoConfigureAfter({RedisAutoConfiguration.class})
@ConditionalOnBean({RedisConnectionFactory.class})
@ConditionalOnMissingBean({CacheManager.class})
@Conditional({CacheCondition.class})
class RedisCacheConfiguration {
    RedisCacheConfiguration() {
    }

    @Bean
    RedisCacheManager cacheManager(CacheProperties cacheProperties, CacheManagerCustomizers cacheManagerCustomizers, ObjectProvider<org.springframework.data.redis.cache.RedisCacheConfiguration> redisCacheConfiguration, ObjectProvider<RedisCacheManagerBuilderCustomizer> redisCacheManagerBuilderCustomizers, RedisConnectionFactory redisConnectionFactory, ResourceLoader resourceLoader) {
        RedisCacheManagerBuilder builder = RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(this.determineConfiguration(cacheProperties, redisCacheConfiguration, resourceLoader.getClassLoader()));
        List<String> cacheNames = cacheProperties.getCacheNames();
        if (!cacheNames.isEmpty()) {
            builder.initialCacheNames(new LinkedHashSet(cacheNames));
        }

        redisCacheManagerBuilderCustomizers.orderedStream().forEach((customizer) -> {
            customizer.customize(builder);
        });
        return (RedisCacheManager)cacheManagerCustomizers.customize(builder.build());
    }
    
     private org.springframework.data.redis.cache.RedisCacheConfiguration determineConfiguration(CacheProperties cacheProperties, ObjectProvider<org.springframework.data.redis.cache.RedisCacheConfiguration> redisCacheConfiguration, ClassLoader classLoader) {
        return (org.springframework.data.redis.cache.RedisCacheConfiguration)redisCacheConfiguration.getIfAvailable(() -> {
            return this.createConfiguration(cacheProperties, classLoader);
        });
    }

    private org.springframework.data.redis.cache.RedisCacheConfiguration createConfiguration(CacheProperties cacheProperties, ClassLoader classLoader) {
        Redis redisProperties = cacheProperties.getRedis();
        org.springframework.data.redis.cache.RedisCacheConfiguration config = org.springframework.data.redis.cache.RedisCacheConfiguration.defaultCacheConfig();
        config = config.serializeValuesWith(SerializationPair.fromSerializer(new JdkSerializationRedisSerializer(classLoader)));
        if (redisProperties.getTimeToLive() != null) {
            config = config.entryTtl(redisProperties.getTimeToLive());
        }

        if (redisProperties.getKeyPrefix() != null) {
            config = config.prefixKeysWith(redisProperties.getKeyPrefix());
        }

        if (!redisProperties.isCacheNullValues()) {
            config = config.disableCachingNullValues();
        }

        if (!redisProperties.isUseKeyPrefix()) {
            config = config.disableKeyPrefix();
        }

        return config;
    }
}

             实例化RedisCacheConfiguration 需要传入参数 CacheProperties,CacheProperties是一

             个配置类,在CacheProperties 中针对不同的缓存中间件配置配置文件都有一个子类与

              其对应;另外由CacheProperties类可以发现SpringCache 支持 .proerties 类型的配置文件

              ,且配置项的名称以 “spring.cache” 开头,CacheProperties类属性如下图所示:

                         

          2)spring cache 整合Redis 需要我们手动配置的内容?

                创建 .properties类型的配置文件(如:application.properties)

                在配置文件一般只需要配置缓存类型,我们使用的缓存中间件是什么这里就配置什么

                (如:redis),spring cache 配置大致如下:

                    

       3、使用Spring Cache 简化缓存操作

             1)在启动类上添加注解 @EnableCaching 表示开始Spring Cache,如下图所示:

                    

              2)在需要使用缓存的方法上添加相应的注解,这里最常用的是注解@Cacheable;

                   注解 @Cacheable执行流程如下:

                           每次调用需要缓存功能的方法时,Spring会检查指定参数指定的目标方

                          法是否已经被调用过(即缓存中是否已经存在该目标方法的执行结果);

                          若缓存中目标方法执行结果已存在,直接获取缓存中执行后的结果;若缓存

                          中不存在数据,则执行目标方法,将执行结果返回给客户,并将执行结果保

                           存到缓存中。

                  注解@Cacheable使用代码如下:                    


@Cacheable
    public Map<String, List<Catalog2VO>> getCatalogJsonFromCache() {

        //1、查询所有的三级分类数据
        List<CategoryEntity> selectList = this.baseMapper.selectList(null);
        //2、将所有的三级分类,按父分类parentId分组
        Map<Long, List<CategoryEntity>> categoryMap = selectList.stream().collect(Collectors.groupingBy(key -> key.getParentCid()));

        //3、查询1级分类
        List<CategoryEntity> level1Category = categoryMap.get(0L);

        //4、封装数据
        Map<String,List<Catalog2VO>> catalog2Map = level1Category.stream().collect(Collectors.toMap(key -> key.getCatId().toString(),level1 -> {
    // 、、、、、省略 。。。。。
        }));

        return catalog2Map;
    }

                  @Cacheable 默认行为:

                         1)@Cacheable标注的方法执行时,先从缓存中取数据,若缓存中有数据,

                               则方法就不执行了,直接返回缓存的数据

                          2)@Cacheable  数据时默认是jdk 序列化格式,jdk序列化是 不能跨平台的

                          3) 缓存中的key 是自动生成的,

                                     格式:缓存名称::$simpleKey

                          4)@Cacheable 在缓存中的数据默认过期时间是-1,表示数据永不过期          。

      4、@Cacheable 自定义配置

            由上边 @Cacheable 默认行为可以发现,直接使用 @Cacheable 很多情况下并不能

           满足我们的要求,对于缓存中的数据我们希望有以下的配置:

                  1)为了数据能够跨平台,我们应该可以指定数据的序列化方式,

                       如:把数据序列化成json

                  2)为了数据的查询,我们应该能指定key 值 

                  3)我们应该能指定缓存数据的过期时间

          在配置@Cacheable 之前,我们先下@Ccheable的结构

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Cacheable {
    @AliasFor("cacheNames")
    String[] value() default {};

    //存储方法调用结果的缓存的名称(即缓存名称)。
    //每一个需要缓存的数据都来指定要存放到哪个名字的缓存(相当于缓存分区,一般建议按:业务类型来分区)
    @AliasFor("value")
    String[] cacheNames() default {};

    //指定缓存的key,若不指定则会通过 keyGenerator() 生成一个key
    String key() default "";

    //用于生成缓存中的key,默认是按EL正则表达式修改(也可以自己修改,但一般不需要自己修改)
    //生成key的EL表达式格式:缓存名称(即value 或 cacheNames 的值)::$simpleKey
    String keyGenerator() default "";
    //指定使用的目标缓存,值是目标缓存的CacheManager缓存管理器
    String cacheManager() default "";
    //值指向要使用的org.springframework.cache.interceptor.CacheResolver 的Bean的名称
    String cacheResolver() default "";
    //想把哪些数据放入缓存(把符合条件饿存缓存)
    String condition() default "";
    //除非我们指定的条件外其他都可以存缓存
    String unless() default "";

    //在高并发环境中,若有多个请求访问该方法,此时若 sync= false,则每个方法都会直接访问方法,
    //若 sync=true,则只有一个方法访问该方法然后把方法执行结果放回到缓存中,其他请求直接返回缓存
    //的结果
    boolean sync() default false;
}

          (1) 可以通过注解@Cacheable 的value 或 cacheNames  指定缓存的名称

          (2)在.properties配置文件中,全局配置缓存的过期时间,如:

                    spring.cache.redis.time-to-live=3600000

          (2)通过key属性指定缓存中的key,

                   注意:解析时 Cacheable 会把key的值看成一个EL表达式,如:

                              key = "#root.method.name" , "#root.method.name"是一个EL表达式(不是字                                符串),表示 以当前@Cacheable标注的方法名称作为缓存的key;

             示例代码如下:

          

//value : 每一个需要缓存的数据都来指定要存放到哪个名字的缓存(相当于缓存分区,一般建议按:业务类型来分区)
    //注意:key 的值是一个表达式,若想指定固定的key的值,可以使用嵌套字符串的格式,如:key = "'catalogJson'"
@Cacheable(value = {"catalogJson"},key = "#root.methodName")
    public Map<String, List<Catalog2VO>> getCatalogJsonFromCache() {

        //1、查询所有的三级分类数据
        List<CategoryEntity> selectList = this.baseMapper.selectList(null);
        //2、将所有的三级分类,按父分类parentId分组
        Map<Long, List<CategoryEntity>> categoryMap = selectList.stream().collect(Collectors.groupingBy(key -> key.getParentCid()));

        //3、查询1级分类
        List<CategoryEntity> level1Category = categoryMap.get(0L);

        //4、封装数据
        Map<String,List<Catalog2VO>> catalog2Map = level1Category.stream().collect(Collectors.toMap(key -> key.getCatId().toString(),level1 -> {
    // 、、、、、省略 。。。。。
        }));

        return catalog2Map;
    }

       5、给Spring Cache 指定序列化方式和超时时间

             在上边Redis 的自动配置类 

             org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration

             类中的 determineConfiguration 方法可以发现,

             若org.springframework.data.redis.cache.RedisCacheConfiguration

              实例不存在,则会使用默认的RedisCacheConfiguration(spring data) 的Bean

              如图所示: 

                 

               org.springframework.data.redis.cache.RedisCacheConfiguration 类结构如下:

               

package org.springframework.data.redis.cache;

import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Optional;
import java.util.function.Consumer;
import org.springframework.cache.interceptor.SimpleKey;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterRegistry;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext.SerializationPair;
import org.springframework.format.support.DefaultFormattingConversionService;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

public class RedisCacheConfiguration {
    private final Duration ttl;
    private final boolean cacheNullValues;
    private final CacheKeyPrefix keyPrefix;
    private final boolean usePrefix;
    private final SerializationPair<String> keySerializationPair;
    private final SerializationPair<Object> valueSerializationPair;
    private final ConversionService conversionService;

    private RedisCacheConfiguration(Duration ttl, Boolean cacheNullValues, Boolean usePrefix, CacheKeyPrefix keyPrefix, SerializationPair<String> keySerializationPair, SerializationPair<?> valueSerializationPair, ConversionService conversionService) {
        this.ttl = ttl;
        this.cacheNullValues = cacheNullValues;
        this.usePrefix = usePrefix;
        this.keyPrefix = keyPrefix;
        this.keySerializationPair = keySerializationPair;
        this.valueSerializationPair = valueSerializationPair;
        this.conversionService = conversionService;
    }

    //。。。。。。。。。。。。。。。省略 。。。。。。。。。。。。。。。

}

              在spring data redis 包下RedisCacheConfiguration 中可以设置缓存过期时间、前缀、

              是否保存null值、序列化方式等等。

             我们只要在项目中实例化一个 RedisCacheConfiguration(spring data) 的Bean,就可以

             在自定义的 RedisCacheConfiguration 指定序列化方式,示例代码如下:

        

@Configuration
public class MyCacheConfig {

    /**
     * 手动实例化 RedisCacheConfiguration 并指定序列化方式
     * @return
     */
    @Bean
    public RedisCacheConfiguration configuration(){
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
        //key序列化成String
        config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
        //value 序列化成json
        config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
        return config;
    }
}

     

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

我理解的文本表示模型

词袋模型与N-grams模型 1 词袋模型 (Bag of Words)1.1 one-hot 取值 (Binary)1.2 Term Frequency 取值 (TF)普通频数 r a w t f raw_{tf} rawtf​频率范数归一化对数频数 1.3 Inverse document frequency (IDF)1.4 TF-IDF scores 取值 N-Gram 最简单的文本建模场景&#xff1a…

openh264 宏块级码率控制源码分析

openh264 宏块级码率控制函数关系 宏块级核心函数分析 WelsRcMbInitGom函数 功能&#xff1a;openh264 码率控制框架中宏块级码率控制函数&#xff0c;根据是否启用GOM QP来决定如何设置宏块的QP值&#xff0c;以控制编码的质量和比特率。原理过程&#xff1a; 函数参数&…

数学-奇异值

有点名词党 奇异值的计算通常涉及矩阵的奇异值分解Singular Value Decomposition, SVD。奇异值分解是将一个矩形矩阵 ( A ) 分解为三个矩阵的乘积&#xff1a; [ A U ΣVT] 其中&#xff1a; - ( U ) 是一个 ( m m ) 的正交矩阵&#xff0c;它的列向量是 ( A AT) 的特征向…

稳定安全生产设备日志采集工具

免费试用下载: Gitee下载 最新版本 优势: A. 开箱即用. 解压直接运行.不需额外安装. B. 批管理设备. 设备配置均在后台管理. C. 无人值守 客户端自启动,自更新. D. 稳定安全. 架构简单,内存占用小,通过授权访问.

自研地面站!自主开源无人飞行系统 Prometheus V2 版重大升级详解

自主开源无人飞行系统 Prometheus V2 相对于 Prometheus V1 在多方面做了重大的升级&#xff0c;今天我们将聊聊 Prometheus V2 的地面站升级。 地面站的重大提升 熟悉 Prometheus 的小伙伴们可能知道&#xff0c;V1 版本是没有专门的地面站的。而在 Prometheus V2 中&#x…

【大模型驯化-Prompt】企业级大模型Prompt调试技巧与batch批量调用方法

【大模型驯化-Prompt】企业级大模型Prompt调试技巧 本次修炼方法请往下查看 &#x1f308; 欢迎莅临我的博客个人主页 &#x1f448;这里是我工作、学习、实践 IT领域、真诚分享 踩坑集合&#xff0c;智慧小天地&#xff01; &#x1f387; 免费获取相关内容文档关注&#x…

【ajax核心02】底层原理-Promise对象

目录 一&#xff1a;promise对象是什么 二&#xff1a;语法&#xff08;Promise使用步骤&#xff09; 三&#xff1a;Promise-三种状态 一&#xff1a;promise对象是什么 Promise 对象代表异步操作最终的完成&#xff08;或失败&#xff09;以及其结果值。 即Promise对象是…

番外篇 | YOLOv8算法解析和实战应用:车辆检测 + 车辆追踪 + 行驶速度计算

前言:Hello大家好,我是小哥谈。YOLOv8是ultralytics公司在2023年1月10号开源的,是YOLOv5的下一个重大更新版本,目前支持图像分类、物体检测和实例分割任务,在还没有开源时就收到了用户的广泛关注。它是一个SOTA模型,建立在以前YOLO版本的成功基础上,并引入了新的功能和改…

JVM的类加载机制

Java中类的加载阶段 类加载 Java中的类加载机制是Java运行时环境的一部分&#xff0c;确保Java类可以被JVM&#xff08;Java虚拟机&#xff09;正确地加载和执行。类加载机制主要分为以下几个阶段&#xff1a; 加载&#xff08;Loading&#xff09;&#xff1a;这个阶段&#x…

剑指offer 算法题(搜索二维矩阵)

剑指offer 第二题 去力扣里测试算法 思路一&#xff1a; 直接暴力遍历二维数组。 class Solution { public:bool searchMatrix(vector<vector<int>>& matrix, int target) {for (unsigned int i{ 0 }; i < matrix.size(); i){for (unsigned int j{ 0 };…

生信软件23 - Samtools和GATK去除PCR重复方法汇总

1. 为什么要去除重复&#xff1f; 在建库测序后&#xff0c; 加上接头的DNA片段进行PCR扩增&#xff08;由于连接flowcell的效率很低&#xff0c;所以需要对片段进行扩增&#xff09;&#xff0c;连接至flowcell上。PCR扩增会导致一个片段会测序多次&#xff0c;当该片段存在变…

Java学习笔记(二)变量原理、常用编码、类型转换

Hi i,m JinXiang ⭐ 前言 ⭐ 本篇文章主要介绍Java变量原理、常用编码、类型转换详细使用以及部分理论知识 🍉欢迎点赞 👍 收藏 ⭐留言评论 📝私信必回哟😁 🍉博主收将持续更新学习记录获,友友们有任何问题可以在评论区留言 1、变量原理 1.1、变量的介绍 变量是程…

Java中setLineWrap(true)和setWrapStyleWord(true)优化TextArea

在 Java Swing 开发中&#xff0c;JTextArea 是一个多行的文本区域组件&#xff0c;常用于显示和编辑大量文本。当处理长文本时&#xff0c;默认行为是不换行并且出现水平滚动条&#xff0c;这通常会降低用户体验。幸运的是&#xff0c;JTextArea 提供了两个非常有用的方法&…

如何卸载windows系统自带游戏

为了清晰地指导如何卸载Windows系统自带游戏&#xff0c;我们可以参考以下步骤进行&#xff1a; 方法一&#xff1a;通过控制面板卸载 打开控制面板进入程序和功能在控制面板中&#xff0c;找到并点击“程序和功能”。在程序列表中&#xff0c;找到你想要卸载的自带游戏。 方…

EtherCAT扫盲,都是知识点

1. 什么是EtherCAT EtherCAT&#xff0c;全称Ethernet for Control Automation Technology&#xff0c;字面意思就是用于控制自动化技术的以太网。它是一种基于以太网的实时工业通信协议&#xff0c;简单说&#xff0c;就是让机器们通过网线互相聊天的高级方式。 EtherCAT 是最…

数仓开发那些事_番外

一位神州的正式员工&#xff08;没错&#xff0c;就是之前文章中出现的实习生&#xff09;&#xff1a;一闪&#xff0c;你今年涨工资了吗&#xff1f; 一闪&#xff1a;mad&#xff0c;一年辛苦到头只涨了500米 神州员工&#xff1a;你去年绩效不是优秀吗&#xff0c;怎么就涨…

C语言基础关键字的含义和使用方法

​关键字在C语言中扮演着非常重要的角色&#xff0c;它们定义了语言的基本构造和语法规则&#xff0c;通过使用关键字&#xff0c;开发者可以创建变量、定义数据类型、控制程序流程&#xff08;如循环和条件判断&#xff09;、声明函数等。由于这些字是保留的&#xff0c;所以编…

手把手教你实现条纹结构光三维重建(3)——相机投影仪标定

我们都知道&#xff0c;投影仪其实就是个反向相机&#xff0c;如果我们了解双目标定的原理&#xff0c;那么相机和投影仪的标定就不难&#xff0c;关键是我们怎么得到投影仪在图像特征点&#xff08;比如棋盘格角点&#xff09;上的像素位置。 投影仪也类似于一个cmos&#xf…

IO读取properties文件实现JDBC连接池实战

参考文章 Java中的池化思想 面试官&#xff1a;为什么数据库连接很消耗资源&#xff0c;资源都消耗在哪里&#xff1f; 池化思想是什么&#xff1f;连接池是什么&#xff1f; 在Java中&#xff0c;池化思想是一种通过创建和管理可重复使用的对象池来提高性能和资源利用率的编…

【图解IO与Netty系列】Netty编解码器、TCP粘包拆包问题处理、Netty心跳检测机制

Netty编解码器、TCP粘包拆包问题处理、Netty心跳检测机制 Netty编解码器编码器解码器编解码器Netty提供的现成编解码器 TCP粘包拆包问题处理Netty心跳检测机制 Netty编解码器 网络传输是以字节流的形式传输的&#xff0c;而我们的应用程序一般不会直接对字节流进行处理&#x…