[Spring MVC7] 解决Redis乱码前缀问题

news2025/1/12 22:50:12

最近在做Redis缓存的时候,遇到了一个棘手的问题,简单来说就是项目使用Spring的RedisTemplate进行Redis数据存取操作,实际应用中发现Redis中key和value会出现“无意义”乱码前缀。如果是普通的java程序是没有这个问题。
本文解决Redis乱码问题,所以对Spring MVC集成Redis没有太多的涉及(这内容将在下一篇博客补齐),同时对序列化进行了些许探究。

目录

  • 问题产生
  • 序列化是什么?
  • 解决乱码问题

问题产生

最近在Spring MVC用到Redis缓存的时候,写了一个Test文件对Redis数据进行了验证,可一直在后台传不进数据,相关代码如下:
关于redis如何集成在Spring MVC里,请看我下一篇博客内容,本文内容是解决序列化问题。

public class RedisTest extends  BaseJunit4Test{
    @Resource
    private RedisTemplate redisTemplate;

    @Test
    public void testRedis() {
        redisTemplate.opsForValue().set("name", "ayccc");
        String name = (String) redisTemplate.opsForValue().get("name");
        System.out.println("value of name is:" + name);
    }

}

这个代码就是普通的向Redis写入和查询的工作,此时我在控制台打印的信息就是ayccc.
但是我去redis-cli查看的时候发现name的value竟然是nil,线外之意就是没有传到,但是为什么我此时的测试文件能打印出来呢?
首先测试了是否连接上Redis,通过日志分析,能够打开Redis,那么连接上是没有问题的。
在这里插入图片描述
那问题会不会出现在redis呢?
使用命令 keys *列举出我的ke
y值有哪些,这个方法非常有效的。
在这里插入图片描述
发现此时name前缀出现了这么多的编码,我立马就想到了这是编码问题(此时对Redis没有概念,不知道是序列化问题),更改项目的UTF-8值以后,发现依旧是这样的问题,这不禁让我重新思考。
我重新在测试文件又插入了一个数据,此时插入的key为htf:
在这里插入图片描述
发现这里出现的htf和name的前缀有点相似,前五个前缀都是一样的。
拿到这块编码很自然地去分析乱码问题:
\x对应0x
\xac\xed对应是0xaced,是ObjectOutputStream的序列化魔数(见java.io.ObjectStreamConstants.STREAM_MAGIC)。
\x00\x05对应是5,是ObjectOutputStream的序列化版本(见java.io.ObjectStreamConstants.STREAM_VERSION)。
这里引出一个小问题:为什么是\x00\x05而不是\x05?
因为上面2个值write时采用的是short,占2个字节。
样例乱码\x05后面有个t,不是很明显。t是转化后的ASCII码值对应字符,对应16进制是0x74,是ObjectOutputStream分配给String类型标记(见java.io.ObjectStreamConstants.TC_STRING)。
\x00-是有\x00和-组成的,是一起的,表示数据的字节数。-是转化后的ASCII码值对应字符,对应16进制是0x2d(10进制是45,样例abcd🔤xxxxxx:passport:associated🔑29708的字符数就是45,1个字符1个字节,字节数也是45)。
为什么是这样一串奇怪的 16 进制? ObjectOutputStream#writeString(String str, boolean unshared) 实际就是标志位 + 字符串长度 + 字符串内容

ok 问题找到了,意思就是redis的序列化问题,但什么是序列化,虽然有些了解但还是不多,索性又进行了探索。

序列化是什么?

序列化:把对象转化为可传输的字节序列过程称为序列化。序列化最终的目的是为了对象可以跨平台存储,和进行网络传输。而我们进行跨平台存储和网络传输的方式就是IO,而我们的IO支持的数据格式就是字节数组。
因为我们单方面的只把对象转成字节数组还不行,因为没有规则的字节数组我们是没办法把对象的本来面目还原回来的,所以我们必须在把对象转成字节数组的时候就制定一种规则(序列化),那么我们从IO流里面读出数据的时候再以这种规则把对象还原回来(反序列化)。
一般来说支持序列化的有这些:JDK(不支持跨语言)、JSON、XML、Hessian、Kryo(不支持跨语言)、Thrift、Protostuff、FST(不支持跨语言)

下面通过java来简单展示一下序列化。
java 实现序列化很简单,只需要实现Serializable 接口即可。

import java.io.Serializable;

