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

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

相关文章:

  • 手写简易Tomcat核心实现:深入理解Servlet容器原理
  • 萌新学 Python 之面向对象的下划线控制权限访问
  • PHP:phpstudy无法启动MySQL服务问题解决
  • Java基础面试题全集
  • 快速使用MASR V3版不能语音识别框架
  • 动态规划完全背包系列一>完全背包
  • 动态规划详解(二):从暴力递归到动态规划的完整优化之路
  • 使用OpenCV和MediaPipe库——驼背检测(姿态监控)
  • 深度神经网络架构设计与工程实践 ——从理论到产业落地的全方位解析
  • leetcode 95.不同的二叉搜索树 Ⅱ
  • 设计AI芯片架构的入门 研究生入行数字芯片设计、验证的项目 opentitan
  • RISC-V汇编学习(三)—— RV指令集
  • MySQL 5.7.40 主从同步配置教程
  • kettle工具使用从入门到精通(一)
  • 高效计算新篇章:探秘稀疏注意力如何颠覆传统Transformer
  • 从0开始,手搓Tomcat
  • 【Docker】通过 Docker 拉取 Python 镜像并设置远程连接(SSH)
  • 在K8S中,svc底层是如何实现的?
  • 基于SpringBoot的“文物管理系统”的设计与实现(源码+数据库+文档+PPT)
  • 【无人机三维路径规划】基于CPO冠豪猪优化算法的无人机三维路径规划Maltab
  • 云朵课堂网站开发怎么收费/seo网络优化是什么工作
  • 文件传输协议登录网站/上海网站制作开发
  • 专门做实习计算机项目的网站平台/免费推广产品的平台
  • 开封做网站哪家好/百度seo网站优化 网络服务
  • 交河做网站/郑州seo优化外包公司
  • 已有网站做app需要多少钱/太原seo建站