【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。应用场景:前端必须需要所有字段
UseISO8601DateFormatDate使用ISO8601格式输出,默认为false
WriteNullListAsEmptyList字段如果为null,输出为[],而不是null
WriteNullStringAsEmpty字符类型字段如果为null,输出为"",而不是null
WriteNullNumberAsZero数值字段如果为null,输出为0,而非null
WriteNullBooleanAsFalseBoolean字段如果为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 插入列表头

九、注意事项

  1. 注意区分 configureMessageConverters 和 extendMessageConverters方 法的不同,前者会覆盖掉原有的消息转换器集合,而只保留当前的集合,因此如果使用了这个方法,就会覆盖掉默认的消息转换器集合,因此这里得注意配置了新的会不会引起功能的缺失,比如说默认的实际上是支持基本的 @RequestBody,@ResponseBody 功能的,配置了新的也要支持,不能让这两个注解失效。如果担心的话,可以使用 extendMessageConverters 方法配置消息转换器,这样就不会覆盖,确保了安全。
  2. @Bean 和 写在 WebMvcConfigurer 里面的区别是,注入bean的方式,这种方法加入的转换器排序是第一位。实现 WebMvcConfigurer 接口,这种方法加入的转换器排序是最后一位。

十、参考文档

  • WebMvcConfigurationSupport 和 WebMvcConfigurer 区别和同时使用产生的问题-解决
  • SpringBoot的序列化和反序列化  
  • spring-boot2.x,使用EnableWebMvc注解导致的自定义HttpMessageConverters不可用


 

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

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

相关文章

回顾 — SFA:简化快速 AlexNet(模糊分类)

模糊图像的样本 一、说明 在本文回顾了基于深度学习的模糊图像分类&#xff08;SFA&#xff09;。在本文中&#xff1a;Simplified-Fast-AlexNet (SFA)旨在对图像是否因散焦模糊、高斯模糊、雾霾模糊或运动模糊而模糊进行分类。 二、大纲 图像模糊建模简要概述简化快速 AlexNet…

vscode 快速打印console.log