public class Employee implements Serializable{
    private static final long serialVersionUID = 1L; //Serial Version UID
    int id;
    String name;
    public Employee(int id, String name) {
        this.id = id;
        this.name = name;
    }
}

这里提一句,Serializable接口是没有任何方法的:

public interface Serializable {
}

Serializable是一个所谓的标记接口,也就是说,实现这个接口是给这个类贴个标签,说它是Serializable的就可以了,具体实现是由JVM内部实现的,这个标签实际上是告诉JVM,你可以将我序列化。但这个标签不是随便贴的,如果你给一个类贴了这个标签,却在内部用到没贴这个标签的类,那运行时就可能有异常抛出。标记接口的用法现在一般被Annotation代替了,但Serializable是在Annotation还没出现前就存在了的。

序列化操作:

import java.io.*;
public class Persist{
    public static void main(String args[]){
        try{
            Employee emp1 =new Employee(20110,"John");
            Employee emp2 =new Employee(22110,"Jerry");
            Employee emp3 =new Employee(20120,"Sam");
            FileOutputStream fout=new FileOutputStream("output.txt");
            ObjectOutputStream out=new ObjectOutputStream(fout);
            out.writeObject(emp1);
            out.writeObject(emp2);
            out.writeObject(emp3);
            out.flush();
            out.close();
            System.out.println("Serialization and Deserialization is been successfully executed");
        }
        catch(Exception e){
            System.out.println(e);}
    }
}

此时看一下output.txt文件,不出所料都是乱码:
在这里插入图片描述
此时已经将类的属性保存在了output.java中了.
反序列化:

import java.io.*;
public class Depersist{
    public static void main(String args[]){
        try{
            ObjectInputStream in=new ObjectInputStream(new FileInputStream("output.txt"));
            Employee e1=(Employee)in.readObject();
            Employee e2=(Employee)in.readObject();
            Employee e3=(Employee)in.readObject();
            System.out.println(e1.id+" "+e1.name);
            System.out.println(e2.id+" "+e2.name);
            System.out.println(e3.id+" "+e3.name);
            in.close();
        }
        catch(Exception e){
            System.out.println(e);}
    }
}

运行结果:
在这里插入图片描述
分析一下writeObject函数:

public final void writeObject(Object obj) throws IOException {
        if (enableOverride) {
            writeObjectOverride(obj);
            return;
        }
        try {
            writeObject0(obj, false);
        } catch (IOException ex) {
            if (depth == 0) {
                writeFatalException(ex);
            }
            throw ex;
        }
    }

进入到writeObject0(obj, false)
最重要的部分源码:

// remaining cases
            if (obj instanceof String) {
                writeString((String) obj, unshared);
            } else if (cl.isArray()) {
                writeArray(obj, desc, unshared);
            } else if (obj instanceof Enum) {
                writeEnum((Enum<?>) obj, desc, unshared);
            } else if (obj instanceof Serializable) {
                writeOrdinaryObject(obj, desc, unshared);
            } else {
                if (extendedDebugInfo) 

注意String 实现了 Serializable 接口,表明如果上面的Employee没有实现此接口会进行报错。

解决乱码问题

现在问题找到了,解决问题就很简单了,问题就出在Spring MVC里面。
org.springframework.data.redis.core.RedisTemplate实例化需要序列化和反序列化组件,如果我们不指定,默认使用org.springframework.data.redis.serializer.JdkSerializationRedisSerializer进行序列化,而JdkSerializationRedisSerializer最终使用的是Java原生java.io.ObjectOutputStream.ObjectOutputStream(OutputStream)进行序列化。这个乱码前缀就是ObjectOutputStream进行序列化时添加的。
现在就需要改变:
一般有两种办法,一是使用配置文件二是写配置类,这里都将解决。
我一开始配置的工具是这个:说白了就是没有实现相应的序列化操作
spring-redis.xml:

   <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"
          p:connection-factory-ref="redisConnectionFactory">
    </bean>

将上述xml改成这个就好:绝大多数情况下,不推荐使用 JdkSerializationRedisSerializer 进行序列化。主要是不方便人工排查数据。

    <!--使用字符串进行序列化-->
    <bean id="stringReadisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
    <!--使用JDK的序列化器进行转化-->
    <bean id="jdkSerializationRedisSerializer" class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
    <!--配置Spring RedisTemplate-->
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
        <property name="connectionFactory" ref="redisConnectionFactory"/>
        <property name="keySerializer" ref="stringReadisSerializer"/>
        <property name="valueSerializer" ref="stringReadisSerializer"/>
    </bean>

或者可以使用配置类:

package com.ay.conf;

import org.aspectj.lang.annotation.After;
import org.springframework.beans.factory.annotation.Autowired;
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.StringRedisSerializer;

import javax.annotation.Resource;

@Configuration
public class RedisConfig {
    @Resource
    private RedisConnectionFactory redisConnectionFactory;

    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new StringRedisSerializer());
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        return redisTemplate;
    }
}

