Java WEB技術-序列化和反序列化認識(SpringBoot的Jackson序列化行為?如何打破序列化過程的駝峰規則?如何解決學序列化循環引用問題?)

一、什么是序列化和反序列化

在java項目中,對象序列化和反序列化通常用于對象的存儲或網絡傳輸等。如:服務端創建一個JSON對象,對象如何在網絡中進行傳輸呢?我們知道網絡傳輸的數據通常都是字節流的形式,對象想要在網絡上傳輸,不例外,也必須要轉化成字節流才行。

將對象轉換為字節流的過程就是對象的序列化過程,轉化后的字節數據就可以在網絡中進行傳輸了;接收端接收到這些字節數據后將其還原為原始對象的過程,也就是反序列化。通過序列化和反序列化的方式,即可以實現對象在不通節點之間進行網絡傳輸了。

1、服務端對象創建與序列化

1、對象創建

首先,在服務端根據業務邏輯或用戶請求創建相應的Java對象。例如,一個包含用戶信息的對象User。

2、選擇序列化方式

通常有兩種方式:
(1)、手動實現Serializable接口
如果使用Java原生的序列化機制,需要讓該類實現java.io.Serializable接口。這是一種標記接口,表明該類的對象可以被序列化。

(2)、使用第三方庫(推薦)
也可以使用如Jackson(JSON)、Gson(JSON)、Fastjson或者Protocol Buffers等第三方庫進行序列化,這些庫提供了更靈活的數據格式支持(如JSON、XML等),并可能具有更好的性能或易用性。

3、觸發序列化

當你需要通過網絡發送對象(比如HTTP響應、RPC調用等)或保存對象到文件系統時,就會觸發序列化過程。這通常涉及到將對象轉換為字節流或特定格式的字符串。如:Controller類上添加了@RestController注解,或接口上添加了@ResponseBody注解。

代碼示例:

@ResponseBody
public MyDto getData() {return myDto; // 自動轉為 JSON
}

解釋:
如上示例,在接口上添加@ResponseBody注解,springboot會在接口返回結果時,自動將對象轉JSON字符串進行序列化處理。

或,對于第三方庫,比如使用Jackson來序列化一個對象為JSON字符串:
代碼示例:

ObjectMapper mapper = new ObjectMapper();
String jsonInString = mapper.writeValueAsString(user);

2、網絡傳輸

序列化后的數據(字節流或字符串形式)通過網絡協議(如HTTP、TCP/IP等)從服務端發送給客戶端。在這個過程中,數據可能會經過編碼、加密等處理以確保安全性和兼容性。

說明下:
字節流和字符流是可以通過編碼的方式相互轉換的。如下的示例展示通過UTF-8編碼,進行字符串和字節流的互轉:

 String originalString = "Hello, 世界!";byte[] byteArray = originalString.getBytes(StandardCharsets.UTF_8);String decodedString = new String(byteArray, StandardCharsets.UTF_8);

**Springboot處理序列化的方式:**會先將返回的對象轉json文本字符串,之后在通過編碼方式,如UTF-8,將JSON字符串編碼為二進制流。在封裝到HTTP包的請求體中,同時在請求頭中指定content-type:application/json,瀏覽器會根據返回內容類型自動解析文件內容。

3、接收端處理

1、接收數據

客戶端接收到從服務端傳來的字節流或字符串數據。

2、反序列化

如果是Java原生序列化格式,可以使用ObjectInputStream來讀取字節流并恢復對象。
代碼示例:

 try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.dat"))) {User user = (User) ois.readObject();} catch (IOException | ClassNotFoundException e) {e.printStackTrace();}

如果是使用了第三方庫進行序列化,則需要相應的反序列化方法。例如,使用Jackson將JSON字符串轉換回Java對象:
代碼示例:

ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(jsonInString, User.class);    // jsonInString為接收的字符串
3、處理對象

一旦對象被成功反序列化,就可以在客戶端進行進一步的操作,比如顯示給用戶、存儲在本地數據庫中等。