第一步 输入这些 {// Print Selected Variabl 为自定义快捷键中需要使用的name&#xff0c;可以自行修改"Print Selected Variable": {"body": ["\nconsole.log("," %c $CLIPBOARD: ,"," background-color: #3756d4; padding:…

action3录制出来的LRF文件的正确打开方式

你会发现使用大疆的产品录制出来的视频会有两种格式&#xff1a;LRF和MP4 这个LRF文件是低分辨率、低码率的预览文件&#xff0c;非常适合预览。 这个文件可以直接通过修改文件后缀转化为.mp4格式

14——1

这句话的意思是&#xff0c;如图中月份12天数23时&#xff0c;就是1223&#xff1b;当月份9天数2时&#xff0c;就是0902. 可以看到在上面给出的数组元素中&#xff0c;并没有连续挨在一起的2023数字元素——就有人可能输出答案0。 所以这里要看一下—— ——子序列的含义&…

云服务器如何选?腾讯云2核2G3M云服务器88元一年!

作为一名程序员&#xff0c;在选择云服务器时&#xff0c;我们需要关注几个要点&#xff1a;网络稳定性、价格以及云服务商的规模。这些要素将直接影响到我们的使用体验和成本效益。接下来&#xff0c;我将为大家推荐一款性价比较高的轻应用云服务器。 腾讯云双11活动 腾讯云…

解密图像处理中的利器——直方图与均衡化

直方图与均衡化是数字图像处理中常用的重要工具&#xff0c;它们能够帮助我们更好地理解和改善图像的亮度分布。本文将首先介绍直方图的基本概念以及其在图像处理中的意义&#xff0c;接着详细阐述直方图均衡化的原理和算法。同时&#xff0c;文章将探讨直方图均衡化在图像增强…

利用网络管理解决方案简化网络运维

当今的网络正朝着提高敏捷性和动态功能的方向发展&#xff0c;以支持高级网络要求和关键业务流程&#xff0c;这导致 IT 基础架构也跨越无线、虚拟和混合环境。但是&#xff0c;随着网络的快速发展&#xff0c;如果没有合适的解决方案&#xff0c;IT 管理员很难管理它们&#x…

基于ChatGPT的文本生成艺术框架—WordArt Designer

WordArt Designer是一个基于gpt-3.5 turbo的艺术字生成框架&#xff0c;包含四个关键模块:LLM引擎、SemTypo、Styltypo和TextTypo模块。由gpt-3.5 turbo驱动的LLM引擎可以解释用户输入&#xff0c;从而将抽象概念转化为具体的设计。 SemTypo模块使用语义概念优化字体设计&…

LoadRunner脚本编写之三(事务函数)

关于脚本的这块&#xff0c;前两篇都在讲C语言&#xff0c;其实&#xff0c;要整理点实用的东西挺难&#xff0c;在应用中多对录制的脚本分析&#xff0c;但对于新手学脚本确实无从下手。 先贴一个脚本&#xff1a; 完整代码&#xff1a; 重点代码部分&#xff1a; Action(…

【python】Django——templates模板、静态文件、django模板语法、请求和响应

笔记为自我总结整理的学习笔记&#xff0c;若有错误欢迎指出哟~ 【Django专栏】 Django——django简介、django安装、创建项目、快速上手 Django——templates模板、静态文件、django模板语法、请求和响应 Django——连接mysql数据库 Django——templates模板、静态文件、djang…

如何实现Redisson分布式锁

首先&#xff0c;不要将分布式锁想的太复杂&#xff0c;如果我们只是平时业务中去使用&#xff0c;其实不算难&#xff0c;但是很多人写的文章不能让人快速上手&#xff0c;接下来&#xff0c;一起看下Redisson分布式锁的快速实现 Redisson 是一个在 Redis 的基础上实现的 Java…

公益SRC实战|SQL注入漏洞攻略

目录 一、信息收集 二、实战演示 三、使用sqlmap进行验证 四、总结 一、信息收集 1.查找带有ID传参的网站&#xff08;可以查找sql注入漏洞&#xff09; inurl:asp idxx 2.查找网站后台&#xff08;多数有登陆框&#xff0c;可以查找弱口令&#xff0c;暴力破解等漏洞&…

SpringBoot和Spring源码下载

1.下载&#xff1a;在一个空的干净地创建一个文件夹叫springsourcecode&#xff0c;其实叫什么都行的。 git clone https://github.com/spring-projects/spring-framework.git 2.JDK要和gradle匹配 我们要21的&#xff0c;今天为止2023年11月13日&#xff0c;idea是2023.2。 …

考研分享第3期 | 211本378分上岸大连理工电子信息经验贴

考研分享第3期 | 211本378分上岸大连理工电子信息经验贴 一、个人信息 姓名&#xff1a;Ming 本科院校&#xff1a;某211学校电子信息工程学院 电子科学与技术专业 上岸院校&#xff1a;大连理工大学 电子信息与电气工程学部 电子信息&#xff08;0854&#xff09; 择校意…

数据中心:精密空调监控,这招太高效了!

在当今日益复杂的工业环境中&#xff0c;精密空调系统的监控和管理变得至关重要。随着科技的迅猛发展&#xff0c;各行各业对温度、湿度和空气质量等参数的高度控制需求不断增加。 精密空调监控系统通过实时数据采集、分析和反馈&#xff0c;为企业提供了可靠的手段来确保生产环…

settings.json配置

settings.json配置 {"editor.tabSize": 2,"git.ignoreWindowsGit27Warning": true,"workbench.editor.untitled.hint": "hidden","security.workspace.trust.untrustedFiles": "open","[vue]": {"…

dll文件【C#】

加载方法&#xff1a; [DllImport("controlcan.dll")] public static extern UInt32 VCI_OpenDevice(UInt32 DeviceType, UInt32 DeviceInd, UInt32 Reserved); 文件存放位置&#xff1a; 一般放Debug文件夹下。 运行错误&#xff1a; 原因是CPU位数选择不对&…

不变式和橄榄树-UMLChina建模知识竞赛第4赛季第20轮

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 参考潘加宇在《软件方法》和UMLChina公众号文章中发表的内容作答。在本文下留言回答。 只要最先答对前3题&#xff0c;即可获得本轮优胜。第4题为附加题&#xff0c;对错不影响优胜者…

科普测量开关电源输出波形的三种方法及电源波形自动化测试步骤

开关电源波形测试就是对开关电源的输出波形进行检测和分析&#xff0c;观察开关电源参数变化&#xff0c;以此来判断开关电源的性能是否符合要求。好的开关电源对于设备以及整个电路的正常运行是非常重要的&#xff0c;因此开关电源输出波形测试是开关电源测试的重要环节&#…

数据同步工具调研选型:SeaTunnel 与 DataX 、Sqoop、Flume、Flink CDC 对比

产品概述 Apache SeaTunnel 是一个非常易用的超高性能分布式数据集成产品&#xff0c;支持海量数据的离线及实时同步。每天可稳定高效同步万亿级数据&#xff0c;已应用于数百家企业生产&#xff0c;也是首个由国人主导贡献到 Apache 基金会的数据集成顶级项目。 SeaTunnel 主…