此配置类方法非常高效,将在后面对此进行深入研究。

以上就将问题全部解决了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/19531.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于Matlab-gui信号系统设计

目 录 1绪论 - 1 - 2系统分析和开发软件概述 - 6 - 2.1 需求分析 - 6 - 2.2系统开发环境和运行环境 - 6 - 2.3 Matlab图形用户界面(GUI) - 6 - 2.4 系统主要目标及功能 - 7 - 2.4.1 系统目标 - 7 - 2.4.2 系统主要功能 - 8 - 3系统软件平台的设计 10 3.1系统整体框图 10 3.2系统…

阿里的三个「价值支点」

历史总是轮回的。 2015年5月&#xff0c;身处低谷的阿里换了掌舵人&#xff0c;张勇由COO升任CEO&#xff0c;随后进行了一轮组织架构调整&#xff0c;并通过新零售、淘宝直播、阿里云等业态为阿里安上未来发展引擎。 当时的阿里市值涨至2000亿美元左右&#xff0c;华尔街投资…

终端天线—11.NFC线圈仿真

NFC线圈单体仿真 NFC线圈主要根据终端设备预留的空间大小&#xff0c;去设计走线宽度和圈数以及面积&#xff0c;NFC单体主要关注其13.56MHz处的电感量大小&#xff0c;以及阻抗的虚部和实部&#xff0c;可以根据ST和NXP芯片的要求去设计。 一、Original model 1.Simulation …

C语言文件操作【详解】

本期介绍&#x1f356; 主要介绍&#xff1a;为什么使用文件&#xff0c;什么是文件&#xff0c;文件的打开和关闭的操作方法&#xff0c;文件的顺序读写于随机读写&#xff0c;文件读取结束的判定&#x1f440;。 文章目录一、为什么使用文件&#x1f356;二、什么是文件&…

React 入门:使用脚手架写一个Hello组件

文章目录本文目标开发前的准备编写主页面 index.html编写外壳组件 App.js编写入口文件 index.js代码组件化开发 Hello 组件开发 Welcome 组件引用组件组件化实现效果样式的模块化提升编码效率本文目标 通过使用脚手架确实让我们很方便的创建一个 React 项目基础代码结构&#…

力扣(LeetCode)11. 盛最多水的容器(C++)

双指针贪心 盛水的面积 长度 \times 左右柱子最低高度 area(r−l)min(height[l],height[r])area (r-l)\times min(height[l],height[r])area(r−l)min(height[l],height[r]) 初始时&#xff0c;我们不知道每个柱子的高度&#xff0c;但是我们可以选取最左侧柱子和最右侧柱子…

期望E与高斯分布的期望

目录 1. 期望定义 2. 期望性质 2.1 用期望定义方差 / 标准差 方差定义 标准差定义 方差的表示——离散型&#xff1a; 方差的表示——连续型&#xff1a; 方差的性质 3. (一元)高斯分布定义 4. (一元)高斯分布的性质 5. 二维随机向量的数学期望E与方差σ 参考 1. …

PyCharm安装部署(一) 百篇文章学PyQT

本文章是百篇文章学PyQT的第一篇&#xff0c;本文讲述如何安装PyCharm IDEA工具&#xff0c;其它工具也可以但是PyCharm 相对来说用的人多大家都认可(方案成熟)&#xff0c;pycharm是一款功能强大的python编辑器&#xff0c;具有跨平台性&#xff0c;本文介绍一下pycharm在wind…

SSM 医院在线挂号系统

SSM 医院在线挂号系统 SSM 医院在线挂号系统 功能介绍 首页 登录注册 图片轮播展示 系统简介 系统公告 医院介绍 医生展示 医院资讯 预约挂号 收藏 评论 在线留言 查看留言 后台管理 登录 管理员管理 修改密码 医院信息管理 医生信息管理 用户权限管理 科室信息管理 预约挂号…

