【SpringBoot】序列化和反序列化介绍
一、认识序列化和反序列化
Serialization(序列化)是一种将对象以一连串的字节描述的过程;deserialization(反序列化)是一种将这些字节重建成一个对象的过程。将程序中的对象,放入文件中保存就是序列化,将文件中的字节码重新转成对象就是反序列化。
二、为什么要实现序列化和反序列化
- 我们创建的 Java 对象被存储在 Java 堆中,当程序运行结束后,这些对象会被 JVM 回收。但在现实的应用中,可能会要求在程序运行结束之后还能读取这些对象,并在以后检索数据,这时就需要用到序列化。
- 当两个进程进行远程通信时,可以相互发送各种类型的数据,包括文本、图片、音频、视频等, 而这些数据都会以二进制序列的形式在网络上传送。而 java 是面向对象的开发方式,一切都是 java 对象,想要实现 java 对象的网络传输,就可以使用序列化和反序列化来实现。发送方将需要发送的 Java 对象序列化转换为字节序列,然后在网络上传送;接收方接收到字符序列后,使用反序列化从字节序列中恢复出 Java 对象。
总结,在网络中数据的传输必须是序列化形式来进行的。
三、序列化和反序列化的实现
1、JDK 类库提供的序列化 API
- java.io.ObjectOutputStream:表示对象输出流
它的 writeObject(Object obj) 方法可以对参数指定的 obj 对象进行序列化,把得到的字节序列写到一个目标输出流中。
- java.io.ObjectInputStream:表示对象输入流
它的 readObject() 方法从源输入流中读取字节序列,再把它们反序列化成为一个对象,并将其返回。
2、实现序列化的要求
只有实现了 Serializable 或 Externalizable 接口的类的对象才能被序列化,否则抛出异常。
public class SerializableTest {public static void main(String[] args) throws IOException, ClassNotFoundException {serializeStudent();deserializeStudent();}// JDK 类库中序列化的步骤static void serializeStudent() throws IOException, ClassNotFoundException {// 创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流:FileOutputStream fos = new FileOutputStream("F:\\HaC.txt");ObjectOutputStream oos = new ObjectOutputStream(fos);Student student1 = new Student("HaC", "HelloCoder", 30);// 通过对象输出流的 writeObject() 方法写对象oos.writeObject(student1);oos.flush();System.out.println("Student 对象序列化成功!");oos.close();}// JDK 类库中反序列化的步骤static void deserializeStudent() throws IOException, ClassNotFoundException {// 创建一个对象输入流,它可以包装一个其它类型输入流,如文件输入流:FileInputStream fis = new FileInputStream("F:\\HaC.txt");ObjectInputStream ois = new ObjectInputStream(fis);// 通过对象输出流的 readObject() 方法读取对象:Student student2 = (Student) ois.readObject();System.out.println(student2.getUserName() + " " +student2.getPassword() + " " + student2.getYear());System.out.println("Student 对象反序列化成功!");}
}@Data
@AllArgsConstructor
class Student implements Serializable {private static final long serialVersionUID = 3608451818006447637L;private String userName;private String password;private String year;
}
可以看到生成了一个打开是乱码的二进制文件:
其实这个例子就是序列化和反序列化的一个小过程,JVM 通过序列化把对象写到文件,再通过反序列化从文件中读取数据,把数据转成一个对象。
看到控制台输出也是正常的:
Student 对象序列化成功!
HaC HelloCoder 30
Student 对象反序列化成功!
四、serialVersionUID 的作用
虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,一个非常重要的一点是两个类的序列化 ID 是否一致,这个所谓的序列化 ID,就是我们在代码中定义的 serialVersionUID。
serialVersionUID 得生成方法:
- private static final long serialVersionUID = 1L;
- 根据包名,类名,继承关系,非私有的方法和属性,以及参数,返回值等诸多因子计算得出的,极度复杂生成的一个 64 位的哈希字段。基本上计算出来的这个值是唯一的。比如:private static final long serialVersionUID = xxxxL。显示声明 serialVersionUID 可以避免对象不一致
- 如果没有显示的定义 serialVersionUID 变量的时候,JAVA 序列化机制会根据 Class 自动生成一个 serialVersionUID 作序列化版本比较用,这种情况下,如果 Class 文件 (类名,方法名等) 没有发生变化(增加空格,换行,增加注释等等),就算再编译多次,serialVersionUID 也不会变化的。
五、SpringBoot 中的序列化和反序列化
在项目开发中,我们的类并没有实现 Serializable 接口,实际上这是 Spring 框架帮我们做了一些事情,Spring 并不是直接把 User 对象进行网络传输,而是把 User 对象转换成 json 格式的字符串,然后再进行传输的,而 String 类实现了 Serializable 接口并且显示指定了 serializableUID
public final class Stringimplements java.io.Serializable, Comparable<String>, CharSequence {private final char value[];private int hash; // Default to 0private static final long serialVersionUID = -6849794470754667710L;
Json 是一种轻量级的文本数据交换格式,在 Json 字符串中 {} 用来表示对象,[ ] 用来表示列表,数据以 key:value 的形式存放,如:
{"name":"zhangsan","age":"22","course":["java","python"]
}
在 SpringBoot 中,想要一个接口接收 Json 格式的数据并返回 Json 格式的数据,前端将 http 请求头 “Accept” 设置为 “application/json”,Content-Type 为 "application/json"
中间件只需要在 Controller 类中做如下定义:
@RestController
@RequestMapping("/equity")
public class EquityExpose {@PostMapping("/list")@ApiOperation(value = "权益列表", notes = "权益列表")public ApiResultResponse<GetEquityResponse>> list(@RequestBody GetEquityRequest request) {return service.list(request);}}
在 Controller 中使用 @ResponseBody 注解即可返回 Json 格式的数据,而 @RestController 注解包含了 @ResponseBody 注解,所以默认情况下,@RestController 即可将返回的数据结构转换成 Json 格式。
这些注解之所以可以进行 Json 与 JavaBean 之间的相互转换,就是因为 HttpMessageConverter 发挥着作用。
org.springframework.http.converter.HttpMessageConverter 是一个策略接口,是 Http request 请求和 response 响应的转换器,该接口只有五个方法,它的 canRead() 方法返回 true,然后它的 read() 方法会从请求中读出请求参数,绑定到 readString() 方法的 string 变量中。
当 SpringMVC 执行 readString 方法后,由于返回值标识了 @ResponseBody,SpringMVC 将使用 StringHttpMessageConverter 的 write() 方法,将结果作为 String 值写入响应报文,当然,此时 canWrite() 方法返回 true。
public interface HttpMessageConverter<T> {//判断当前转换器是否可以解析前端传来的数据boolean canRead(Class<?> clazz, MediaType mediaType);//判断当前转换器是否可以将后端数据解析为前端需要的格式boolean canWrite(Class<?> clazz, MediaType mediaType);//获取当前转换器可以解析的数据类型List<MediaType> getSupportedMediaTypes();//读取前端传来的数据T read(Class<? extends T> clazz, HttpInputMessage inputMessage)throws IOException, HttpMessageNotReadableException;//将后台数据转换,返回给前端void write(T t, MediaType contentType, HttpOutputMessage outputMessage)throws IOException, HttpMessageNotWritableException;}
流程图如下:
前端发来请求后,先调用 HttpInputMessage 从输入流中获取 Json 字符串,然后在 HttpMessageConverter 中把 Json 转换为接口需要的形参类型。
六、定制化
当出现特定的需求时,比如:此时需要自定义自己的消息转换器,可以使用 Spring 或者第三方提供的 HttpMessageConverter 如(FastJson,Gson, Jackson)
问题引入字符类型字段为 null 时,输出为 “”,而不是 null
1、引入依赖
<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.70</version>
</dependency>
2、对 FastJsonHttpMessageConverter 进行配置
@Configuration
public class MyWebmvcConfiguration implements WebMvcConfigurer {@Overridepublic void extendMessageConverters(List<HttpMessageConverter<?>> converters) {FastJsonHttpMessageConverter fjc = new FastJsonHttpMessageConverter();FastJsonConfig fj = new FastJsonConfig();//字符类型字段如果为null,则输出"",而非nullfj.setSerializerFeatures(SerializerFeature.WriteNullStringAsEmpty);fjc.setFastJsonConfig(fj);// 将该自定义转换器排在第一个,使其生效converters.add(0, fjc);}
}
3、SerializerFeature 配置属性的解释
属性名称 | 解释 |
QuoteFieldNames | 输出key时是否使用双引号,默认为true |
UseSingleQuotes | 使用单引号而不是双引号,默认为false |
WriteMapNullValue | 是否输出值为null的字段,默认为false。应用场景:前端必须需要所有字段 |
UseISO8601DateFormat | Date使用ISO8601格式输出,默认为false |
WriteNullListAsEmpty | List字段如果为null,输出为[],而不是null |
WriteNullStringAsEmpty | 字符类型字段如果为null,输出为"",而不是null |
WriteNullNumberAsZero | 数值字段如果为null,输出为0,而非null |
WriteNullBooleanAsFalse | Boolean字段如果为null,输出为false,而非null |
SkipTransientField | 如果是true,类中的Get方法对应的Field是transient,序列化时将会被忽略。默认为true |
SortField | 按字段名称排序后输出。默认为false |
配置前:如果字符串类型为 null 的话,输出是为 null
{"id": "11","name": null
}
配置后:如果字符串类型为 null 的话,输出是为 ""
{"id": "11","name": null
}
七、序列化及反序列化相关知识
- 在Java中,只要一个类实现了 java.io.Serializable 接口,那么它就可以被序列化。
- 通过 ObjectOutputStream 和 ObjectInputStream 对对象进行序列化及反序列化。
- 虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,一个非常重要的一点是两个类的序列化 ID 是否一致(就是 private static final long serialVersionUID )。
- 序列化并不保存静态变量。
- 要想将父类对象也序列化,就需要让父类也实现 Serializable 接口。
- Transient 关键字的作用是控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null。
八、过程中遇到的问题及解决办法
1、FastJsonHttpMessageConverter 不生效,没有注入到 Spring 容器中
原因:项目中同时存在了 WebMvcConfigurationSupport 和 WebMvcConfigurer 这两个配置,只有该 WebMvcConfigurationSupport 的配置生效
解决办法:将 WebMvcConfigurationSupport 改造成 WebMvcConfigurer。因为多个WebMvcConfigurer 的配置的话,都是会生效的
2、FastJsonHttpMessageConverter 注入到 Spring 容器中,但是不起作用
原因:由于 converters 是 HttpMessageConverter 的列表list,而新 add 的消息转换器位于列表的最后,所以可能不生效
解决办法:可以使用列表 list 的 add(0, object),converters.add(0,fastJsonHttpMessageConverter),把 fastJsonHttpMessageConverter 插入列表头
九、注意事项
- 注意区分 configureMessageConverters 和 extendMessageConverters方 法的不同,前者会覆盖掉原有的消息转换器集合,而只保留当前的集合,因此如果使用了这个方法,就会覆盖掉默认的消息转换器集合,因此这里得注意配置了新的会不会引起功能的缺失,比如说默认的实际上是支持基本的 @RequestBody,@ResponseBody 功能的,配置了新的也要支持,不能让这两个注解失效。如果担心的话,可以使用 extendMessageConverters 方法配置消息转换器,这样就不会覆盖,确保了安全。
- @Bean 和 写在 WebMvcConfigurer 里面的区别是,注入bean的方式,这种方法加入的转换器排序是第一位。实现 WebMvcConfigurer 接口,这种方法加入的转换器排序是最后一位。
十、参考文档
- WebMvcConfigurationSupport 和 WebMvcConfigurer 区别和同时使用产生的问题-解决
- SpringBoot的序列化和反序列化
- spring-boot2.x,使用EnableWebMvc注解导致的自定义HttpMessageConverters不可用
相关文章:

【SpringBoot】序列化和反序列化介绍
一、认识序列化和反序列化 Serialization(序列化)是一种将对象以一连串的字节描述的过程;deserialization(反序列化)是一种将这些字节重建成一个对象的过程。将程序中的对象,放入文件中保存就是序列化&…...
Android 升级软件后清空工厂模式测试进度
Android 升级软件后清空工厂模式测试进度 最近收到项目需求反馈:升级软件后,进入工厂模式测试项,界面显示测试项保留了升级前的测试状态(有成功及失败),需修改升级软件后默认清空测试项测试状态,具体修改参照如下: /…...
Promise原理、以及Promise.race、Promise.all、Promise.resolve、Promise.reject实现;
为了向那道光亮奔过去,他敢往深渊里跳; 于是今天朝着Promise的实现前进吧,写了四个小时,终于完结撒花; 我知道大家没有耐心,当然我也坐的腰疼,直接上代码,跟着我的注释一行行看过去…...
mysql---MHA(高可用)
MHA概述 magterhight availabulity :基于主库的高可用环境下,主故障切换基础要求:主从架构 (一主两从)解决mysql的单点故障问题,一旦数据库崩溃,MHA会在0-30s内这东东完成故障切换。复制方式:半…...

人工智能基础_机器学习032_多项式回归升维_原理理解---人工智能工作笔记0072
现在开始我们来看多项式回归,首先理解多维 原来我们学习的使用线性回归,其实就是一条直线对吧,那个是一维的,我们之前学的全部都是一维的对吧,是一维的,然后是多远的,因为有多个x1,x2,x3,x4... 但是比如我们有一个数据集,是上面这种,的如果用一条直线很难拟合,那么 这个时候,…...
C#截取范围
string[] strs new string[]{"1e2qe","23123e21","3ewqewq","4fewfew","5fsdfds"};var list strs[1..2];Range p 0..3;var list strs[Range];...

用 winget 在 Windows 上安装 kubectl
目录 kubectl 是什么? 安装 kubectl 以管理员身份打开 PowerShell 使用 winget 安装 kubectl 测试一下,确保安装的是最新版本 导航到你的 home 目录: 验证 kubectl 配置 kubectl 是什么? kubectl 是 Kubernetes 的命令行工…...

1 Supervised Machine Learning Regression and Classification
文章目录 Week1OverViewSupervised LearningUnsupervised LearningLinear Regression ModelCost functionGradient Descent Week2Muliple FeatureVectorizationGradient Descent for Multiple RegressionFeature ScalingGradient DescentFeature EngineeringPolynomial Regress…...

Antv/G2 折线图 DataSet 数据展开成指定格式
DataSet 文档 G2 3.2 DataSet 文档 Demo: <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><m…...
物理问题中常见的分析问题----什么样的函数性质较好
物理问题中常见的积分符号位置交换问题 重极限与累次极限 高数下的定义 累次极限:求极限时需要遵循一定的顺序重极限:任意方向趋于的极限 两者之间的关系: 两者没啥关系存在累次极限存在而不相等的函数...... 求和符号与积分符号互换--逐项积…...
8 Go的函数
概述 在上一节的内容中,我们介绍了Go的指针,包括:使用指针、空指针、指针数组、指向指针的指针等。在本节中,我们将介绍Go的函数。函数允许开发者将相关的代码组织在一起,并将其命名,以便在其他地方进行调用…...

算法笔记-第九章-二叉树的遍历(待整理)
算法笔记-第九章-二叉树的遍历 二叉树的先序遍历二叉树的中序遍历二叉树的先序遍历 //二叉树的先序遍历 #include <cstdio> #include <vector> using namespace std;const int MAXN = 50;struct Node //用结构体表示左子树和右子树的数据 {int l, r; } nodes[MAXN]…...

C语言从入门到精通之【字符串】
C语言没有专门用于储存字符串的变量类型,字符串都被储存在char类型的数组中。数组由连续的存储单元组成,字符串中的字符被储存在相邻的存储单元中,每个单元储存一个字符,每个字符占1个字节。 数组末尾位置的字符\0。这是空字符&am…...

超详细!必看!!STM32--时钟树原理
一、什么是时钟? 时钟是单片机的脉搏,是系统工作的同步节拍。单片机上至CPU,下至总线外设,它们工作时序的配合,都需要一个同步的时钟信号来统一指挥。时钟信号是周期性的脉冲信号。 二、什么是时钟树? S…...
用 Golang 采集 Nginx 接口流量大小
简介 在开发和运维中,我们经常需要监控和分析服务器的接口流量大小,特别是对于部署了 Nginx 的服务器。本文将介绍如何使用 Golang 采集 Nginx 接口流量大小,并展示如何将这些数据进行实时监控和分析。 步骤一:准备工作 在开始…...
Linux java jar启停脚本(合并版)
#包文件路径及名称(目录按照各自配置) APP_NAME=/opt/whkc/gs/app-java.jar#查询进程,并杀掉当前jar/java程序 pid=`ps -ef|grep app-java.jar | grep -v grep | awk {print $2}` kill...

计算机毕业设计选题推荐-公共浴池微信小程序/安卓APP-项目实战
✨作者主页:IT毕设梦工厂✨ 个人简介:曾从事计算机专业培训教学,擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…...

sqli-labs关卡13(基于post提交的单引号加括号的报错盲注)通关思路
文章目录 前言一、回顾第十二关知识点二、靶场第十三关通关思路1、判断注入点2、爆显位3、爆数据库名4、爆数据库表5、爆数据库列6、爆数据库关键信息 总结 前言 此文章只用于学习和反思巩固sql注入知识,禁止用于做非法攻击。注意靶场是可以练习的平台,…...

SparkAi创作系统ChatGPT网站源码+详细搭建部署教程+AI绘画系统+支持GPT4.0+Midjourney绘画
一、AI创作系统 SparkAi创作系统是基于OpenAI很火的ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统,支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美,可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如…...
shiro默认session设置永不超时
Shiro默认情况下session是有超时时间的,而不是永不超时。默认的超时时间是30分钟,可以通过修改Shiro的配置文件来更改超时时间。如果想要让session永不超时,可以将超时时间设置为一个很大的值,例如Integer.MAX_VALUE。 以下是修改…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...
React Native 导航系统实战(React Navigation)
导航系统实战(React Navigation) React Navigation 是 React Native 应用中最常用的导航库之一,它提供了多种导航模式,如堆栈导航(Stack Navigator)、标签导航(Tab Navigator)和抽屉…...

高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...
Spring Boot面试题精选汇总
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成
厌倦手动写WordPress文章?AI自动生成,效率提升10倍! 支持多语言、自动配图、定时发布,让内容创作更轻松! AI内容生成 → 不想每天写文章?AI一键生成高质量内容!多语言支持 → 跨境电商必备&am…...

IT供电系统绝缘监测及故障定位解决方案
随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...

【论文阅读28】-CNN-BiLSTM-Attention-(2024)
本文把滑坡位移序列拆开、筛优质因子,再用 CNN-BiLSTM-Attention 来动态预测每个子序列,最后重构出总位移,预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵(S…...

Redis数据倾斜问题解决
Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中,部分节点存储的数据量或访问量远高于其他节点,导致这些节点负载过高,影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...

Kafka入门-生产者
生产者 生产者发送流程: 延迟时间为0ms时,也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于:异步发送不需要等待结果,同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...