推荐文章阅读
Click me to read SpringBoot与缓存 ~ 概念、注解、整合Redis、分布式锁
Click me to read SpringBoot整合缓存 ~ SpringBoot缓存工作原理以及@Cacheable运行流程
Click me to read SpringBoot整合缓存 ~ 整合Redis缓存和序列化
前言
之 前 想 写 S p r i n g B o o t 高 级 篇 整 合 各 种 技 术 的 , 一 直 拖 着 没 写 , 现 在 给 大 家 补 上 抱 歉 \color{red}之前想写SpringBoot高级篇整合各种技术的,一直拖着没写,现在给大家补上抱歉 之前想写SpringBoot高级篇整合各种技术的,一直拖着没写,现在给大家补上抱歉
SpringBoot整合Redis的博客很多,大致套路都是
- 引入Pom
- 配置Redis
- 使用注解@RedisTemplate注解
然后redisTemplate.opsForValue(),redisTemplate.opsForList()等方法去操作
如
果
是
让
你
去
整
合
R
e
d
i
s
,
按
照
这
些
文
章
操
作
时
没
有
任
何
问
题
。
\color{#f6941d}如果是让你去整合Redis,按照这些文章操作时没有任何问题。
如果是让你去整合Redis,按照这些文章操作时没有任何问题。
但
是
你
会
“
知
其
然
而
不
知
所
以
然
"
,
可
能
你
会
整
合
,
但
是
你
并
不
明
白
期
间
的
一
些
细
节
点
\color{#f6941d}但是你会“知其然而不知所以然", 可能你会整合,但是你并不明白期间的一些细节点
但是你会“知其然而不知所以然",可能你会整合,但是你并不明白期间的一些细节点
本篇主要从缓存概念、缓存注解、整合Redis三个方面讲,核心在于缓存注解,以及整合Redis和序列化机制。如果你是想学RedisTemplate Api, 建议可以绕到了,这里不会介绍详细使用API,更多的是一种知识普及。
缓存概念
作
为
过
来
人
,
很
多
人
在
学
习
时
,
更
多
倾
向
于
如
何
使
用
具
体
的
技
术
,
缺
忽
略
了
理
念
。
\color{red}作为过来人,很多人在学习时,更多倾向于如何使用具体的技术,缺忽略了理念。
作为过来人,很多人在学习时,更多倾向于如何使用具体的技术,缺忽略了理念。
要
知
道
一
句
话
:
天
上
飞
的
理
论
,
必
然
有
落
地
的
实
现
产
品
,
缓
存
是
理
念
,
r
e
d
i
s
是
它
的
落
地
产
品
\color{red}要知道一句话:天上飞的理论,必然有落地的实现产品,缓存是理念,redis是它的落地产品
要知道一句话:天上飞的理论,必然有落地的实现产品,缓存是理念,redis是它的落地产品
落
地
产
品
并
不
只
有
这
一
款
,
十
几
甚
至
几
十
款
,
只
有
掌
握
这
个
理
论
,
才
能
举
一
反
三
,
更
好
的
理
解
落
地
产
品
\color{red}落地产品并不只有这一款,十几甚至几十款,只有掌握这个理论,才能举一反三,更好的理解落地产品
落地产品并不只有这一款,十几甚至几十款,只有掌握这个理论,才能举一反三,更好的理解落地产品
Java Caching ~ JSR107规范
Java Caching定义了5个核心接口,分别是CachingProvider,CachingManager、 Cache、 Entry 和 Expiry
- CachingProvider定义了创建、配置、获取、管理和控制多个CacheManager。一个应用可以在运行期访问多个CachingProvider。
- CacheManager定义了创建、配置、获取、管理和控制多个唯一命名的Cache,这些Cache存在于CacheManager的上下文中。一个CacheManager仅被一个CachingProvider所拥有
- Cache是一个类似Map的数据结构并临时存储以Key为索引的值。一个Cache仅被一个CacheManager所拥有
- Entry是一个存储在Cache中的key-value对
- Expiry 每一个存储在Cache中的条目有一个定义的有效期。一旦超过这个时间,条目为过期
的状态。一旦过期,条目将不可访问、更新和删除。缓存有效期可以通过ExpiryPolicy设置
【举例】换成具体的实例理解
缓存注解
项目准备
SpringBoot整合mybatis-plus,过程就不演示了,直接上代码
@Cacheable 根据方法的请求参数对其结果进行缓存
若想使用缓存注解,必须开启缓存,在启动类上加上注解:@EnableCaching
案例1: 不使用缓存时,重复查询员工信息与加上缓存注解时重复查询员工信息
@Cacheable(cacheNames = "emp")
public Employee get(Integer id){
return employeeMapper.selectById(id);
}
@cachable 注解属性讲解
cacheNames/value:指定缓存组件的名字,支持多个名字,必须填写的。例如员工缓存组件、部门缓存组件,也可以同时指定
@Cacheable(cacheNames = {"emp", "department"})
key: 指定缓存数据使用的Key,默认是使用方法参数的值,1-方法的返回值
支持SPEL: #id, 参数id的值 #a0 、#p0 、 #root.argsp[0]
案例2:若指定key为1,则第一次查库后,不管查询id为多少,都不查库,直接查缓存,说明就是用默认方法作为key
@Cacheable(cacheNames = {"emp", "department"}, key = "1")
@Cacheable(cacheNames = {"emp", "department"}, key = #id)
keyGenerator:指定缓存生成器,用于生成key,这个灵活性更强
【
注
意
】
k
e
y
和
k
e
y
G
e
n
e
r
a
t
o
r
只
能
二
选
一
,
从
定
义
上
来
看
就
知
道
了
\color{red}【注意】key和keyGenerator只能二选一,从定义上来看就知道了
【注意】key和keyGenerator只能二选一,从定义上来看就知道了
案例3:
/**
* @author xiaozheng
* @version 1.0
* @date 2020/11/24 15:33
*/
@Configuration
public class MyCacheConfig {
@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 = {"emp", "department"}, keyGenerator = "myKeyGenerator")
public Employee get(Integer id){
return employeeMapper.selectById(id);
}
直接打断点就可以看到,生成key为随机数,发现都会查询数据库了
condition:支持SPEL表达式,当满足该条件的时候才缓存,否则不缓存
案例4:
@Cacheable(cacheNames = {"emp", "department"}, keyGenerator = "myKeyGenerator",
condition = "#id > 1"
)
观察现象
unless: 否定缓存,当unless条件为true时,不缓存,若unless条件为false才缓存
案例5:
// 若查询的id大于1,且id小于5的,才会缓存
@Cacheable(cacheNames = {"emp", "department"}, keyGenerator = "myKeyGenerator",
condition = "#id > 1", unless = "#id > 5"
)
sync:是否使用异步,这个不讲
以@Cacheable为例,看一下缓存的工作原理
Click me to study SpringBoot整合缓存 ~ SpringBoot缓存工作原理以及@Cacheable运行流程,强烈推荐哦
@CachePut 更新数据并且将结果缓存
案例1:
1、查询1号员工,将查询结果放入缓存
2、以后查询还是原来的结果
3、更新1号员工,也将结果缓存
4、重新查询1号员工,查询时之前的数据还是新的数据呢?
// controller
/**
* @author xiaozheng
* @version 1.0
* @date 2020/11/24 14:48
*/
/**
* @author xiaozheng
* @version 1.0
* @date 2020/11/24 14:48
*/
@RestController
@RequestMapping("/emp")
public class EmployeeController {
@Autowired
private EmployeeService employeeService;
@RequestMapping("/get/{id}")
public Employee get(@PathVariable("id") Integer id){
return employeeService.get(id);
}
@RequestMapping("/update")
public Employee update(Integer id, String lastName, Integer geneder, String email){
Employee employee = new Employee();
employee.setLastName(lastName);
employee.setId(id);
employee.setEmail(email);
employee.setGender(geneder);
return employeeService.update(employee);
}
}
// service
/**
* @author xiaozheng
* @version 1.0
* @date 2020/11/24 14:50
*/
@Service
public class EmployeeService {
@Autowired
private EmployeeMapper employeeMapper;
// 若查询的id大于1,且id小于5的,才会缓存
@Cacheable(cacheNames = {"emp"})
public Employee get(Integer id){
return employeeMapper.selectById(id);
}
@CachePut(cacheNames = {"emp"})
public Employee update(Employee employee){
employeeMapper.updateById(employee);
return employee;
}
}
结
果
:
查
询
的
是
没
更
新
前
的
数
据
\color{red}结果: 查询的是没更新前的数据
结果:查询的是没更新前的数据
原因在于:key不一样
若指定:
@CachePut(value = {"emp"}, key="#result.id")
http://localhost:8080/emp/get/1
http://localhost:8080/emp/update?id=1&lastName=2&geneder=150&email=1
若换种操作,将controller改为:
@RequestMapping("/update")
public Employee update(Integer id, String lastName, Integer geneder, String email){
Employee employee = employeeService.get(id);
employee.setLastName(lastName);
employee.setId(id);
employee.setEmail(email);
employee.setGender(geneder);
return employeeService.update(employee);
}
先去缓存找,然后再更新,它就会同步更新缓存,大家可以试试哦
@CacheEVict 请求缓存
@CacheEVict 注解属性讲解
value和cacheName指定定缓存组件
key:指定需要删除的key
allEntries: 请求该缓存组件中的所有缓存数据
beforeInvocation: 指定缓存的清除是否在方法之前,默认是false,若方法执行异常,则不会清除缓存,若设为true,不管方法执行结果如何,都清除缓存
案例1:测试效果
*/
@RestController
@RequestMapping("/emp")
public class EmployeeController {
@Autowired
private EmployeeService employeeService;
@RequestMapping("/get/{id}")
public Employee get(@PathVariable("id") Integer id){
return employeeService.get(id);
}
@RequestMapping("/delete/{id}")
public Integer delete(@PathVariable("id") Integer id){
employeeService.delete(id);
return id;
}
@RequestMapping("/update")
public Employee update(Integer id, String lastName, Integer geneder, String email){
Employee employee = employeeService.get(id);
employee.setLastName(lastName);
employee.setId(id);
employee.setEmail(email);
employee.setGender(geneder);
return employeeService.update(employee);
}
}
/**
* @author xiaozheng
* @version 1.0
* @date 2020/11/24 14:50
*/
@Service
public class EmployeeService {
@Autowired
private EmployeeMapper employeeMapper;
// 若查询的id大于1,且id小于5的,才会缓存
@Cacheable(value = {"emp"})
public Employee get(Integer id){
return employeeMapper.selectById(id);
}
@CachePut(value = {"emp"}, key="#result.id")
public Employee update(Employee employee){
employeeMapper.updateById(employee);
return employee;
}
@CacheEvict(value = {"emp"})
public void delete(Integer id){
System.out.println("删除s数据");
}
}
核心代码:
@CacheEvict(value = {"emp"})
案例2:
@CacheEvict(value = {"emp"}, allEntries = true)
案例3:
@CacheEvict(value = {"emp"}, allEntries = true, beforeInvocation = true)
public void delete(Integer id){
System.out.println("删除s数据");
int i = 10 / 0 ;
}
@Caching 注解
若一个方法的缓存规则比价复杂时,可用该注解
// @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);
}