文章目录
- 问题引入
- 原因
- 查看源码
- 解决办法
- 自定义RedisTemplate的序列化
- key:String & value:String
- key:String & value:Object
- StringRedisTemplate
- 概述
- 代码实现
- 测试
- 总结
问题引入
当我们使用 RedisTemplate 向Redis 中 写入一条String数据时,可以正常插入获取数据
@Autowired
private RedisTemplate redisTemplate;
@Test
void testString() {
ValueOperations valueOperations = redisTemplate.opsForValue();
valueOperations.set("name","xiaotengteng"); //打断点追源码
System.out.println("name = "+valueOperations.get("name"));
}
但打开Redis 客户端查看,或者 redis-cli 查看时发现:
插入的数据被序列化为字节形式
原因
RedisTemplate可以接收任意Object作为值写入Redis:
只不过写入前会把Object序列化为字节形式,默认是采用JDK序列化工具:ObjectOutputStream,所以得到以上结果,尝试追一下源码:
进入 RedisTemplate 类里面:keySerializer,valueSerializer,hashKeySerializer,hashValueSerializer,普通字符串和 hash结构的 序列化器,来实现序列化和反序列化
默认 Jdk 序列化器
查看源码
在 set 方法上打断点,debug 追源码:
传进来的值变为字节
获取值的序列化器
进入到默认的 JDK 序列化器:JdkSerializationRedisSerializer
接着往下
缓冲
把 java对象转为 字节 写入 Redis:ObjectOutputStream,也就是我们看到的那长串字节
这种序列化方式的缺点:
- 可读性差
- 内存占用较大
其他序列化器:
- key 通常为 字符串,可以使用:StringRedisSerializer
- value 通常为 object 对象,可以使用:Jackson2JsonRedisSerializer
解决办法
自定义RedisTemplate的序列化
key:String & value:String
新建 RedisConfig 配置类
package com.itheima.springdataredisdemo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory connectionFactory){
//创建 RedisTemplate 对象
RedisTemplate<String, Object> template = new RedisTemplate<>();
//设置连接工厂
template.setConnectionFactory(connectionFactory);
//创建 JSON 序列化工具
GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
//设置 key 的序列化
template.setKeySerializer(RedisSerializer.string());
template.setHashKeySerializer(RedisSerializer.string());
//设置 value 的序列化
template.setValueSerializer(jsonRedisSerializer);
template.setHashValueSerializer(jsonRedisSerializer);
//返回
return template;
}
}
测试
@Autowired
private RedisTemplate<String,Object> redisTemplate;
@Test
void testString() {
ValueOperations valueOperations = redisTemplate.opsForValue();
valueOperations.set("name","xiaotengteng");
System.out.println("name = "+valueOperations.get("name"));
}
结果报错,错误信息如下:
Caused by: java.lang.ClassNotFoundException: com.fasterxml.jackson.core.JsonProcessingException
//需要添加依赖
pom.xml
<!--Jackson依赖-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
测试:修改 name 成功
key:String & value:Object
新建 User 类
package com.itheima.springdataredisdemo.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String name;
private Integer age;
}
测试
@Test
void testSaveUser(){
redisTemplate.opsForValue().set("user",new User("杰克",21));
User user = (User) redisTemplate.opsForValue().get("user");
System.out.println(user);
}
这里采用了JSON序列化来代替默认的JDK序列化方式。最终结果如图:
整体可读性有了很大提升,并且能将Java对象自动的序列化为JSON字符串,并且查询时能自动把JSON反序列化为Java对象。不过,其中记录了序列化时对应的class名称,目的是为了查询时实现自动反序列化。这会带来额外的内存开销。
StringRedisTemplate
概述
为了节省内存空间,我们可以不使用JSON序列化器来处理value,而是统一使用String序列化器,要求只能存储String类型的key和value。当需要存储Java对象时,手动完成对象的序列化和反序列化。
因为存入和读取时的序列化及反序列化都是我们自己实现的,SpringDataRedis就不会将class信息写入Redis了。
这种用法比较普遍,因此SpringDataRedis就提供了RedisTemplate的子类:StringRedisTemplate,它的key和value的序列化方式默认就是String方式。
代码实现
@SpringBootTest
class RedisStringTests {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Test
void testString() {
// 写入一条String数据
stringRedisTemplate.opsForValue().set("verify:phone:13600527634", "124143");
// 获取string数据
Object name = stringRedisTemplate.opsForValue().get("name");
System.out.println("name = " + name);
}
private static final ObjectMapper mapper = new ObjectMapper();
@Test
void testSaveUser() throws JsonProcessingException {
// 创建对象
User user = new User("虎哥", 21);
// 手动序列化
String json = mapper.writeValueAsString(user);
// 写入数据
stringRedisTemplate.opsForValue().set("user:200", json);
// 获取数据
String jsonUser = stringRedisTemplate.opsForValue().get("user:200");
// 手动反序列化
User user1 = mapper.readValue(jsonUser, User.class);
System.out.println("user1 = " + user1);
}
}
测试
此时我们再来看一看存储的数据,发现那个class数据已经不在了,节约了我们的空间~
总结
RedisTemplate的两种序列化实践方案:
- 方案一:
- 自定义RedisTemplate
- 修改RedisTemplate的序列化器为GenericJackson2JsonRedisSerializer
- 方案二:
- 使用StringRedisTemplate
- 写入Redis时,手动把对象序列化为JSON
- 读取Redis时,手动把读取到的JSON反序列化为对象