一文精通Spring Boot序列化與反序列化(告別數(shù)據(jù)混亂)
大家好,我是加洛斯 ????。作為一名程序員,我深深信奉費(fèi)曼學(xué)習(xí)法——教,是最好的學(xué) ??。這里是我的知識(shí)筆記與分享,旨在把復(fù)雜的東西講明白。如果發(fā)現(xiàn)有誤,萬(wàn)分歡迎你幫我指出來(lái) ??!廢話(huà)不多說(shuō),正文開(kāi)始 ??
在開(kāi)發(fā)中我們經(jīng)常能聽(tīng)到的兩個(gè)詞,序列化與反序列化。很多萌新小白聽(tīng)到這個(gè)概念肯定是滿(mǎn)腦門(mén)問(wèn)號(hào),接下來(lái)我將用通俗易懂的語(yǔ)言和妙趣橫生的比喻,深入淺出的將序列化與反序列的基礎(chǔ)以及原理給大家講述出來(lái)。
一、基礎(chǔ)概念
簡(jiǎn)單來(lái)說(shuō),序列化 和 反序列化 就是 將數(shù)據(jù)在不同格式之間進(jìn)行轉(zhuǎn)換 的過(guò)程,目的是為了在不同的系統(tǒng)或不同的上下文之間傳輸和存儲(chǔ)數(shù)據(jù)。
- 序列化 (
Serialization): 將 對(duì)象 轉(zhuǎn)換為 可傳輸或可存儲(chǔ)的格式(通常是 JSON、XML 等)。- 在 Spring Boot 中的場(chǎng)景: 當(dāng)你的 Controller 方法返回一個(gè) Java 對(duì)象時(shí),Spring Boot 會(huì)自動(dòng)將它序列化成 JSON 字符串,然后通過(guò) HTTP 響應(yīng)發(fā)送給前端(如瀏覽器、手機(jī) App)。
- 反序列化 (
Deserialization): 將 可傳輸或可存儲(chǔ)的格式(如 JSON、XML)轉(zhuǎn)換回 對(duì)象。- 在 Spring Boot 中的場(chǎng)景: 當(dāng)前端通過(guò) HTTP 請(qǐng)求(如 POST、PUT)發(fā)送一個(gè) JSON 數(shù)據(jù)到你的服務(wù)器時(shí),Spring Boot 會(huì)自動(dòng)將這個(gè) JSON 字符串反序列化成你 Controller 方法參數(shù)中定義的 Java 對(duì)象。
那么簡(jiǎn)單來(lái)講就是,序列化是將對(duì)象轉(zhuǎn)化為JSON等格式,反序列化就是將JSON等格式轉(zhuǎn)化為對(duì)象。
二、具體應(yīng)用
Spring Boot 通過(guò)Spring MVC模塊和默認(rèn)集成的Jackson庫(kù),讓這個(gè)過(guò)程變得極為簡(jiǎn)單,但是其背后的原理卻是相當(dāng)復(fù)雜,我們先從簡(jiǎn)單的開(kāi)始來(lái),看看怎么用以及干了什么。
2.1 反序列
假設(shè)前端要?jiǎng)?chuàng)建一個(gè)用戶(hù),它發(fā)送了一個(gè) POST 請(qǐng)求,請(qǐng)求體是 JSON:
{
"name": "張三",
"age": 25,
"email": "zhangsan@example.com"
}對(duì)應(yīng)的 User 類(lèi)(Java Bean):
// 必須有無(wú)參構(gòu)造函數(shù)
// 必須有為所有屬性生成的 Getter 和 Setter 方法
// lombok注解 自動(dòng)生成getter與setter方法與無(wú)參構(gòu)造函數(shù)
@Data
public class User {
private String name;
private Integer age;
private String email;
}在你的 Spring Boot Controller 中,你會(huì)這樣寫(xiě):
@RestController
public class UserController {
@PostMapping("/users")
public ResponseEntity<String> createUser(@RequestBody User user) {
// 在這個(gè)方法內(nèi)部,User 對(duì)象已經(jīng)是一個(gè)可以直接使用的 Java 對(duì)象了
System.out.println("用戶(hù)名:" + user.getName());
System.out.println("年齡:" + user.getage());
// ... 保存用戶(hù)到數(shù)據(jù)庫(kù)等操作
return ResponseEntity.ok("用戶(hù)創(chuàng)建成功");
}
}這里發(fā)生了什么?
- 前端發(fā)送 JSON 數(shù)據(jù)。
- Spring Boot 看到
@RequestBody注解和User這個(gè)參數(shù)類(lèi)型。 - Spring Boot 使用 Jackson 庫(kù) 反序列化 JSON 字符串,根據(jù) JSON 的 key 匹配
User類(lèi)的屬性(如name匹配name),并創(chuàng)建一個(gè)User對(duì)象。 - 這個(gè)創(chuàng)建好的
User對(duì)象被傳入你的createUser方法中。
這樣我們就從前端發(fā)送的JSON中解析了我們需要的數(shù)據(jù),是不是很簡(jiǎn)單,現(xiàn)在沒(méi)有配置任何文件,緊靠SpringBoot自帶的Jackson庫(kù)就完成了反序列化。
2.2 序列化
同樣一個(gè) Controller,我們寫(xiě)一個(gè)查詢(xún)用戶(hù)的方法:
@RestController
public class UserController {
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
// 假設(shè)從數(shù)據(jù)庫(kù)或其他地方查詢(xún)到了一個(gè)用戶(hù)
User user = new User();
user.setName("李四");
user.setAge(30);
user.setEmail("lisi@example.com");
// 直接返回這個(gè)對(duì)象
return user;
}
}這里發(fā)生了什么?
- 你的方法返回了一個(gè)
User對(duì)象。 - Spring Boot 看到返回值后,使用 Jackson 庫(kù) 序列化 這個(gè)
User對(duì)象。 - 最終,前端收到的 HTTP 響應(yīng)體就是這樣的 JSON 字符串:
{
"name": "李四",
"age": 30,
"email": "lisi@example.com"
}三、Jackson
SpringBoot通過(guò)自動(dòng)配置和默認(rèn)的 Jackson 集成,極大地簡(jiǎn)化了 Web 開(kāi)發(fā)中數(shù)據(jù)格式轉(zhuǎn)換的復(fù)雜性。開(kāi)發(fā)者幾乎不需要編寫(xiě)轉(zhuǎn)換代碼,只需關(guān)注業(yè)務(wù)邏輯(定義對(duì)象、編寫(xiě) Controller),就能輕松實(shí)現(xiàn) RESTful API 的數(shù)據(jù)交互。我們來(lái)學(xué)習(xí)幾個(gè)常用的 Jackson 注解。
@JsonIgnore:在序列化時(shí)忽略某個(gè)屬性。
public class User {
private String name;
@JsonIgnore // 返回給前端的 JSON 中不會(huì)包含 password 字段,保障安全
private String password;
}
@JsonProperty:為屬性指定一個(gè)別名。
public class User {
@JsonProperty("full_name") // 序列化后 JSON 中的 key 是 "full_name"
private String name;
}@JsonFormat:格式化日期字段。
public class User {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
}
@JsonInclude:控制序列化時(shí)包含哪些屬性。
@JsonInclude(JsonInclude.Include.NON_NULL) // 只包含非 null 字段
public class User {
private String name;
private Integer age; // 如果 age 為 null,則序列化結(jié)果中無(wú)此字段
}
@JsonView:定義視圖,實(shí)現(xiàn)不同接口返回不同字段。
public class Views {
public static class Public {} // 公共視圖
public static class Internal extends Public {} // 內(nèi)部視圖,繼承公共視圖
}
public class User {
@JsonView(Views.Public.class)
private String name;
@JsonView(Views.Internal.class)
private String email;
}
@RestController
public class UserController {
@GetMapping("/user/public")
@JsonView(Views.Public.class) // 只序列化 Public 視圖的字段
public User getPublicUser() { ... }
@GetMapping("/user/internal")
@JsonView(Views.Internal.class) // 序列化 Internal 和 Public 視圖的字段
public User getInternalUser() { ... }
}| 過(guò)程 | 方向 | 動(dòng)作 | 比喻 | Spring Boot 中的關(guān)鍵注解 |
|---|---|---|---|---|
| 序列化 | 對(duì)象 -> JSON | 將Java對(duì)象轉(zhuǎn)換為傳輸/存儲(chǔ)格式 | 打包 | (默認(rèn)行為,無(wú)需注解) |
| 反序列化 | JSON -> 對(duì)象 | 將傳輸/存儲(chǔ)格式轉(zhuǎn)換為Java對(duì)象 | 拆包組裝 | @RequestBody |
四、原理
Spring Boot 的序列化與反序列化其背后是一套名為 HttpMessageConverter 的可插拔策略接口。
- 工作原理: 當(dāng) Spring MVC 處理一個(gè) HTTP 請(qǐng)求時(shí),它會(huì)根據(jù)請(qǐng)求的
Content-Type(反序列化)和Accept(序列化)頭,以及目標(biāo)方法的參數(shù)/返回類(lèi)型,來(lái)選擇一個(gè)合適的HttpMessageConverter實(shí)現(xiàn)。 - 可插拔性: 你可以自定義或替換這些轉(zhuǎn)換器,從而支持不同的數(shù)據(jù)格式(如 JSON、XML、Protobuf 等)。
Spring Boot 通過(guò)自動(dòng)配置,默認(rèn)為我們注冊(cè)了以下常用的轉(zhuǎn)換器:
| 轉(zhuǎn)換器類(lèi) | 支持格式 | 說(shuō)明 |
|---|---|---|
MappingJackson2HttpMessageConverter | JSON | 默認(rèn),使用 Jackson 庫(kù) |
GsonHttpMessageConverter | JSON | 使用 Google 的 Gson 庫(kù) |
Jaxb2RootElementHttpMessageConverter | XML | 處理 XML 格式 |
如何切換? 例如,如果你想用 Gson 替代 Jackson,只需在
pom.xml中排除 Jackson 并引入 Gson 依賴(lài),Spring Boot 會(huì)自動(dòng)切換默認(rèn)的 JSON 轉(zhuǎn)換器。
五、自定義序列化與反序列化器
當(dāng)默認(rèn)行為無(wú)法滿(mǎn)足復(fù)雜需求時(shí),可以編寫(xiě)自定義的 JsonSerializer 和 JsonDeserializer。 我們希望將 Money 對(duì)象序列化為 "100.00元" 的格式。
// 1. 自定義序列化器
public class MoneySerializer extends JsonSerializer<Money> {
@Override
public void serialize(Money value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
// 將 Money 對(duì)象轉(zhuǎn)換為字符串
String moneyStr = value.getAmount() + "元";
gen.writeString(moneyStr);
}
}
// 2. 自定義反序列化器
public class MoneyDeserializer extends JsonDeserializer<Money> {
@Override
public Money deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String text = p.getText(); // 例如 "100.00元"
// 解析字符串,創(chuàng)建 Money 對(duì)象
String amountStr = text.replace("元", "");
BigDecimal amount = new BigDecimal(amountStr);
return new Money(amount);
}
}
// 3. 在 Money 類(lèi)上使用注解綁定
public class Money {
private BigDecimal amount;
@JsonSerialize(using = MoneySerializer.class)
@JsonDeserialize(using = MoneyDeserializer.class)
public BigDecimal getAmount() {
return amount;
}
}在 application.properties 或通過(guò)配置類(lèi) Jackson2ObjectMapperBuilderCustomizer 可以進(jìn)行全局配置,避免在每個(gè)類(lèi)上重復(fù)注解。
配置文件方式:
# 縮進(jìn)輸出,便于閱讀 spring.jackson.serialization.indent-output=true # 忽略未知屬性,反序列化時(shí)如果JSON有類(lèi)中沒(méi)有的字段不報(bào)錯(cuò) spring.jackson.deserialization.fail-on-unknown-properties=false # 日期全局格式 spring.jackson.date-format=yyyy-MM-dd HH:mm:ss # 忽略值為 null 的字段 spring.jackson.default-property-inclusion=non_null
配置類(lèi)方式(Configuration):
@Configuration
public class JacksonConfig {
@Bean
public Jackson2ObjectMapperBuilderCustomizer jacksonCustomizer() {
return builder -> {
builder.serializationInclusion(JsonInclude.Include.NON_NULL);
builder.featuresToEnable(SerializationFeature.INDENT_OUTPUT);
builder.featuresToDisable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
builder.simpleDateFormat("yyyy-MM-dd HH:mm:ss");
};
}
}到此這篇關(guān)于一文精通Spring Boot序列化與反序列化(告別數(shù)據(jù)混亂)的文章就介紹到這了,更多相關(guān)Spring Boot序列化與反序列化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot中fastjson自定義序列化和反序列化的實(shí)戰(zhàn)分享
- springboot序列化和反序列化器配置方法
- 詳解SpringBoot中時(shí)間類(lèi)型的序列化與反序列化
- SpringBoot之Json的序列化和反序列化問(wèn)題
- springboot jackson自定義序列化和反序列化實(shí)例
- SpringBoot中時(shí)間類(lèi)型 序列化、反序列化、格式處理示例代碼
- SpringBoot Redis配置Fastjson進(jìn)行序列化和反序列化實(shí)現(xiàn)
- SpringBoot2.0整合jackson配置日期格式化和反序列化的實(shí)現(xiàn)
相關(guān)文章
Java中二叉樹(shù)的建立和各種遍歷實(shí)例代碼
這篇文章主要介紹了Java中二叉樹(shù)的建立和各種遍歷實(shí)例代碼,涉及樹(shù)節(jié)點(diǎn)的定義,后序遍歷,層序遍歷,深度優(yōu)先和廣度優(yōu)先等相關(guān)內(nèi)容,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-01-01
Java源碼解析CopyOnWriteArrayList的講解
今天小編就為大家分享一篇關(guān)于Java源碼解析CopyOnWriteArrayList的講解,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-01-01
springboot如何開(kāi)啟一個(gè)監(jiān)聽(tīng)線程執(zhí)行任務(wù)
這篇文章主要介紹了springboot如何開(kāi)啟一個(gè)監(jiān)聽(tīng)線程執(zhí)行任務(wù)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-02-02
用Maven插件生成Mybatis代碼的實(shí)現(xiàn)方法
本文主要介紹 Maven插件生成Mybatis代碼,現(xiàn)在做開(kāi)發(fā)的朋友有好多用Maven 來(lái)管理代碼,這里給大家舉個(gè)例子,有需要的同學(xué)可以看下2016-07-07
Servlet注解之@WebInitParam多個(gè)InitParam的使用
這篇文章主要介紹了Servlet注解之@WebInitParam多個(gè)InitParam的使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04

