SpringBoot——Cache使用原理及Redis整合
前言及核心概念介绍
前言
本篇主要介绍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整合的更多相关文章
- SpringBoot日记——Redis整合
上一篇文章,简单记录了一下缓存的使用方法,这篇文章将把我们熟悉的redis整合进来. 那么如何去整合呢?首先需要下载和安装,为了使用方便,也可以做环境变量的配置. 下载和安装的方法,之前有介绍,在do ...
- SpringBoot缓存管理(二) 整合Redis缓存实现
SpringBoot支持的缓存组件 在SpringBoot中,数据的缓存管理存储依赖于Spring框架中cache相关的org.springframework.cache.Cache和org.spri ...
- SpringBoot+Redis整合
SpringBoot+Redis整合 1.在pom.xml添加Redis依赖 <!--整合Redis--> <dependency> <groupId>org.sp ...
- ssm+redis整合(通过cache方式)
这几天的研究ssm redis 终于进入主题了,今天参考了网上一些文章搭建了一下ssm+redis整合,特别记录下来以便以后可以查询使用,有什么不足请大牛们提点 项目架构 1.pom.xml < ...
- redis实现cache系统原理(五)
1. 介绍 cache就是人们所说的缓存.我们这里所说的cache是web上的.对用户来说,衡量一个网站是否具有良好的体验,其中一个标准就是响应速度的快慢.可能网站刚上线,功能还较少,数据库的记录也不 ...
- springboot和Redis整合
springboot简化了许多的配置,大大提高了使用效率.下面介绍一下和Redis整合的一些注意事项. 首先介绍单机版的redis整合. 1.第一步当然是导入依赖 <dependency> ...
- redis(七)---- SpringBoot和redis整合
SpringBoot和Redis整合非常简单 添加pom依赖 <dependency> <groupId>org.springframework.boot</groupI ...
- SpringCloud+MyBatis+Redis整合—— 超详细实例(二)
2.SpringCloud+MyBatis+Redis redis①是一种nosql数据库,以键值对<key,value>的形式存储数据,其速度相比于MySQL之类的数据库,相当于内存读写 ...
- springBoot cache操作2
版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/zxd1435513775/article/details/85091793一.基本项目搭建测试项目是 ...
随机推荐
- 学会使用数据讲故事——Excel研究网络研讨会
编者按:在数据密集型研究的新时代,Excel将成为研究者讲故事的强大工具.在即将举行的Excel研究网络研讨会中,我们将与你探讨如何用新的方式来寻找.查询.分析数据并实现数据可视化.Office 36 ...
- MySQL 之全文索引
最近在复习数据库索引部分,看到了 fulltext,也即全文索引,虽然全文索引在平时的业务中用到的不多,但是感觉它有点儿意思,所以花了点时间研究一下,特此记录. 引入概念通过数值比较.范围过滤等就可以 ...
- 3DSMAX卸载/完美解决安装失败/如何彻底卸载清除干净3DSMAX各种残留注册表和文件的方法
在卸载3dsmax重装3dsmax时发现安装失败,提示是已安装3dsmax或安装失败.这是因为上一次卸载3dsmax没有清理干净,系统会误认为已经安装3dsmax了.有的同学是新装的系统也会出现3ds ...
- 【阅读笔记】rocketmq 概念与架构 (一)
介绍 rocketmq 框架与基本概念 1. 概念 1.1 namesrv(name server) 记录了 broker 集群信息,消息队列的信息以及 key-value 配置,见 RouteInf ...
- npm install依赖时,常见错误
1.npm install依赖时,报错:npm ERR! Unexpected end of JSON input while parsing near '...gin":"^1. ...
- 数据检索|文献检索|事实检索|yandex|Title vs topic|检索技术|检索技巧|
信息检索: 信息检索原理是,将书写不规范的原始数据先存储,再通过归纳化or标准化手段进行拆分,便于用户搜索. 信息检索类型可依据数据内容进行分类,文献检索是通过输入关键字进入搜索引擎,搜索仅找到含有关 ...
- pycharm中进行全局搜索
- 吴裕雄--python学习笔记:通过sqlite3 进行文字界面学生管理
import sqlite3 conn = sqlite3.connect('E:\\student.db') print("Opened database successfully&quo ...
- Python知识点总结及其介绍链接
Python 弱引用(不会增加引用计数的引用,可以用来做对象缓存,避免循环引用导致内存无法回收):http://python.jobbole.com/85431/ from future import ...
- 码海拾遗:简述C++(一)
C++是Bjarne Stroustrup博士于1982年,在C语言的基础上引入并扩充了面向对象的概念后发明的一种新的程序语言.就与C语言的渊源而言,C++可以说是C语言的超集,它兼容C的一切(可能是 ...