微信小程序如何转云开发

微信小程序云开发&#xff0c;为前端全栈开发提供了很大的便利。本文主要介绍普通的微信小程序如何让转为云开发。 一、建cloudfunction文件夹 cloudfunction文件夹建立在小程序的根目录下。 二、修改 project.config.json配置 在 project.config.json文件中&#xff0c;添加&…

ip子网的划分方法

目录 1 子网划分的定义&#xff1a; 2 掩码介绍 3、子网划分要解决的问题&#xff1a; 4 子网划分步骤 5 范例1根据ip和掩码求子网和网络广播地址&#xff1a; 6 范例2根据ip和掩码求同网段地址 7 合并子网的例子根据ip范围合并&#xff0c;并添加回程路由&#xff1a; …

还不进来看吗?c趁你不注意偷偷将你的数据类型转换啦

前言 如果不了解 整形提升 的小伙伴可就要注意了,c偷偷将你的数据类型改变了你都不知道.快点和牛牛一起学习一下c语言中 整形提升的知识吧 ! 一、整形提升是什么&#xff0c;又是怎样提升的? 不知道小伙伴们有没有听过整形提升这个词? 整形提升是什么呢? C语言中,在进行…

[附源码]java毕业设计石林县石漠化信息查询分析系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

【GlobalMapper精品教程】020:Lidar点云数据分类(自动分类、手动分类)案例详解

航测点云通常跟DSM一致,即包含植被、房屋等信息,必须进行点云分类、过滤,才能生成准确的高程点、等高线和DEM等地形数据。本文以案例的形式详细讲解globalmapper23中点云工具及使用方法。 文章目录 1. 点云分类2. 创建地面高程格网3. 地形绘制4. 格网转点云5. 点云抽稀6. 点…

面试:java中的各种锁

共享锁 共享锁有CountDownLatch, CyclicBarrier, Semaphore, ReentrantReadWriteLock等 ReadWriteLock&#xff0c;顾名思义&#xff0c;是读写锁。它维护了一对相关的锁 — — “读取锁”和“写入锁”&#xff0c;一个用于读取操作&#xff0c;另一个用于写入操作。“读取锁…

java计算机毕业设计ssm建设路小学芙童币和芙童印章管理系统

项目介绍 随着移动互联网技术的迅速发展,时代对人们的知识水平和综合素质要求也越来越高了,各种教育管理系统层出不穷。其中以建设路小学吉祥物“芙童”为卡通原型设计的芙童印章、芙童币深受学生和老师们的喜爱。这是学校结合德育教学、少先队活动和社会实践活动为他们量身定…

Mybatis源码解析(六):一级缓存和二级缓存的优先级

Mybatis源码系列文章 手写源码&#xff08;了解源码整体流程及重要组件&#xff09; Mybatis源码解析(一)&#xff1a;环境搭建 Mybatis源码解析(二)&#xff1a;全局配置文件的解析 Mybatis源码解析(三)&#xff1a;映射配置文件的解析 Mybatis源码解析(四)&#xff1a;s…

Unity学习笔记[一] RollBall小游戏

目录 一、适配vs 二、初识Unity 2.1 unity核心模块 2.2 Unity基本操作和场景操作 2.3 世界坐标系和局部坐标系 2.4 工具栏 QWER 三、基础知识 3.1 基本组件 3.2 刚体组件 3.2.1 获取刚体组件 3.2.2 给刚体施加力 3.3 三维向量Vector3 3.4 通过按键控制左右运动 3…

【Pygame】 游戏开发 基础知识

【Pygame】 第一课 游戏开发 基础知识概述Pygame 的安装Pygame 基础命令pygame.locals 模块pygame.init()pygame.display.set_mode()案例Pygame 显示文字pygame.font.Font()fill()render()blit()pygame.display.update()案例显示英文显示中文概述 Pygame 是一个跨平台的 Pytho…

计算机网络 - 物理层数据链路层大题复习题

文章目录1、在下面给出的TCP/IP层次模型图示中填写空缺处①-⑤的协议名称。2、请写出在OSI的第几层分别处理下面的问题&#xff1f;3、试简述数据链路层的三个基本问题为什么都必须加以解决&#xff1f;4、收发两端之间的传输距离为100km&#xff0c;信号在媒体上的传播速率为2…