JDK自带序列化方式
在Java中RedisTemplete提供了统一的API来操作Redis,比如插入一条String类型的数据,我可以用
redisTemplate.opsForValue().set("name", "美羊羊");
SpringDataRedis可以接收任何类型的对象并将其转成Redis可以处理的字节,观察这段代码中set()方法的形参列表,发现传进去的key与value都被当作了Java对象Object来处理
将对象转成字节,这一操作好像和序列化非常相似,看一眼RedisTemplete实现过程,发现针对传入的不同key-value都会有相应的序列化器来操作:
进去可以看到,在头部定义的几个RedisSerializer类型变量都是null,均需要指定并初始化。
一般按照向set方法里传参的方式则会直接走默认的JDK序列化:
通过一次Debug来简单分析一下
首先,传入的value通过serialize()被转换成了字节数组
在serialize()方法的内部,则是将value值传入内部的convert()方法
而在convert()方法的内部则是调用了JDK内部的序列化工具,以流的形式将对象完成序列化
强制步入serialize()方法后,发现底层就是调用了ObjectoutputStream()来将对象转换成字节写入到Redis中!
但是去查看Redis却发现写入的数据不是以Java端写入的类型存入,而是以序列化的形式存入,并且key与value均被序列化。
如果这样的话:1.可读性很差2.内存占用大
自定义序列化方式
上述的方式不推荐使用,我们可以人为改变一下RedisTemplete的序列化方式,即封装一个方法,指定key与value序列化的方式,不去使用默认的JDK内部序列化方式
就像这样:
@Configuration
public class ConfigRedis {
@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());
//静态方法 string()
template.setHashKeySerializer(RedisSerializer.string());
// 设置Value的序列化
template.setValueSerializer(jsonRedisSerializer);
template.setHashValueSerializer(jsonRedisSerializer);
// 返回
return template;
}
}
其实就是创建一个RedisTemplete实例,并指定四个默认的序列化方式
主要就是将key序列化成String类型,将Object序列化成JSON类型数据而不是字节
并且可以在获取库中的数据时自动反序列化:
User o = (User) redisTemplate.opsForValue().get("user:001");
按照这样的模式,以后根据需要想转换成什么样的序列化方式就可以转成什么样的方式了
最合理的字符串序列化方式
对于我们这些初学者,自动化的方式确实很香(数据量少的情况下),由于实现反序列化时为了知道对象的类型而存在的那串数据:"@class": "com.yu7daily.Pojo.User"
,乍一看没什么,一旦数据量增多则会占用大量的内存,所以上述方式还是不建议使用
为了节省内存空间,实际开发中并不会使用JSON序列化器来处理value,而是统一使用String序列化器,要求只能存储String类型的key和value。当需要存储Java对象时,手动完成对象的序列化和反序列化。SpringDataRedis提供了RedisTemplate的子类:StringRedisTemplate,它的key和value的序列化方式默认就是String方式
由于继承了RedisTemplate就不需要再次去定义,直接使用该子类的实例化对象即可:
就像这样:
@Autowired
private StringRedisTemplate stringRedisTemplate;
private static final ObjectMapper mapper = new ObjectMapper(); //JSON工具
@Test
void testSaveUser() throws JsonProcessingException {
// 创建对象
User user = new User("懒羊羊", 77);
// 手动序列化
String json = mapper.writeValueAsString(user);
// 写入数据
stringRedisTemplate.opsForValue().set("user:002", json);
// 获取数据
String jsonUser = stringRedisTemplate.opsForValue().get("user:002");
// 手动反序列化
User user2 = mapper.readValue(jsonUser, User.class);
System.out.println("user002 = " + user2);
}
运行测试类,存入对象数据后发现果然没有了“@class”数据