当前位置: 首页 > article >正文

解决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类型操作测试代码,没有什么问题。

浏览器正常响应

后端控制台也没有报错

问题产生原因:

       但是却在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;
    }
}


http://www.kler.cn/a/578816.html

相关文章:

  • npm install 报错ERESOLVE
  • Django工程获取请求参数的几种方式
  • Scala 中的访问修饰符
  • WebGPT: 基于浏览器辅助的问答系统,结合人类反馈优化答案质量
  • sysbench手动测试OceanBase v4.2.4集群
  • 推荐一个好用的在线文本对比网站 - diffchecker
  • 如何使用Webpack打包React项目?
  • 系统思考与问题诊断
  • Ubuntu 24.04安装搜狗出现闪烁,使用 Fcitx5 平替并添加自定义字典教程
  • 如何应对Maven中的依赖导入挑战?
  • 广州哪家公司做柔性装备?富唯智能以科技重构镀膜生产新范式
  • Linux查询内存使用率 并使用命令计算出来
  • MySQL:CRUD(增删查改)
  • 【C#】详解C#中的内存管理机制
  • 【C语言】--- 动态内存管理详解
  • Spring Boot与Axon Framework整合教程
  • Java EE 进阶:Spring IoCDI
  • RISC-V医疗芯片工程师复合型转型的路径与策略
  • 知识图谱相关的Terse RDF Triple Language 文件格式介绍
  • 微服务拆分-远程调用