推荐文章阅读
Click me to read SpringBoot与缓存 ~ 概念、注解、整合Redis、分布式锁
Click me to read SpringBoot整合缓存 ~ SpringBoot缓存工作原理以及@Cacheable运行流程
Click me to read SpringBoot整合缓存 ~ 整合Redis缓存和序列化
前言
SpringBoot整合Redis的博客很多,大致套路都是
- 引入Pom
- 配置Redis
- 使用注解@RedisTemplate注解 然后redisTemplate.opsForValue(),redisTemplate.opsForList()等方法去操作
本篇主要从缓存概念、缓存注解、整合Redis三个方面讲,核心在于缓存注解,以及整合Redis和序列化机制。如果你是想学RedisTemplate Api, 建议可以绕到了,这里不会介绍详细使用API,更多的是一种知识普及。
缓存概念
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,这个灵活性更强 案例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;
}
}
原因在于: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);
}