前言及核心概念介绍

前言

本篇主要介绍SpringBoot2.x 中 Cahe 的原理及几个主要注解,以及整合 Redis 作为缓存的步骤

核心概念

先来看看核心接口的作用及关系图:

CachingProvider  管理并创建CacheManager,一个CachingProvider可以管理多个CacheManager

CacheManager  管理并创建Cache,一个CacheManager管理多个Cache

Cache  结构类似于Map<CacheName,Cache>,每个Cache有唯一的名字

Entry 结构类似于Map<KeyObject,ValueObject>,以键值对的形式储存在Cache中

Expiry  Cache中每个条目都有有效期,过期则会被删除或更新


一、SpringBoot中的缓存结构:

要知道SpringBoot是通过XXXAutoConfiguration来向容器中注册组件的

所以只要知道CacheAutoConfiguration注册了哪些组件,我们就能入手进行分析

找到添加的组件

1、首先进入CacheAutoConfiguration
  可以看到其导入了CacheConfigurationImportSelector
  从名字可以看出它是用来导入缓存配置类的

2、进入CacheConfigurationImportSelector
  这是一个静态内部类,只有一个selectImports方法,方法的最后将字符串数组返回
  我们在方法上打上断点进行测试


3、执行完第二步的方法,直接查看最终的返回结果
  可以看到返回了很多XXXCacheConfiguration


4、在配置文件中添加 debug=true
  要想知道到底用了哪个CacheConfiguration,我们可以在配置文件中添加 debug=true 来查看详细的日志
  启动应用,在日志中搜索CacheConfiguration,会发现只有SimpleCacheConfiguration是matched
  而其他的XXXCacheConfiguration都是Did not match
  结论:springboot默认使用SimpleCacheConfiguration作为缓存配置类  
找到了配置类,顺着配置类一层层进入,就能很快了解其中的结构


查看缓存结构
  1、进入默认配置类SimpleCacheConfiguration
  发现配置类中注册了ConcurrentMapCacheManager作为CacheManager
  注意:@ConditionalOnMissingBean(CacheManager.class)注解
当容器中存在CacheManager时,本配置类就不会生效,而CacheManager是通过配置类创建的,也就是说,如果选择了
其他的XXXCacheConfiguration,就会生成其他的CacheManager,本配置类就不会起作用。
这也是我们后面导入Redis的startor后就会自动使用RedisCacheConfiguration的原因

  

2、进入ConcurrentMapCacheManager
  cacheMap正是ConcurrentMapCacheManager管理的Cache结构


3、通过调试,找到这里的Cache实现类为ConcurrentMapCache
  其中两个属性,name为cache的名字,store用于储存键值对


到此为止,springboot的默认cache结构就出来了,接下来看看我们实现缓存功能需要的常用注解以及他们要注意的地方

二、几个关键注解

  1、@Cacheable

    标注在方法上,将方法返回的结果存入缓存中

    可以指定cachename或value来对ConcurrentMapCache的name属性进行设置

    也可以通过指定keyGenerator来制定缓存键值对中生成key的规则 

    默认情况:name为传入的参数,键值对中的值为方法返回结果 

  2、@CachePut

    标注在方法上,先执行方法,再用方法返回的值来更新缓存内容

  3、@CacheEvict

    清除缓存

  4、@Caching

    复杂的cache配置,可以在里面配置上面的几个注解

  5、@CacheConfig

    标注在类上,对类中的缓存操作作统一配置

三、@Cacheable工作原理

  下来通过几个重要的方法来展示@Cacheable工作原理

  这里的测试方法将从数据库中获取1号员工的数据,方法上只标注了@Cacheable

1、第一次查询

1、第一个重要方法:ConcurrentMapCacheManager.getCache(String name) 方法具体如下图

 在ConcurrentMapCacheManager中,查看cacheMap中是否存在名为emp的Cache

 如果存在则返回这个cache,如果不存在,就以传入的name作为cache的name创建并返回

 这里我们是不存在的,所以创建并返回一个名为emp的cache

2、由于是第一次查询,缓存中肯定是不存在任何员工的内容的,

所以接下来还是会执行真正的查询方法,调用数据库操作

3、返回结果之后,调用前面创建的cache,并调用其put方法,把员工id,员工信息,以键值对的方式存入cache中

2、第二次查询

