SpringBoot与缓存 ~ 概念、注解、整合Redis、分布式锁

推荐文章阅读

Click me to read SpringBoot与缓存 ~ 概念、注解、整合Redis、分布式锁

Click me to read SpringBoot整合缓存 ~ SpringBoot缓存工作原理以及@Cacheable运行流程

Click me to read SpringBoot整合缓存 ~ 整合Redis缓存和序列化

Click me to download

前言

之 前 想 写 S p r i n g B o o t 高 级 篇 整 合 各 种 技 术 的 , 一 直 拖 着 没 写 , 现 在 给 大 家 补 上 抱 歉 \color{red}之前想写SpringBoot高级篇整合各种技术的,一直拖着没写,现在给大家补上抱歉 SpringBoot

SpringBoot整合Redis的博客很多,大致套路都是

  1. 引入Pom
  2. 配置Redis
  3. 使用注解@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只能二选一,从定义上来看就知道了 keykeyGenerator
案例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);
    }

猜你喜欢

转载自blog.csdn.net/xiaozhegaa/article/details/110039459