一、Spring Cache介绍
Spring Cache是一个框架,实现了基于注解的缓存功能,只需要简单地加一个注解,就能实现缓存功能。
Spring Cache提供了一层抽象,底层可以切换不同的cache实现。具体就是通过CacheManager接口来统一不同的缓存技术。
CacheManager时spring提供的各种缓存技术抽象接口,针对不同的缓存技术需要实现不同的CacheManager:
案例代码:
package com.itheima.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.itheima.entity.User;
import com.itheima.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
/* 注意: 在使用Spring Cache框架实现注解方式的缓存功能时,其底层缓存技术产品为Redis
要求其所添加注解缓存的方法的返回值类型如果是自定义实体类则该实体类必须实现Serializable系列化接口,不然无法实现数据缓存!!!*/
@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {
/*当我们项目中没有导入相关缓存技术的依赖包时,其默认使用的缓存管理器实现是ConcurrentMapCacheManager,其底层是基于ConcurrentMap来缓存数据的
注意: ConcurrentMap集合其实是基于内存的,即当我们重启服务后该缓存中就没有数据了!!!(我们后期使用Redis缓存技术来实现缓存功能,便没有这种问题)*/
@Autowired
private CacheManager cacheManager;//Ctrl + Alt + B,快速查看CacheManager接口的实现类
@Autowired
private UserService userService;
/**
* CachePut:将方法返回值放入缓存
* value:缓存的名称(相当于一类缓存),每个缓存名称下面可以有多个key
* key:缓存的key
* 大多数时候key值都不会固定写死,而是动态编辑
* 这里的#user.id指的是sava()方法中的形参user,即用服务端接收前端传递的参数的user对象中的id值作为缓存的key, 动态获取
* * 像#user.id这样的表达式叫做 SpEL
*
* 前端发送对应Url请求时不需要传入id的值,因为在Controller()中完成数据的save操作后,mybatisplus会自动给其生成id,若手动赋值id,则以我们手动的为准
*
*/
@CachePut(value = "userCache",key = "#user.id")
@PostMapping
public User save(User user){//注意: 在使用Spring Cache实现了基于注解的缓存功能时,返回结果User这个类必须要实现Serializable系列化接口才行,不然无法实现缓存数据!!!
userService.save(user);
return user;
}
/**
* CacheEvict:清理指定缓存 , 将一条或多条数据从缓存中删除
* value:缓存的名称(相当于一类缓存),每个缓存名称下面可以有多个key
* key:缓存的key
* 多数时候key值都不会固定写死,而是动态编辑
* 这里的#id指的是delete()方法中的形参id,即用服务端接收前端传递的参数id值作为缓存的key, 动态获取
* * 像#id这样的表达式叫做 SpEL
*
* `@CacheEvict`注解中的allEntries参数:
* * allEntries:设置是否全部删除该value(缓存的名称,类似于代表一类缓存)下的所有缓存数据,ture代表全部删除,false代表不
*
* 常用的SpEL有:
* * #id:直接 # + 形参名 是最简单的获取参数值的方式!!!
* * #p0: 获取方法形参列表中的第一个参数,#p1获取第二个参数,依次类推...
* * #root.args[0]: 获取方法形参列表中的第一个参数,#root.args[1]获取第二个参数,依次类推...
*
* 类似的还有:
* * #result获取方法的返回值
* * #root.method获取方法对象
* * #root.methodName获取方法名称
* * 具体的可以看@CacheEvivt注解的源码!!!
*/
@CacheEvict(value = "userCache",key = "#p0")
//@CacheEvict(value = "userCache",key = "#root.args[0]")
//@CacheEvict(value = "userCache",key = "#id")
@DeleteMapping("/{id}")
public void delete(@PathVariable Long id){
userService.removeById(id);
}
//@CacheEvict(value = "userCache",key = "#p0.id")
//@CacheEvict(value = "userCache",key = "#user.id")
//@CacheEvict(value = "userCache",key = "#root.args[0].id")
@CacheEvict(value = "userCache",key = "#result.id")
@PutMapping
public User update(User user){//注意: 在使用Spring Cache实现了基于注解的缓存功能时,返回结果User这个类必须要实现Serializable系列化接口才行,不然无法实现缓存数据!!!
userService.updateById(user);
return user;
}
/**
* Cacheable:在方法执行前spring先查看缓存中是否有数据,如果有数据,则直接返回缓存数据,不再执行Controller()方法;
* 若缓存中没有数据,则再调用Controller()方法并将方法返回值存放到缓存中!!!
* value:缓存的名称(相当于一类缓存),每个缓存名称下面可以有多个key
* key:缓存的key
* 多数时候key值都不会固定写死,而是动态编辑
* 这里的#id指的是getById()方法中的形参id,即用服务端接收前端传递的参数id值作为缓存的key, 动态获取
* * 像#id这样的表达式叫做 SpEL
*
* `@Cacheable` 注解中有俩个比较实用的参数:
* * condition:条件,满足条件时才缓存数据
* * 注意:在condition参数中能使用的内置对象只能为#root、#root.methodName、#root.args[1]...不能使用#result作为内置对象!!!具体看Cacheable的源码
* * unless:满足条件时则不缓存数据
* * (例如该getById(),若前端页面传入的id在数据库中查询不到对应数据,此时方法返回结果为null,
* 但Cacheable注解仍会将其数据存入缓存中key为#id, 而value则直接以null存储,这显然不是我们想要的,故需添加condition或unless参数来进行条件判断是否缓存数据!!!)
*/
@Cacheable(value = "userCache",key = "#id",unless = "#result == null")
@GetMapping("/{id}")
public User getById(@PathVariable Long id){//注意: 在使用Spring Cache实现了基于注解的缓存功能时,返回结果User这个类必须要实现Serializable系列化接口才行,不然无法实现缓存数据!!!
User user = userService.getById(id);
return user;
}
//该list()通过条件查询返回结果,故参照方法实现中是通过id、name来组合查询,故这里的key动态设置为: "#user.id + '_' + #user.name"
@Cacheable(value = "userCache",key = "#user.id + '_' + #user.name")
@GetMapping("/list")
public List<User> list(User user){//注意: 在使用Spring Cache实现了基于注解的缓存功能时,返回结果User这个类必须要实现Serializable系列化接口才行,不然无法实现缓存数据!!!
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(user.getId() != null,User::getId,user.getId());
queryWrapper.eq(user.getName() != null,User::getName,user.getName());
List<User> list = userService.list(queryWrapper);
return list;
}
}
User.java实体类需要实现Serializable系列化接口!!!
package com.itheima.entity;
import lombok.Data;
import java.io.Serializable;
/**
* * 注意: 在使用Spring Cache框架实现注解方式的缓存功能时,
* * 要求其所添加注解缓存的方法的返回值类型如果是自定义实体类则该实体类必须实现Serializable系列化接口,不然无法实现数据缓存!!!
*/
@Data
public class User implements Serializable {//implements Serializable 实现系列化接口!!!
private static final long serialVersionUID = 1L;
private Long id;
private String name;
private int age;
private String address;
}
pom.xml中:
<!--在SpringBoot项目中使用Spring Cache(使用Redis缓存技术作为底层缓存产品,需要导入如下俩个依赖spring-boot-starter-cache、spring-boot-starter-data-redis)-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
Spring Cache框架使用Redis缓存技术作为Spring Cache的底层缓存产品: