SpringBoot缓存机制
一、SpringBoot缓存机制概念
1.JSR107的五个核心接口
- CachingProvider定义了创建、配置、获取、管理和控制多个CacheManager。一个应用可以在运行期访问多个CachingProvider。
- CacheManager定义了创建、配置、获取、管理和控制多个唯一命名的Cache,这些Cache存在于CacheManager的上下文中。CacheManager仅被一个CachingProvider所拥有。
- Cache是一个类似Map的数据结构并临时存储以Key为索引的值。一个Cache仅被一个CacheManager所拥有。
- Entry是一个存储在Cache中的key-value对。
- Expiry每一个存储在Cache中的条目有一个定义的有效期。一旦超过这个时间,条目为过期的状态。一旦过期,条目将不可访问、更新和删除。缓存有效期可以通过ExpiryPolicy设置。
2.Spring的抽象缓存
Spring自从3.1以后,定义了org.springframework.cache.Cache和org.springframework.cache.CacheManager接口来统一不同的缓存技术;并支持使用JCache(JSR-107)注解简化我们开发;
- Cache接口为缓存的组件规范定义,包含缓存的各种操作集合;
- Cache接口下Spring提供了各种xxxCache的实现;如RedisCache,EhCacheCache , ConcurrentMapCache等;
- 每次调用需要缓存功能的方法时,Spring会检查检查指定参数的指定的目标方法是否已经被调用过;如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓存结果后返回给用户。下次调用直接从缓存中获取。
3.几个重要概念及缓存注解
| Cache | 缓存接口,定义缓存操作。实现有:RedisCache、EhCacheCache、ConcurrentMapCache等 |
|---|---|
| CacheManager | 缓存管理器,管理各种缓存(Cache)组件 |
| @Cacheable | 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存 |
| @CacheEvict | 清空缓存 |
| @CachePut | 保证方法被调用,又希望结果被缓存。 |
| @EnableCaching | 开启基于注解的缓存 |
| keyGenerator | 缓存数据时key生成策略 |
| serialize | 缓存数据时value序列化策略 |
二、SpringBoot缓存机制操作
1.环境搭建
在SpringBoot快速初始化环境

