大家好,我是加洛斯 👨💻。作为一名程序员,我深深信奉费曼学习法------教,是最好的学 📚。这里是我的知识笔记与分享,旨在把复杂的东西讲明白。如果发现有误,万分欢迎你帮我指出来 🔍!废话不多说,正文开始 👇
在开发中我们经常能听到的两个词,序列化与反序列化。很多萌新小白听到这个概念肯定是满脑门问号,接下来我将用通俗易懂的语言和妙趣横生的比喻,深入浅出的将序列化与反序列的基础以及原理给大家讲述出来。
一、基础概念
简单来说,序列化 和 反序列化 就是 将数据在不同格式之间进行转换 的过程,目的是为了在不同的系统或不同的上下文之间传输和存储数据。
-
序列化 (
Serialization): 将 对象 转换为 可传输或可存储的格式(通常是 JSON、XML 等)。- 在 Spring Boot 中的场景: 当你的 Controller 方法返回一个 Java 对象时,Spring Boot 会自动将它序列化成 JSON 字符串,然后通过 HTTP 响应发送给前端(如浏览器、手机 App)。
-
反序列化 (
Deserialization): 将 可传输或可存储的格式 (如 JSON、XML)转换回 对象。- 在 Spring Boot 中的场景: 当前端通过 HTTP 请求(如 POST、PUT)发送一个 JSON 数据到你的服务器时,Spring Boot 会自动将这个 JSON 字符串反序列化成你 Controller 方法参数中定义的 Java 对象。
那么简单来讲就是,序列化是将对象转化为JSON等格式,反序列化就是将JSON等格式转化为对象。
二、具体应用
Spring Boot 通过Spring MVC 模块和默认集成的Jackson库,让这个过程变得极为简单,但是其背后的原理却是相当复杂,我们先从简单的开始来,看看怎么用以及干了什么。
2.1 反序列
假设前端要创建一个用户,它发送了一个 POST 请求,请求体是 JSON:
json
{
"name": "张三",
"age": 25,
"email": "zhangsan@example.com"
}
对应的 User 类(Java Bean):
java
// 必须有无参构造函数
// 必须有为所有属性生成的 Getter 和 Setter 方法
// lombok注解 自动生成getter与setter方法与无参构造函数
@Data
public class User {
private String name;
private Integer age;
private String email;
}
在你的 Spring Boot Controller 中,你会这样写:
java
@RestController
public class UserController {
@PostMapping("/users")
public ResponseEntity<String> createUser(@RequestBody User user) {
// 在这个方法内部,User 对象已经是一个可以直接使用的 Java 对象了
System.out.println("用户名:" + user.getName());
System.out.println("年龄:" + user.getage());
// ... 保存用户到数据库等操作
return ResponseEntity.ok("用户创建成功");
}
}
这里发生了什么?
- 前端发送 JSON 数据。
- Spring Boot 看到
@RequestBody注解和User这个参数类型。 - Spring Boot 使用 Jackson 库 反序列化 JSON 字符串,根据 JSON 的 key 匹配
User类的属性(如name匹配name),并创建一个User对象。 - 这个创建好的
User对象被传入你的createUser方法中。
这样我们就从前端发送的JSON中解析了我们需要的数据,是不是很简单,现在没有配置任何文件,紧靠SpringBoot自带的Jackson库就完成了反序列化。
2.2 序列化
同样一个 Controller,我们写一个查询用户的方法:
java
@RestController
public class UserController {
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
// 假设从数据库或其他地方查询到了一个用户
User user = new User();
user.setName("李四");
user.setAge(30);
user.setEmail("lisi@example.com");
// 直接返回这个对象
return user;
}
}
这里发生了什么?
- 你的方法返回了一个
User对象。 - Spring Boot 看到返回值后,使用 Jackson 库 序列化 这个
User对象。 - 最终,前端收到的 HTTP 响应体就是这样的 JSON 字符串:
json
{
"name": "李四",
"age": 30,
"email": "lisi@example.com"
}
三、Jackson
SpringBoot通过自动配置和默认的 Jackson 集成,极大地简化了 Web 开发中数据格式转换的复杂性。开发者几乎不需要编写转换代码,只需关注业务逻辑(定义对象、编写 Controller),就能轻松实现 RESTful API 的数据交互。我们来学习几个常用的 Jackson 注解。
@JsonIgnore:在序列化时忽略某个属性。
java
public class User {
private String name;
@JsonIgnore // 返回给前端的 JSON 中不会包含 password 字段,保障安全
private String password;
}
@JsonProperty:为属性指定一个别名。
java
public class User {
@JsonProperty("full_name") // 序列化后 JSON 中的 key 是 "full_name"
private String name;
}
@JsonFormat:格式化日期字段。
java
public class User {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
}
@JsonInclude:控制序列化时包含哪些属性。
java
@JsonInclude(JsonInclude.Include.NON_NULL) // 只包含非 null 字段
public class User {
private String name;
private Integer age; // 如果 age 为 null,则序列化结果中无此字段
}
@JsonView:定义视图,实现不同接口返回不同字段。
java
public class Views {
public static class Public {} // 公共视图
public static class Internal extends Public {} // 内部视图,继承公共视图
}
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() { ... }
}
| 过程 | 方向 | 动作 | 比喻 | Spring Boot 中的关键注解 |
|---|---|---|---|---|
| 序列化 | 对象 -> JSON | 将Java对象转换为传输/存储格式 | 打包 | (默认行为,无需注解) |
| 反序列化 | JSON -> 对象 | 将传输/存储格式转换为Java对象 | 拆包组装 | @RequestBody |
四、原理
Spring Boot 的序列化与反序列化其背后是一套名为 HttpMessageConverter 的可插拔策略接口。
- 工作原理: 当 Spring MVC 处理一个 HTTP 请求时,它会根据请求的
Content-Type(反序列化)和Accept(序列化)头,以及目标方法的参数/返回类型,来选择一个合适的HttpMessageConverter实现。 - 可插拔性: 你可以自定义或替换这些转换器,从而支持不同的数据格式(如 JSON、XML、Protobuf 等)。
Spring Boot 通过自动配置,默认为我们注册了以下常用的转换器:
| 转换器类 | 支持格式 | 说明 |
|---|---|---|
MappingJackson2HttpMessageConverter |
JSON | 默认,使用 Jackson 库 |
GsonHttpMessageConverter |
JSON | 使用 Google 的 Gson 库 |
Jaxb2RootElementHttpMessageConverter |
XML | 处理 XML 格式 |
如何切换? 例如,如果你想用 Gson 替代 Jackson,只需在
pom.xml中排除 Jackson 并引入 Gson 依赖,Spring Boot 会自动切换默认的 JSON 转换器。
五、自定义序列化与反序列化器
当默认行为无法满足复杂需求时,可以编写自定义的 JsonSerializer 和 JsonDeserializer。 我们希望将 Money 对象序列化为 "100.00元" 的格式。
java
// 1. 自定义序列化器
public class MoneySerializer extends JsonSerializer<Money> {
@Override
public void serialize(Money value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
// 将 Money 对象转换为字符串
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元"
// 解析字符串,创建 Money 对象
String amountStr = text.replace("元", "");
BigDecimal amount = new BigDecimal(amountStr);
return new Money(amount);
}
}
// 3. 在 Money 类上使用注解绑定
public class Money {
private BigDecimal amount;
@JsonSerialize(using = MoneySerializer.class)
@JsonDeserialize(using = MoneyDeserializer.class)
public BigDecimal getAmount() {
return amount;
}
}
在 application.properties 或通过配置类 Jackson2ObjectMapperBuilderCustomizer 可以进行全局配置,避免在每个类上重复注解。
配置文件方式:
properties
# 缩进输出,便于阅读
spring.jackson.serialization.indent-output=true
# 忽略未知属性,反序列化时如果JSON有类中没有的字段不报错
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
配置类方式(Configuration):
java
@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");
};
}
}