目录

解决JDK-序列化导致的-Redis-Key-非预期编码问题

解决JDK 序列化导致的 Redis Key 非预期编码问题

@RequestMapping("/testList")
    @ResponseBody
    public String testList() {
        //清楚之前的数据
        redisTemplate.execute((RedisConnection redisConnection)->{
            //excute 要求回调方法中必须写 return语句,返回个东西
            //这个回调返回的对象,就会作为excute本身的返回值。
            redisConnection.flushAll();
            return null;
        });
        redisTemplate.opsForList().leftPush("key", "111");
        redisTemplate.opsForList().leftPush("key", "222");
        redisTemplate.opsForList().leftPush("key", "333");
        String value = (String) redisTemplate.opsForList().leftPop("key");
        System.out.println(value);
        value = (String) redisTemplate.opsForList().leftPop("key");
        System.out.println(value);

        return "ok";
    }

以上为List类型操作测试代码,没有什么问题。

https://i-blog.csdnimg.cn/direct/3757fbb025454c0495f3cf09224e01ca.png

浏览器正常响应

https://i-blog.csdnimg.cn/direct/c27e8e4f90ff4deeb3574fef01ad3ad0.png

后端控制台也没有报错

问题产生原因:

https://i-blog.csdnimg.cn/direct/8794ec8b75db4ccab1aad81a5243771f.png

但是却在xshell终端中出现了,key值存储显示异常的问题,同时单纯的对“key”使用命令,没有效果。之后我查找相关资料,了解到这是JDK 序列化导致的 Redis Key 非预期编码问题。产生这个问题的主要原因是 RedisTemplate 默认使用JDK序列( JdkSerializationRedisSerializer )作为 keySerializer ,它会把 Key 进行 Java 对象序列化后存入 Redis,而不是存储原始字符串。

问题解决办法:

优化默认的序列化方式:

Key 统一使用 StringRedisSerializer,避免 JDK 序列化的乱码问题。

Value 根据类型动态选择合适的序列化方式:

String 直接存 StringRedisSerializer。

对象、集合 存 JSON(GenericJackson2JsonRedisSerializer)。

避免 JSON 额外的 ",保证字符串存储不会被 JSON 影响。

支持 Java 8 时间类型的 JSON 序列化。

package org.example.redisdemo.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
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;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);

        // 1. Key 和 HashKey 统一用 StringRedisSerializer
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        template.setKeySerializer(stringRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);

        // 2. 配置 JSON 序列化器
        ObjectMapper objectMapper = new ObjectMapper();
        PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder()
                .allowIfSubType(Object.class)
                .build();
        objectMapper.activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.NON_FINAL);
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        objectMapper.registerModule(new JavaTimeModule());

        GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer(objectMapper);

        // 3. Value 序列化:String 类型的值用 StringRedisSerializer,其他用 JSON
        template.setValueSerializer(new RedisSerializer<>() {
            private final StringRedisSerializer stringSerializer = new StringRedisSerializer();

            @Override
            public byte[] serialize(Object o) {
                if (o instanceof String) {
                    return stringSerializer.serialize((String) o);
                }
                return jsonRedisSerializer.serialize(o);
            }

            @Override
            public Object deserialize(byte[] bytes) {
                if (bytes == null) return null;
                String str = stringSerializer.deserialize(bytes);
                return (str != null && str.startsWith("\"") && str.endsWith("\"")) ? jsonRedisSerializer.deserialize(bytes) : str;
            }
        });

        // 4. Hash Value 也做类似处理,避免 String 额外的 `\"`
        template.setHashValueSerializer(new RedisSerializer<>() {
            private final StringRedisSerializer stringSerializer = new StringRedisSerializer();

            @Override
            public byte[] serialize(Object o) {
                if (o instanceof String) {
                    return stringSerializer.serialize((String) o);
                }
                return jsonRedisSerializer.serialize(o);
            }

            @Override
            public Object deserialize(byte[] bytes) {
                if (bytes == null) return null;
                String str = stringSerializer.deserialize(bytes);
                return (str != null && str.startsWith("\"") && str.endsWith("\"")) ? jsonRedisSerializer.deserialize(bytes) : str;
            }
        });

        template.afterPropertiesSet();
        return template;
    }
}