4、序列化和反序列化的整體流程

返回對象–>json字符串–>編碼為二進制流–>封裝HTTP報文–>網絡傳輸–>客戶端解碼–>反序列化為對象。

二、SpringBoot中的Jackson ObjectMapper是如何工作的?

1、Java原生Serializable序列化和第三方的Jackson序列化

在這里插入圖片描述

  • Serializable主要用于Java對象的深拷貝、RMI、緩存(如Redis存為二進制)等場景。
  • Jackson主要用于Web接口的數據交換(JSON/XML)。

2、Spring Boot中的ObjectMapper是如何工作的?

1、注入ObjectMapper對象(即Jackson的序列化器)

代碼示例:

@Bean
public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {ObjectMapper objectMapper = builder.createXmlMapper(false).build();
//        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL)    // 為null字段不返回objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS)     // 返回所有字段,包含null值的字段.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS).registerModule(new ParameterNamesModule()).registerModule(new Jdk8Module()).registerModule(new JavaTimeModule());/*** 序列換成json時,將所有的long變成string* 因為js中得數字類型不能包含所有的java long值*/SimpleModule module = new SimpleModule();module.addSerializer(Long.class, ToStringSerializer.instance);module.addSerializer(Long.TYPE, ToStringSerializer.instance);// Double類型對象到前端自動去除小數點末尾無效的0module.addSerializer(Double.class, new SmartNumberSerializer());module.addSerializer(Double.TYPE, new SmartNumberSerializer());objectMapper.registerModule(module);return objectMapper;}

解釋:
如上配置并注入Jackson 的序列化器后,Spring Boot的行為會如下:
1、Spring Boot會使用Jackson作為默認的JSON處理庫(通常是jackson-databind),而不是在Java默認的序列化器。
2、這個ObjectMapper實例會被注入到Spring MVC的HttpMessageConverter中,特別是:

  • MappingJackson2HttpMessageConverter
    3、當你使用:
@ResponseBody
@RestController
public MyDto getData() {return myDto; // 自動轉為 JSON
}

Spring就會調用ObjectMapper.writeValueAsString(myDto) 把對象轉成JSON字符串,寫入HTTP響應體。

2、自動進行序列化和反序列化”是如何觸發的?
(1)、序列化(Java對象 → HTTP響應)

當你返回一個對象,如下:

@GetMapping("/user")
public User getUser() {return new User("張三", 25);
}

Spring 執行流程:
1、調用getUser()方法,得到User對象。
2、發現方法上有@ResponseBody(或類上是@RestController)。
3、查找合適的HttpMessageConverter。
4、找到MappingJackson2HttpMessageConverter。
5、調用objectMapper.writeValueAsString(user) → 得到JSON字符串。
6、寫入響應體,Content-Type設為 application/json。

如上就是Spring的“自動序列化”行為。

(2)、反序列化(HTTP請求體 → Java對象)

當你接收一個JSON請求體。如下:

@PostMapping("/user")
public String createUser(@RequestBody User user) {// user 已經從 JSON 自動解析出來了return "OK";
}

Spring 執行流程:
1、收到POST請求,Content-Type: application/json。
2、發現參數上有@RequestBody。
3、查找合適的HttpMessageConverter。
4、找到MappingJackson2HttpMessageConverter。
5、調用objectMapper.readValue(jsonString, User.class) → 構造出User對象。
6、注入到方法參數。

如上就是Spring的“自動反序列化”行為。

3、總結:

在Spring Boot Web項目中,對象通過網絡傳輸時的“序列化”指的是Jackson將對象轉為JSON串的過程,而不是Java原生的Serializable機制。只要配置了ObjectMapper,Spring就會在@ResponseBody和@RequestBody處自動完成序列化和反序列化,對象類無需實現Serializable接口。

三、序列化過程中的循環引用是什么問題?怎么解決?

這是一個非常重要且常見的問題:序列化過程中的循環引用(Circular Reference)。它在Java對象序列化(尤其是JSON序列化)中非常容易出現,如果不處理,會導致錯誤或無限遞歸。

1、什么是循環引用?

當兩個或多個對象之間相互引用,形成一個閉環時,就產生了循環引用。

代碼示例:

public class User {private Long id;private String name;private List<Order> orders;// getter/setter
}public class Order {private Long id;private User user;// getter/setter
}

解釋:
如果集合orders中包含的元素存在相同的值,就會產生循環引用的問題。
造成序列化結果如:
在這里插入圖片描述

2、為什么循環引用在序列化時會出問題?

當你嘗試將user對象序列化成JSON串:

代碼示例:

ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(user);

會發生如下行為:
1、開始序列化user。
2、發現orders字段,開始序列化Order。
3、在Order中發現user字段,又去序列化這個user。
4、這個user又有orders,繼續序列化…
5、→ 無限遞歸!棧溢出(StackOverflowError)

結果:程序崩潰!

3、不同序列化庫如何處理循環引用?

1、Jackson(推薦方案)

Jackson 默認不會拋異常,而是使用**@id和@ref**機制來避免重復和循環。
這也是現在系統的默認行為,更安全,能有效防止棧溢出。

序列化的json示例:

{"id": 1,"name": "張三","orders": [{"id": 101,"user": {"@id": "1"}}]
}

解釋:

  • 第一次出現的user被標記為@id: “1”。
  • 后續再引用時,用{“@id”: “1”}表示“這是之前那個對象”

這樣,程序不會崩潰,也不會無限遞歸。

2、FastJSON

FastJSON默認會檢測循環引用,并用$ref表示重復對象。

默認行為示例:

{"id": 1,"name": "張三","orders": [{"id": 101,"user": {"$ref": "$"}  // $ 表示根對象}]
}

結果:前端可能看不懂$ref,導致解析失敗。

4、解決方案

方案1:關閉循環檢測(有風險)

代碼示例:

JSON.toJSONString(user, SerializerFeature.DisableCircularReferenceDetect);

存在風險(StackOverflowError),但實際項目中用的比較多。

方案2:使用 @JSONField(serialize = false) 忽略反向字段

代碼示例:

public class Order {@JSONField(serialize = false)private User user;
}

4、如何打破駝峰命名規則

Java對象字段名是小寫(駝峰命名),但有時候需要生成的JSON報文字段必須是大寫字母開頭(如ProtocolType),而Jackson默認使用駝峰轉小寫開頭,這會導致生成的JSON字段名變成小寫(如protocolType)。

1、問題原因

Jackson序列化時默認使用Java駝峰命名到JSON小駝峰(lowerCamelCase)的映射規則。

代碼示例:

private Integer ProtocolType;  // → JSON: "protocolType"(首字母小寫)

解釋:
定義的字段都是大寫開頭的ProtocolType,因為Jackson默認使用Java的駝峰命名規則,造成返回的json中是protocolType開頭小寫的字段。

2、解決方案:使用@JsonProperty顯式指定字段名

需要在需要大寫的字段上加上@JsonProperty(“原始大寫名”)注解,告訴Jackson序列化時使用指定名稱。

代碼示例:

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.List;@Data
public class InfraredConfigDTO {@JsonProperty("ProtocolType")    // 指定的屬性名為我們要求的大寫開頭的格式private Integer protocolType;    // 屬性定義必須是小寫@JsonProperty("Type")private Integer type;@JsonProperty("Preset")private Integer preset;
}

注意:
屬性字段必須定義成小寫開頭的駝峰規則。@JsonProperty注解注定滿足要求的大寫規則。

生成的JSON效果:

{"ProtocolType": 1,"Type": 2,"Preset": 0
}

向陽前行,Dare To Be!!!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/93748.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/93748.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/93748.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【生活系列】MBTI探索 16 種性格類型

博客目錄一、MBTI 的四個核心維度1. 精力來源&#xff1a;外向&#xff08;E&#xff09;vs 內向&#xff08;I&#xff09;2. 信息獲取方式&#xff1a;感覺&#xff08;S&#xff09;vs 直覺&#xff08;N&#xff09;3. 決策方式&#xff1a;思考&#xff08;T&#xff09;v…

innovus在ccopt_design時設置update io latency

我正在「拾陸樓」和朋友們討論有趣的話題,你?起來吧? 拾陸樓知識星球入口 往期文章:

電腦出現英文字母開不了機怎么辦 原因與修復方法

當您按下電腦開機鍵&#xff0c;屏幕上卻只顯示一串串陌生的英文字母&#xff0c;無法正常進入系統時&#xff0c;這通常是電腦在向您“求救”。這種情況可能由多種原因引起&#xff0c;從外部設備沖突到系統文件損壞&#xff0c;都可能導致電腦無法啟動。不必過于焦慮&#xf…

CSS和XPATH選擇器對比

1、優缺點比較特性CSS選擇器XPath語法復雜度簡潔易讀較為復雜性能通常更快可能較慢向上遍歷不支持支持&#xff08;可選擇父元素&#xff09;文本內容選擇有限支持完全支持索引選擇支持&#xff08;:nth-child&#xff09;支持&#xff08;position()&#xff09;瀏覽器兼容性優…

libomxil-bellagio移植到OpenHarmony

當使用mesa3dcangh提供的amd顯卡驅動時&#xff0c;想利用 Mesa 提供的圖形硬件加速能力&#xff0c;來支持視頻編解碼操作時。需要依賴libomxil-bellagio庫&#xff0c;現在成果分享如下&#xff1a; 基礎知識 1.OpenHarmony中mesa3d amd顯卡驅動編譯 2.OpenHarmony中基于G…

uvm-tlm-sockets

TLM 2.0引入了套接字(Socket)機制&#xff0c;實現發起方(initiator)與目標方(target)組件間的異步雙向數據傳輸。套接字與端口(port)和導出(export)同源&#xff0c;均繼承自uvm_port_base基類。發起事務的組件使用發起方套接字(initiator socket)&#xff0c;稱為發起方&…

AI 如何評價股票:三七互娛(SZ:002555),巨人網絡(SZ:002558)

三七互娛&#xff08;SZ:002555&#xff09;作為國內領先的游戲公司&#xff0c;其股票表現需結合財務健康度、行業地位、戰略布局及潛在風險綜合評估。以下從多維度展開分析&#xff1a; 一、財務表現&#xff1a;增長乏力與高分紅并存營收與利潤雙降 2025年Q1營收42.43億元&a…

Vibe Coding:AI驅動開發的安全暗礁與防護體系

當OpenAI聯合創始人Andrej Karpathy在2025年初的推文里首次提及"Vibe Coding"時&#xff0c;這個概念迅速在開發者社區引發共鳴——它描繪了一種誘人的開發模式&#xff1a;開發者用自然語言描述需求&#xff0c;AI接管代碼生成、修改甚至調試&#xff0c;整個過程以…

四、主輔源電路

一、主輔源結構主輔源采用反激變換器拓撲&#xff0c;輸入供電有母線供電、電池輔源供電、電網輔源供電。開關管為一個高耐壓NMOS功率管。主控芯片采用ICE3BS03LJG&#xff0c;其主要參數如下&#xff1a;商品目錄AC-DC控制器和穩壓器是否隔離隔離工作電壓10.5V~26V開關頻率65k…

制造業企業如何保障文件外發圖紙數據安全的?

在制造業的發展進程中&#xff0c;文件外發是必不可少的環節&#xff0c;但這也給圖紙數據安全帶來了諸多挑戰。一旦圖紙數據泄露&#xff0c;企業的核心競爭力可能會受到嚴重損害。那么&#xff0c;制造業企業該如何保障文件外發圖紙數據安全呢&#xff1f;建立完善的管理制度…

RAG:讓AI更聰明的“外接大腦“ | AI小知識

RAG&#xff1a;讓AI更聰明的"外接大腦" 什么是RAG&#xff1f; 想象你在參加知識競賽&#xff0c;突然遇到不會的題目。這時你掏出手機快速搜索正確答案——這就是RAG&#xff08;Retrieval-Augmented Generation&#xff0c;檢索式增強生成&#xff09;的工作原理。…

TCP 連接管理 之 三次握手詳解

TCP 連接管理 之 三次握手詳解 &#xff08;一&#xff09;TCP三次握手詳細過程及狀態變化 1. 第一次握手&#xff08;客戶端 → 服務器&#xff09; 報文標志位&#xff1a;SYN1&#xff08;同步序列號&#xff09;&#xff0c;ACK0&#xff08;首次握手無確認&#xff09;序列…

day066-內容分發網絡(CDN)與web應用防火墻(WAF)

文章目錄0. 老男孩思想-老男孩名言警句1. 云產品日常管理2. 創建快照策略3. CDN 內容分發網絡3.1 添加域名3.2 配置CNAME3.3 測試3.4 CDN刷新/預熱3.5 命中率3.5.1 查看命中率3.5.2 CDN命中率低怎么辦&#xff1f;3.6 訪問控制3.7 流量限制4. WAF web應用防火墻4.1 使用WAF4.2 …

Redis高頻問題全解析

Q1: Redis為什么這么快&#xff1f; Redis速度快主要有四個核心原因。首先是基于內存操作&#xff0c;所有數據都存儲在內存中&#xff0c;避免了磁盤I/O的開銷&#xff0c;內存讀寫速度比磁盤快幾萬倍。其次采用單線程模型&#xff0c;避免了多線程環境下的線程切換和鎖競爭帶…

【MATLAB】(三)數據類型與運算符

一.MATLAB的數據存儲類型一般而言&#xff0c;在 MATLAB 中數據的存儲與計算都是以雙精度進行的&#xff0c;但有多種顯示形式。在默認情況下&#xff0c;若數據為整數&#xff0c;就以整數表示;若數據為實數&#xff0c;則以保留小數點后4位的精度近似表示。用戶可以改變數字顯…

智能客服 VS 人工客服:電商企業該選哪種服務模式?

在數字化浪潮的推動下&#xff0c;電商企業對客戶服務的效率和質量提出了更高要求。智能客服與人工客服作為兩種主流服務模式&#xff0c;各有優劣。本文將從服務效率、成本投入、客戶體驗等維度&#xff0c;客觀分析兩者的特點&#xff0c;幫助電商企業做出合理選擇。一、服務…

前端基礎之《Vue(28)—Vue3 ref相關API》

ref相關API介紹1、ref()&#xff08;1&#xff09;ref介紹 ref用于定義基本數據類型&#xff0c;比如&#xff1a;string / boolean / number等&#xff08;因為這幾個沒辦法代理&#xff09;。 ref的背后是使用reactive來實現的響應式。 使用.value來訪問變量的值。&#xff0…

項目管理中如何避免延期?核心策略分析

在項目管理中避免延期&#xff0c;并非依賴于單一技巧&#xff0c;而是要構筑一個系統性的、多維度的防御體系。其核心策略涵蓋了&#xff1a;進行全面細致的前期規劃與估算、實施嚴格的范圍管理與變更控制、建立主動式全過程風險管理機制、維持高透明度的持續溝通、以及采用數…

【計算機視覺與代碼大模型全景解析:從理論基礎到學習路線】

&#x1f4d8;計算機視覺與代碼大模型全景解析&#xff1a;從理論基礎到學習路線&#x1f4d1; 目錄1?? 摘要2?? 計算機視覺與大模型基礎理論??2.1 &#x1f9e0; 深度卷積神經網絡基礎原理??2.2 &#x1f441;??&#x1f5e8;? 計算機視覺的技術發展與視覺圖靈概念…

力扣-104. 二叉樹的最大深度

題目鏈接 104. 二叉樹的最大深度 class Solution {public int maxDepth(TreeNode root) {if (root null)return 0;return Math.max(maxDepth(root.left), maxDepth(root.right)) 1;} }小結&#xff1a;秒了&#xff01;&#xff01;&#xff01;