RedisTemplate的RedisSerializer(序列化)

场景一

在使用 increment 时发生报错

io.lettuce.core.RedisCommandExecutionException: ERR value is not an integer or out of range

如下: 

int id = (Integer) redisTemplate.opsForValue().get("id");
redisTemplate.opsForValue().increment("id",5);

场景二 

 成功执行了如下代码

@Test
    void contextLoads() {
        redisTemplate.opsForValue().set("id",0);
    }

Redis中却没出现 id 的key,而出现了一串字符 ,就连 value 也不是 0 

 

上面两种场景都是因为 RedisTemplate 的序列化

 上面我们可以看到 redistTemplate 的 set 方法接收的是 Object对象

redisTemplate的功能就是接收任何类型的对象,然后底层通过JDK序列化器,将Java对象转化为Redis可以识别的字节

我们跟踪 redistTemplate 的 set方法,可以发现set底层调用了 DefaultValueOperations<K, V> 中的set方法如下:

public void set(K key, V value) {
        final byte[] rawValue = this.rawValue(value);
        this.execute(new AbstractOperations<K, V>.ValueDeserializingRedisCallback(key) {
            protected byte[] inRedis(byte[] rawKey, RedisConnection connection) {
                connection.set(rawKey, rawValue);
                return null;
            }
        });
    }

我们发现,传入的value被 rawValue方法转换成 byte数组,继续跟踪 rawValue方法

byte[] rawValue(Object value) {
        return this.valueSerializer() == null && value instanceof byte[] ? (byte[])((byte[])value) : this.valueSerializer().serialize(value);
    }

 我们发现调用了serialize方法,继续跟踪serialize方法,发现我们进到了JdkSerializationRedisSerializer 的 serialize 方法,如下:

public byte[] serialize(@Nullable Object object) {
        if (object == null) {
            return SerializationUtils.EMPTY_ARRAY;
        } else {
            try {
                return (byte[])this.serializer.convert(object);
            } catch (Exception var3) {
                throw new SerializationException("Cannot serialize", var3);
            }
        }
    }

不断深入跟踪上面的convert方法,最后来到了 DefaultSerializer 中的 serialize方法,我们看到底层用到了 ObjectOutputStream 将object对象转换为字节流写入Redis, 从而就达到了我们看到的效果

public void serialize(Object object, OutputStream outputStream) throws IOException {
        if (!(object instanceof Serializable)) {
            throw new IllegalArgumentException(this.getClass().getSimpleName() + " requires a Serializable payload but received an object of type [" + object.getClass().getName() + "]");
        } else {
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
            objectOutputStream.writeObject(object);
            objectOutputStream.flush();
        }
    }

这种序列化方式有俩个弊端:

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

如何做到 写入 的跟 存入 的是一样的呢?

这只需要修改RedisTemplate的序列化方式,添加如下配置类

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;
    }
}

重新运行

 查看Redis

成功了,没毛病 ^ - ^ 

插入一个对象看看

import lombok.Data;

@Data
public class User {

    private String name;
    private Integer age;

}

 

 还是没毛病 嘿嘿 ^ - ^ 

配置 RedisTemplate 序列化可以提高数据的读写效率和可读性,以下是几种常见的配置方法: ### 自定义序列化方式 可以创建一个配置类,在其中自定义 RedisTemplate序列化方式。示例代码如下: ```java 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<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) { // 创建 RedisTemplate 对象 RedisTemplate<Object, 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; } } ``` 这种配置方式中,Key 使用 String 序列化,Value 使用 JSON 序列化,避免了 Redis 默认 JDK 序列化键值识别性差的问题[^2]。 ### 使用 Jackson2JsonRedisSerializer 一般来说,Redis 存储的键用字符串形式,值用 JSON 形式,使用 Jackson2JsonRedisSerializer 可以自动进行序列化和反序列化,无需手动处理。不过需要注意,使用 GenericJackson2JsonRedisSerializer 配置序列化时,在 Redis 存储中会有一个 `@class` 字段指定数据类型,会占用一定空间[^4]。 ### 结合 StringRedisTemplate 可以结合 StringRedisTemplate 进行测试,示例代码如下: ```java import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.core.StringRedisTemplate; @SpringBootTest class RedisTestApplicationTests { @Autowired private StringRedisTemplate stringRedisTemplate; // Json 工具 private ObjectMapper mapper = new ObjectMapper(); @Test void StringTemplate() throws Exception { User user = new User("青秋", 18); // 手动序列化 String json = mapper.writeValueAsString(user); // 写入数据 stringRedisTemplate.opsForValue().set("stringredistemplate", json); // 读取数据 String val = stringRedisTemplate.opsForValue().get("stringredistemplate"); // 反序列化 User u = mapper.readValue(val, User.class); System.out.println(u); } } ``` RedisTemplate 默认使用 JdkSerializationRedisSerializer 存入数据,会将数据先序列化成字节数组再存入 Redis 数据库,这种方式存储的 value 不可读。而 StringRedisTemplate 可以处理正常的可读字符串数据。当需要获取的数据不是以字节数组存在 Redis 中时,使用 StringRedisTemplate 或专门设置 RedisTemplate序列化器是更好的选择[^3]。 ### 针对 LocalDateTime 配置 ObjectMapper 使用 Jackson2JsonRedisSerializer 构造器时,ObjectMapper 内部不会对 LocalDateTime 进行字符串化,而是保持对象化,反序列化时会报错。因此需要针对 LocalDateTime 配置自己的 ObjectMapper。同时,Jackson2JsonRedisSerializer 序列化提供的 ObjectMapper 在没有设置反序列化时,对象会以 LinkedHashMap 的形式存储,也需要单独对其进行配置[^5]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值