有了上面的查询,1号员工的信息已经被缓存起来了

接下来看看再次查询1号员工会发生什么

1、首先还是进入 ConcurrentMapCacheManager 的 getCache 方法查找 cache

  因为第一次的操作,cacheMap中存在名为emp的cache,所以直接返回cache

2、接下来调用cache的lookup方法,通过键查找值

3、再接着方法将查找到的值返回,然后就直接结束了,没有调用实际的数据库操作

3、总结

在第一次查询时,会创建cache,然后调用方法,最后将方法的返回值存入cache

这样在查找相同内容时就直接从cache中获取,无需调用方法操作数据库来查找

四、整合Redis

1、加入redis的startor,springboot会自动识别并使用RedisCacheConfiguration,具体原因上面有提到

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2、开启Redis服务(可以使用docker)

3、创建配置类,配置RedisCacheManager(配置序列号方式等属性)

package com.tlj.cache.config;

import com.tlj.cache.bean.Department;
import com.tlj.cache.bean.Employee;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.redis.cache.CacheKeyPrefix;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext; import java.time.Duration; @Configuration
public class RedisConfig { // @Bean
// public RedisTemplate<Object, Employee> empRedisTemplate(
// RedisConnectionFactory redisConnectionFactory){
// RedisTemplate<Object,Employee> template=new RedisTemplate<Object,Employee>();
// template.setConnectionFactory(redisConnectionFactory);
// Jackson2JsonRedisSerializer<Employee> redisSerializer=new Jackson2JsonRedisSerializer<Employee>(Employee.class);
// template.setDefaultSerializer(redisSerializer);
// return template;
// } // @Bean
// public RedisCacheConfiguration redisCacheConfiguration(){
// Jackson2JsonRedisSerializer<Employee> Jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Employee>(Employee.class);
// RedisSerializationContext.SerializationPair<Employee> pair = RedisSerializationContext.SerializationPair.fromSerializer(Jackson2JsonRedisSerializer);
// return RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair);
// } /**
* 或者直接跳过RedisCacheConfiguration创建RedisCacheManager
* (在多个manager的情况下可以在@CacheConfig指定)
* @param redisConnectionFactory
* @return
*/
@Primary//多个Manager时需要设置
@Bean
public RedisCacheManager empCacheManager(RedisConnectionFactory redisConnectionFactory){
//初始化一个RedisCacheWriter
RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
//设置CacheManager的值序列化方式为Jackson2JsonRedisSerializer
Jackson2JsonRedisSerializer<Employee> Jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Employee>(Employee.class);
RedisSerializationContext.SerializationPair<Employee> pair = RedisSerializationContext.SerializationPair.fromSerializer(Jackson2JsonRedisSerializer);
RedisCacheConfiguration defaultCacheConfig=RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair);
//设置默认超过期时间是30秒
defaultCacheConfig.entryTtl(Duration.ofSeconds(30));
//初始化RedisCacheManager
RedisCacheManager cacheManager = new RedisCacheManager(redisCacheWriter, defaultCacheConfig);
return cacheManager;
} @Bean
public RedisCacheManager deptCacheManager(RedisConnectionFactory redisConnectionFactory){
//初始化一个RedisCacheWriter
RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
//设置CacheManager的值序列化方式为Jackson2JsonRedisSerializer
Jackson2JsonRedisSerializer<Department> Jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Department>(Department.class);
RedisSerializationContext.SerializationPair<Department> pair = RedisSerializationContext.SerializationPair.fromSerializer(Jackson2JsonRedisSerializer);
RedisCacheConfiguration defaultCacheConfig=RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair);
//设置默认超过期时间是30秒
defaultCacheConfig.entryTtl(Duration.ofSeconds(30));
//初始化RedisCacheManager
RedisCacheManager cacheManager = new RedisCacheManager(redisCacheWriter, defaultCacheConfig);
return cacheManager;
}
}

RedisConfig

4、在对应的类上指定对应的RedisCacheManager,类似下图