2.创建数据库及建表
1 建表
/*
Navicat MySQL Data Transfer
Source Server : 本地
Source Server Version : 50528
Source Host : 127.0.0.1:3306
Source Database : springboot_cache
Target Server Type : MYSQL
Target Server Version : 50528
File Encoding : 65001
Date: 2018-04-27 14:54:04
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for department
-- ----------------------------
DROP TABLE IF EXISTS `department`;
CREATE TABLE `department` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`departmentName` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Table structure for employee
-- ----------------------------
DROP TABLE IF EXISTS `employee`;
CREATE TABLE `employee` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`lastName` varchar(255) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`gender` int(2) DEFAULT NULL,
`d_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2 建立实体类
public class Employee {
private Integer id;
private String lastName;
private String email;
private Integer gender; //性别 1男 0女
private Integer dId;
public Employee() {
super();
}
public Employee(Integer id, String lastName, String email, Integer gender, Integer dId) {
super();
this.id = id;
this.lastName = lastName;
this.email = email;
this.gender = gender;
this.dId = dId;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Integer getGender() {
return gender;
}
public void setGender(Integer gender) {
this.gender = gender;
}
public Integer getdId() {
return dId;
}
public void setdId(Integer dId) {
this.dId = dId;
}
@Override
public String toString() {
return "Employee [id=" + id + ", lastName=" + lastName + ", email=" + email + ", gender=" + gender + ", dId="
+ dId + "]";
}
}
public class Department {
private Integer id;
private String departmentName;
public Department() {
super();
// TODO Auto-generated constructor stub
}
public Department(Integer id, String departmentName) {
super();
this.id = id;
this.departmentName = departmentName;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getDepartmentName() {
return departmentName;
}
public void setDepartmentName(String departmentName) {
this.departmentName = departmentName;
}
@Override
public String toString() {
return "Department [id=" + id + ", departmentName=" + departmentName + "]";
}
}
3.配置mybatis
1 配置数据库参数
spring:
datasource:
#url: jdbc:mysql://localhost:3306/spring_cache,需要添加时区,否则会出现乱码
url: jdbc:mysql://localhost:3306/spring_cache?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
#开启驼峰命名
mybatis:
configuration:
map-underscore-to-camel-case: true
2 编写mapper
@Mapper
public interface EmpolyeeMapper {
@Insert("INSERT INTO employee (`lastName`,`email`,`gender`,`d_id`) values (#{lastName},#{email},#{gender},#{dId})")
public void save(Employee employee);
@Delete("DELETE FROM employee WHERE `id` = #{id}")
public void deleteById(Integer id);
@Update("UPDATE employee SET `lastName`=#{lastName},`email`=#{email},`gender`=#{gender},`d_id`=#{dId} WHERE id = #{id}")
public void update(Employee employee);
@Select("SELECT * FROM employee WHERE `id` = #{id}")
public Employee findById(Integer id);
}
3 编写service
@Service
public class EmployeeService {
@Autowired
private EmpolyeeMapper empolyeeMapper;
public void save(Employee employee) {
empolyeeMapper.save(employee);
}
public void deleteById(Integer id) {
System.out.println("删除"+id+"号员工");
empolyeeMapper.deleteById(id);
}
public void update(Employee employee) {
empolyeeMapper.update(employee);
}
public Employee findById(Integer id) {
System.out.println("查询"+id+"号员工");
return empolyeeMapper.findById(id);
}
}
4 编写Controller
@RestController
public class EmployeeController {
@Autowired
private EmployeeService service;
@GetMapping("/emp/{id}")
public Employee findById(@PathVariable Integer id) {
Employee emp = service.findById(id);
System.out.println(emp);
return emp;
}
@GetMapping("/emp")
public Employee save(Employee employee) {
service.save(employee);
return employee;
}
@GetMapping("/emp/del/{id}")
public String deleteById(@PathVariable Integer id) {
service.deleteById(id);
return "success";
}
@GetMapping("/emp/update")
public String update(Employee employee) {
service.update(employee);
return "redirect:/emp/#{id}";
}
}
4.使用Cache
1 在main方法上配置一个方法
@SpringBootApplication
@MapperScan("com.xiaoxiao.springboot.mapper")
//配置后自动开启缓存
@EnableCaching
public class SpringBootCache01Application {
public static void main(String[] args) {
SpringApplication.run(SpringBootCache01Application.class, args);
}
}
在yaml文件配置一下日志debug
logging:
level:
com.xiaoxiao.springboot.mapper: debug
2 在Service层配置缓存注解
@Cacheable(value = "emp",key = "#id") public Employee findById(Integer id) { System.out.println("查询"+id+"号员工"); return empolyeeMapper.findById(id); }
| 名字 | 位置 | 描述 | 示例 |
|---|---|---|---|
| methodName | root object | 当前被调用的方法名 | #root.methodName |
| method | root object | 当前被调用的方法 | #root.method.name |
| target | root object | 当前被调用的目标对象 | #root.target |
| targetClass | root object | 当前被调用的目标对象类 | #root.targetClass |
| args | root object | 当前被调用的方法的参数列表 | #root.args[0] |
| caches | root object | 当前方法调用使用的缓存列表(如@Cacheable(value={“cache1”, “cache2”})),则有两个cache | #root.caches[0].name |
| argument name | evaluation context | 方法参数的名字. 可以直接#参数名,也可以使用#p0或#a0 的形式,0代表参数的索引; | #iban、#a0 、#p0 |
| result | evaluation context | 方法执行后的返回值(仅当方法执行之后的判断有效,如‘unless’,’cache put’的表达式’cache evict’的表达式beforeInvocation=false) | #result |
3 Cache工作原理
@Cacheable
用法:将方法的结果进行返回,以map的形式存储,如果还要该数据就不用查库,直接从缓存中调用
属性:
cacheNames 数组,指定缓存名字,将返回值存入该缓存中
value 与cacheNames作用相同
key 缓存数据用的key,默认使用方法参数的值,这里默认参数是1.当然,可以指定key使用Spel,如下表所示
keyGenerator key生成器,可以自己设置key生成器的组件ID key和keyGenerator选一个使用
cacheManager cacheResolver选择一个使用
condition 符合条件的情况下进行缓存
unless 满足该条件就不缓存
sync 使用异步模式
查询源码之后可以发现
- CacheManager(ConcurrentMapCacheManager)按照名字获得Cache(ConcurrentMap)
- 默认生成key使用keyGenerator,默认使用方法参数作为key,如果没有参数,key=new SimpleKey();如果有一个参数那么key=这个参数,如果有多个参数,key=new SimpleKey(params)
- 如果用key查到缓存中有值,那就直接调用返回,否则就将key和方法返回值存入缓存中
4 CachePut注解
@CachePut
用法:修改数据库的内容,同时更新缓存,通常用于更新数据库
属性:
value 指定缓存的名字
key:查询用的Key,默认情况与Cacheable注解用法一样,所以更新时要注意用相同的key查询值,例如#emp.id或者#result.id
5 CacheEvict注解
@CacheEvict
用法:删除缓存中的数据
属性 和上面是一样的
allEntries:是否清空所有缓存内容,缺省为false,如果指定为true,则方法调用后将立即清空所有缓存
beforeInvocation:是否在方法执行前就清空,缺省为false,如果指定为true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存.如果是true,无论有没有异常都会删掉缓存
6 Caching组合查询
@Caching
用法:用来组合查询,多种注释组合
三、整合Redis作为缓存
1.docker运行redis
docker run -d -p 6379:6379 --name myredis docker.io/redis
2.在yaml文件中进行配置
spring:
redis:
host: 192.168.1.50
3.pom.xml中导入依赖文件
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>2.3.1.RELEASE</version>
</dependency>
4.常见的redis操作
1 操作模板
@Autowired
private StringRedisTemplate stringRedisTemplate;//用来操作字符串的
@Autowired
private RedisTemplate redisTemplate;//用来操作k-v的模板,k,v都可以是任意object
2 操作类型
redis中有五大数据类型
/**
* redis有常见的五种数据类型:String(字符串),List(列表),Set(集合),Hash(散列),ZSet(有序集合)
* stringRedisTemplate.opsForValue();//操作字符串
* stringRedisTemplate.opsForList();//操作列表
* stringRedisTemplate.opsForSet();//操作集合
* stringRedisTemplate.opsForHash();//操作散列
* stringRedisTemplate.opsForZSet();//操作有序集合
*/
3 保存自定义对象
将对象进行序列化后,可以看到存入redis的数据并不是json格式,原因是因为传入的是object类,解析不了该格式,所以可以自定义转成json格式
改变redis序列化的默认规则
public void afterPropertiesSet() {
super.afterPropertiesSet();
boolean defaultUsed = false;
//默认使用jdk的序列化规则
if (this.defaultSerializer == null) {
this.defaultSerializer = new JdkSerializationRedisSerializer(this.classLoader != null ? this.classLoader : this.getClass().getClassLoader());
}
那么我们可以自定义序列化规则
@Configuration
public class MyRedisConfig {
@Bean
public RedisTemplate<Object, Employee> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate<Object, Employee> template = new RedisTemplate();
template.setDefaultSerializer(new Jackson2JsonRedisSerializer<Employee>(Employee.class));
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
4 redis作为cache
引入redis的starter以后,springboot会帮我们创建一个RedisManager
RedisManager通过RedisCache进行操作缓存存入redis中
都是通过序列化保存,序列化规则默认是用的jdk
那么如果想要转成json数据,就要自定义规则,自己制定一个RedisManager
@Bean
public RedisCacheManager employeeCacheManager(RedisConnectionFactory redisConnectionFactory) {
//初始化一个RedisCacheWriter
RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
//设置CacheManager的值序列化方式为json序列化
RedisSerializer<Object> jsonSerializer = new GenericJackson2JsonRedisSerializer();
RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair
.fromSerializer(jsonSerializer);
RedisCacheConfiguration defaultCacheConfig=RedisCacheConfiguration.defaultCacheConfig()
.serializeValuesWith(pair);
//设置默认超过期时间是30秒
defaultCacheConfig.entryTtl(Duration.ofSeconds(30));
//初始化RedisCacheManager
return new RedisCacheManager(redisCacheWriter, defaultCacheConfig);
}
来源:https://www.jianshu.com/p/652f53bd9118
配置这个以后,无论是什么类型的对象都可以转换成json数据
版权声明:本文为qq_41522089原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。