文章目录

  • 问题引入
  • 原因
  • 查看源码
  • 解决办法
  • 自定义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 设置序列自增 redistemplate.opsforvalue 序列化_java

插入的数据被序列化为字节形式

redisTemplate 设置序列自增 redistemplate.opsforvalue 序列化_java_02

原因

RedisTemplate可以接收任意Object作为值写入Redis:

redisTemplate 设置序列自增 redistemplate.opsforvalue 序列化_学习_03

只不过写入前会把Object序列化为字节形式,默认是采用JDK序列化工具:ObjectOutputStream,所以得到以上结果,尝试追一下源码:

进入 RedisTemplate 类里面:keySerializer,valueSerializer,hashKeySerializer,hashValueSerializer,普通字符串和 hash结构的 序列化器,来实现序列化和反序列化

redisTemplate 设置序列自增 redistemplate.opsforvalue 序列化_java_04

默认 Jdk 序列化器

redisTemplate 设置序列自增 redistemplate.opsforvalue 序列化_java_05

查看源码

在 set 方法上打断点,debug 追源码:

传进来的值变为字节

redisTemplate 设置序列自增 redistemplate.opsforvalue 序列化_redis_06

获取值的序列化器

redisTemplate 设置序列自增 redistemplate.opsforvalue 序列化_学习_07

进入到默认的 JDK 序列化器:JdkSerializationRedisSerializer

redisTemplate 设置序列自增 redistemplate.opsforvalue 序列化_java_08

接着往下

redisTemplate 设置序列自增 redistemplate.opsforvalue 序列化_缓存_09

缓冲

redisTemplate 设置序列自增 redistemplate.opsforvalue 序列化_缓存_10

把 java对象转为 字节 写入 Redis:ObjectOutputStream,也就是我们看到的那长串字节

redisTemplate 设置序列自增 redistemplate.opsforvalue 序列化_redis_11

这种序列化方式的缺点:

  • 可读性差
  • 内存占用较大

其他序列化器:

  • key 通常为 字符串,可以使用:StringRedisSerializer
  • value 通常为 object 对象,可以使用:Jackson2JsonRedisSerializer

redisTemplate 设置序列自增 redistemplate.opsforvalue 序列化_java_12

解决办法

自定义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 成功

redisTemplate 设置序列自增 redistemplate.opsforvalue 序列化_序列化_13

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序列化方式。最终结果如图:

redisTemplate 设置序列自增 redistemplate.opsforvalue 序列化_学习_14

整体可读性有了很大提升,并且能将Java对象自动的序列化为JSON字符串,并且查询时能自动把JSON反序列化为Java对象。不过,其中记录了序列化时对应的class名称,目的是为了查询时实现自动反序列化。这会带来额外的内存开销。

StringRedisTemplate

概述

为了节省内存空间,我们可以不使用JSON序列化器来处理value,而是统一使用String序列化器,要求只能存储String类型的key和value。当需要存储Java对象时,手动完成对象的序列化和反序列化。

redisTemplate 设置序列自增 redistemplate.opsforvalue 序列化_redis_15

因为存入和读取时的序列化及反序列化都是我们自己实现的,SpringDataRedis就不会将class信息写入Redis了。

这种用法比较普遍,因此SpringDataRedis就提供了RedisTemplate的子类:StringRedisTemplate,它的key和value的序列化方式默认就是String方式。

redisTemplate 设置序列自增 redistemplate.opsforvalue 序列化_学习_16

代码实现

@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.opsforvalue 序列化_序列化_17

总结

RedisTemplate的两种序列化实践方案:

  • 方案一:
  • 自定义RedisTemplate
  • 修改RedisTemplate的序列化器为GenericJackson2JsonRedisSerializer
  • 方案二:
  • 使用StringRedisTemplate
  • 写入Redis时,手动把对象序列化为JSON
  • 读取Redis时,手动把读取到的JSON反序列化为对象