SpringBoot——Cache使用原理及Redis整合的更多相关文章

  1. SpringBoot日记——Redis整合

    上一篇文章,简单记录了一下缓存的使用方法,这篇文章将把我们熟悉的redis整合进来. 那么如何去整合呢?首先需要下载和安装,为了使用方便,也可以做环境变量的配置. 下载和安装的方法,之前有介绍,在do ...

  2. SpringBoot缓存管理(二) 整合Redis缓存实现

    SpringBoot支持的缓存组件 在SpringBoot中,数据的缓存管理存储依赖于Spring框架中cache相关的org.springframework.cache.Cache和org.spri ...

  3. SpringBoot+Redis整合

    SpringBoot+Redis整合 1.在pom.xml添加Redis依赖 <!--整合Redis--> <dependency> <groupId>org.sp ...

  4. ssm+redis整合(通过cache方式)

    这几天的研究ssm redis 终于进入主题了,今天参考了网上一些文章搭建了一下ssm+redis整合,特别记录下来以便以后可以查询使用,有什么不足请大牛们提点 项目架构 1.pom.xml < ...

  5. redis实现cache系统原理(五)

    1. 介绍 cache就是人们所说的缓存.我们这里所说的cache是web上的.对用户来说,衡量一个网站是否具有良好的体验,其中一个标准就是响应速度的快慢.可能网站刚上线,功能还较少,数据库的记录也不 ...

  6. springboot和Redis整合

    springboot简化了许多的配置,大大提高了使用效率.下面介绍一下和Redis整合的一些注意事项. 首先介绍单机版的redis整合. 1.第一步当然是导入依赖 <dependency> ...

  7. redis(七)---- SpringBoot和redis整合

    SpringBoot和Redis整合非常简单 添加pom依赖 <dependency> <groupId>org.springframework.boot</groupI ...

  8. SpringCloud+MyBatis+Redis整合—— 超详细实例(二)

    2.SpringCloud+MyBatis+Redis redis①是一种nosql数据库,以键值对<key,value>的形式存储数据,其速度相比于MySQL之类的数据库,相当于内存读写 ...

  9. springBoot cache操作2

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/zxd1435513775/article/details/85091793一.基本项目搭建测试项目是 ...

随机推荐

  1. django框架中的cookie与session

    cookie因为http是一个无状态协议,无法记录用户上一步的操作,所以需要状态保持.cookie和session的区别:1.cookie是保存在浏览器本地的,所以相对不安全.cookie是4k的大小 ...

  2. 【Java集合】试读ArrayList源码

    ArrayList简介 ArrayList 是一个数组队列,相当于 动态数组.与Java中的数组相比,它的容量能动态增长.它继承于AbstractList,实现了List, RandomAccess, ...

  3. SQL中的一些关键字用法

    1.where 条件筛选结果 select * from `表名` where `列名`='value' 上诉语句的意思是在某表中查询某列名等于某特定值得所有列 2.Like 模糊查询 select ...

  4. OpenCV 读取、修改、保存图像

    代码如下: #include <cv.h> #include <highgui.h> using namespace cv; int main( int argc, char* ...

  5. 使用JavaScript获取前一周的日期

    在开发当中遇到了一个关于echarts初始展示当前前7天的数据,正好记录一下如何获取前"n"天的日期, 返回时间格式:2020-02-02 // 返回前number天的日期格式为2 ...

  6. python爬虫心得(第一天)

    爬虫是什么? 我个人觉得用简单通俗的话来说就是在浏览网页的过程中将有价值的信息下载到本地硬盘或者是储存到数据库中的行为. 爬虫的基础认知 可以参考此链接:https://www.imooc.com/a ...

  7. 吴裕雄--天生自然python学习笔记:Python MySQL - mysql-connector 驱动

    本章节我们为大家介绍使用 mysql-connector 来连接使用 MySQL, mysql-connector 是 MySQL 官方提供的驱动器. 我们可以使用 pip 命令来安装 mysql-c ...

  8. 将js进行到底:node学习5

    HTTP开发之Connect工具集--中间件 继学习node.js的TCP API和HTTP API之后,node.js web开发进入了正轨,但这就好像Java的servlet一样,我们不可能使用最 ...

  9. Selenium的Web自动化测试(送源码)

    8.1  Selenium自动化测试准备 1.Selenium介绍 Selenium是一个Web开源自动化测试框架,页面级操作,模拟用户真实操作,API从系统层面触发事件. Selenium 1.0  ...

  10. mongodb 4.0配置认证模块

    use admin db.createUser({user:"root",pwd:"xxx",roles:[{role:"root",db: ...