对于我们新手来说,似乎按照对应的规则配置, 就能接受返回参数, controller层上得到或返回一个对象; 其实内部有个序列化/反序列化操作,上文实体类的注解就需要我们理解一下这些逻辑了
为什么必须序列化/反序列化?
HTTP 协议传输的数据本质上是文本(字符串),它不认识 Java 对象或 JavaScript 对象。所以对象在传输前必须转换成字符串,接收方再把字符串还原成对象。
后端Java对象 ──[序列化]──→JSON字符串 ──[HTTP传输]──→JSON字符串 ──[反序列化]──→ 前端JS对象 前端JS对象 ──[序列化]──→JSON字符串 ──[HTTP传输]──→JSON字符串 ──[反序列化]──→ 后端Java对象后端 → 前端
// 后端:Java 对象Orderorder=newOrder();order.setId(1L);order.setCreateTime(LocalDateTime.now());// 序列化:Java 对象 → JSON 字符串// {"id":1,"createTime":"2026-07-15 10:50:00"}returnorder;// Spring 自动通过 Jackson/FastJSON 序列化后返回// 前端:收到 JSON 字符串,自动反序列化为 JS 对象fetch('/order/1').then(res=>res.json()).then(data=>{console.log(data.id);// 1console.log(data.createTime);// "2026-07-15 10:50:00"});前端 → 后端
// 前端:JS 对象constorder={id:1,createTime:"2026-07-15 10:50:00"};// 序列化:JS 对象 → JSON 字符串,通过 HTTP 发送fetch('/order',{method:'POST',headers:{'Content-Type': 'application/json'},body:JSON.stringify(order)// 手动或框架自动序列化});// 后端:收到 JSON 字符串,反序列化为 Java 对象@PostMapping("/order")publicvoidcreateOrder(@RequestBodyOrderorder){// Spring 自动反序列化System.out.println(order.getId());// 1System.out.println(order.getCreateTime());// LocalDateTime 对象}| 传输内容 | 是否需要序列化 | 说明 |
|---|---|---|
| 对象/复杂数据结构 | ✅ 必须 | 转成 JSON 字符串传输 |
| 简单类型(String、Number) | 可以看作隐式序列化 | 本质也是转成字符串 |
| 文件(二进制) | ✅ 但方式不同 | 通常用multipart/form-data,不是 JSON 序列化 |
| URL 参数(?id=1&name=xxx) | ✅ 但方式不同 | 用 URL 编码,不是 JSON 序列化 |
这也是为什么前面讨论的 @JsonFormat、@JSONField、@DateTimeFormat 这些注解如此重要——它们就是在控制序列化和反序列化过程中日期字段的格式。
规则
JSON 序列化框架(FastJSON、Jackson)的默认规则是:
序列化:把对象中所有非静态、非瞬态的字段(或有 getter 的属性)转成 JSON 键值对
反序列化:把 JSON 中的键值对,按字段名匹配,自动赋值给对象的对应属性(通过 setter 或直接字段访问)
@DatapublicclassOrder{privateLongid;privateStringname;privateIntegerrelatedFundProjectCount;// 普通属性,什么都没加}// 序列化:Java → JSONOrderorder=newOrder();order.setId(1L);order.setName("测试订单");order.setRelatedFundProjectCount(5);Stringjson=JSON.toJSONString(order);// 输出:{"id":1,"name":"测试订单","relatedFundProjectCount":5} ✅ 正常// 反序列化:JSON → JavaStringjsonInput="{\"id\":2,\"name":"另一个订单","relatedFundProjectCount":10}";Orderorder2=JSON.parseObject(jsonInput,Order.class);System.out.println(order2.getRelatedFundProjectCount());// 10 ✅ 正常类的转换
实体类里, 以下常见类型都不需要加任何注解,框架内置了转换器:
| 类型 | 示例 |
|---|---|
| 基本类型及包装类 | Integer、Long、Boolean、Double |
| 字符串 | String |
| 集合 | List、Set、Map |
| 数组 | String[]、Integer[] |
| 嵌套对象 | User、OrderItem等自定义类 |
只有框架不知道该怎么转的类型,才需要加注解或自定义转换器:
| 类型 | 问题 | 解决方案 |
|---|---|---|
Date/LocalDateTime | 格式不统一 | @JSONField(format = ...) |
LocalDate | 格式不统一 | @JSONField(format = ...) |
| 枚举 | 默认输出序号或名称,可能不符合需求 | @JSONField或自定义序列化器 |
特殊对象(如BigDecimal需要指定精度) | 默认行为不满足 | 自定义序列化器 |
普通属性(Integer、String、Long 等)什么都不加,序列化和反序列化都能正常工作。
加注解(如 @JSONField)只是用来覆盖默认行为,比如改格式、改字段名、忽略字段等。不需要特殊处理时,就不用加。
Jackson(Spring Boot 默认)
为什么要写@Data
除了在代码里 使用set, get方法, 用这个注解的还有个重要作用就是 Spring Boot 默认Jackson默认的字段可见性策略是
- 序列化:找 public 的 getter 方法 或 public 的字段
- 反序列化:找 public 的 setter 方法 或 public 的字段
所以,不写 getter/setter 也能序列化/反序列化,只是 Jackson 默认配置下不行,需要额外配置。而 FastJSON 和 Lombok 方案天然支持。
JSON.toJSONString, JSON.parseObject
有时候 数据库存了一个json类型的字符串, 用的就是上的方法
// 序列化:Java → JSONOrderorder=newOrder();order.setId(1L);order.setName("测试订单");order.setRelatedFundProjectCount(5);Stringjson=JSON.toJSONString(order);// 输出:{"id":1,"name":"测试订单","relatedFundProjectCount":5} ✅ 正常// 反序列化:JSON → JavaStringjsonInput="{\"id\":2,\"name":"另一个订单","relatedFundProjectCount":10}";Orderorder2=JSON.parseObject(jsonInput,Order.class);System.out.println(order2.getRelatedFundProjectCount());// 10 ✅ 正常