SpringBoot专题学习Part26:Spring Cache整合Redis实现缓存及SpringBoot 2.x新版本自定义CacheManager
Spring Cache默认使用的是ConcurrentMapCacheManager管理器
创建的是ConcurrentMapCache缓存 然后将数据保存在ConcurrentMap中
实际使用缓存中间件的场合会更多 例如Redis EhCache等
Redis是一个高性能的支持网络 可基于内存亦可持久化的日志型 Key-Value数据库
Spring Cache默认使用的是SimpleCacheConfiguration这个配置类
当然 还有其他很多配置类(默认是不开启的
当导入其他相应的缓存场景即可自动开启对应的缓存配置
一、配置
首先 如果机器上还没装Redis 可使用Docker安装Redis
# 拉取最新redis镜像
docker pull docker.mirrors.ustc.edu.cn/library/redis
#启动redis容器
docker run -d -p 6379:6379 --name redis1 docker.mirrors.ustc.edu.cn/library/redis
当然自己安装Redis然后配置也可
然后 在项目中引入redis的starter(启动器):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
然后在配置文件中配置Redis的主机ip:
spring.redis.host=111.111.111.111(主机ip)
当引入了Redis的starter(启动器)之后 RedisAutoConfiguration这个配置类就启动了
底层配置了RedisTemplate和StringRedisTemplate 这两个Template类是用于操作Redis的 就类似于JDBC的JdbcTemplate
若要使用 只需注入即可
StringRedisTemplate 顾名思义 是操作字符串的
而RedisTemplate是用来操作对象的
二、使用
Redis常用五大数据类型:String字符串 List列表 Set集合 Hash散列 ZSet有序集合
Template都有对应的方法stringRedisTemplate.opsForValue()
:操作k-v字符串stringRedisTemplate.opsForList()
:操作列表stringRedisTemplate.opsForSet()
:操作集合stringRedisTemplate.opsForHash()
:操作散列stringRedisTemplate.opsForZSet()
:操作有序集合
redisTemplate里也有这些方法
1、字符串操作
// 添加key-value
stringRedisTemplate.opsForValue().append("msg","HelloWorld");
// 通过key获取指定的value
System.out.println(stringRedisTemplate.opsForValue().get("msg"));
// 往list左侧添加
stringRedisTemplate.opsForList().leftPush("mylist","1");
stringRedisTemplate.opsForList().leftPush("mylist","2");
stringRedisTemplate.opsForList().leftPush("mylist","3");
具体语法和Redis的是一样的 不过多阐述
2、对象操作
Employee employee = employeeMapper.findEmployeeById(1);
redisTemplate.opsForValue().set("emp1",employee);
此时 会报错:Caused by: java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [net.zjitc.springboot.bean.Employee]
因为 需要传入可序列化的类 而存入的类不可被序列化 因此报错
需要在实体类上实现Serializable接口 Serializable只是一个声明 代表该类是可以被序列化的 仅此而已
public class Employee implements Serializable {
private Integer id;
private String lastName;
private String email;
private Integer gender;
private Integer dId;
...
}
这样即可将对象正常 存入Redis了
储存乱码问题:
默认保存对象使用的是JDK序列化机制 将序列化后的数据保存到Redis中
因而保存后的数据会多了很多诸如\xAC\xED\x00\x05sr\x00
这样的乱码
平时的使用的话 通常是将数据以JSON的形式保存
有两种方法可以实现转换:
- 1、自己将对象转为JSON 有很多转换工具可供选择 例如Fastjson
- 2、使用RedisTemplate默认的序列化规则
在RedisTemplate中 定义了很多序列化器 默认使用的是JdkSerializationRedisSerializer
只需要改变其为JSON的序列化器 即可
自定义一个Redis的配置类:
@Configuration
public class MyRedisConfig {
@Bean
public RedisTemplate<Object, Employee> empRedisTemplate(
RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate<Object, Employee> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
// 传入要转换为JSON的类的class
Jackson2JsonRedisSerializer<Employee> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Employee>(Employee.class);
template.setDefaultSerializer(jackson2JsonRedisSerializer);
return template;
}
}
配置好之后 在类里注入即可使用:
// 注入
@Autowired
RedisTemplate<Object, Employee> empRedisTemplate;
// 使用
Employee employee = employeeMapper.findEmployeeById(1);
empRedisTemplate.opsForValue().set("emp1",employee);
完美
三、自定义CacheManager
其实原理就是CacheManager缓存管理器创建出Cache缓存组件
然后由Cache缓存组件来进行实际的CRUD操作
引入Redis的starter启动器之后 经过内部的判断 容器中就会自动使用RedisCacheManager(Redis缓存管理器)
RedisCacheManager创建了RedisCache作为缓存组件 该缓存组件是通过操作Redis来缓存数据
而这一切 都是自动配置好的
Just enjoy it
But…
在使用SpringCache缓存抽象的注解来实现缓存时 若引入了Redis的starter(启动器)
则@Cacheable缓存数据会自动储存到Redis中
然鹅 默认还是用的是JDK序列化 储存到Redis中的还是一堆乱码!
那么 如何储存为JSON数据?
在引入了Redis的starter之后 默认使用的CacheManager是RedisCacheManager
默认创建的RedisCacheManager操作Redis的时候使用的是RedisTemplate<Object,Object>
请注意 泛型是Object,Object
而这个默认的RedisTemplate<Object,Object>
使用的序列化机制正是JdkSerializationRedisSerializer 它会导致存储(中文)乱码
此时 需要自定义CacheManager
在配置类中往容器里添加一个CacheManager:
// 定制缓存管理器规则
@Bean
public CacheManager cacheManager(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);
//设置默认超时过期时间
// defaultCacheConfig.entryTtl(Duration.ofSeconds(30));
return new RedisCacheManager(redisCacheWriter, defaultCacheConfig);
}
默认的CacheManager生效条件是当容器中没有CacheManager 因此 自己编写的CacheManager一旦添加到容器中 自动会生效
默认会使用前缀 即 默认将注解的cacheNames属性的值作为key的前缀 避免了key重复
以编码方式使用缓存管理器
不仅可用注解方式 还可单独使用编码方式来操作缓存管理器:
首先要注入缓存管理器 然后直接使用即可(当然 前提是已经自定义了CacheManager 如果你想以JSON格式储存到Redis的话)
// 缓存管理器
@Autowired
CacheManager cacheManager;
public Department getDepartmentById(Integer id)
{
System.out.println("查询"+id+"部门");
Department employee = departmentMapper.findEmployeeById(id);
// 获取一个指定名称的缓存
Cache dept = cacheManager.getCache("dept");
// 对指定缓存进行操作 会自动添加前缀
dept.put(id,employee);
return employee;
}