Spring Boot:DTO 字段 cPlanId 无法反序列化的奇葩问题

发布于:2025-07-16 ⋅ 阅读:(19) ⋅ 点赞:(0)

本文记录一次在 Spring Boot 项目中,DTO 字段明明有值,反序列化后却是 null 的问题。最终发现并不是常见的 JSON 工具库 Bug,而是隐藏在 setter 命名大小写规则中的坑。


💻 背景介绍

技术栈如下:

  • Spring Boot:2.2.3.RELEASE

  • Java:1.8

  • JSON 序列化:默认使用 Jackson,部分地方使用 Fastjson

  • 使用了 Lombok@Data 注解简化 DTO 编写

业务中,前端提交如下 JSON 数据:

{
  "cPlanId": "CP-202507150001"
}

DTO 如下:

@Data
public class CPlanIdRequestDTO implements Serializable {
    @NotBlank(message = "不能为空")
    private String cPlanId;
}

🧪 问题表现

后端接收到的参数绑定结果中,cPlanId == null。但是 JSON 明明是正确的,而且 DTO 上也有 @Data 自动生成 getter/setter。

尝试如下方式:

public void setCPlanId(String cPlanId) {
    this.cPlanId = cPlanId;
}

依然无效

尝试这样写:

public void setcPlanId(String cPlanId) {
    this.cPlanId = cPlanId;
}

反而成功了!


🔍 排查过程

✅ Step 1:确认 JSON 数据没问题

接口调试工具(如 Postman、Apifox)发送参数正常,字段名也符合预期:"cPlanId"

✅ Step 2:确认使用的 JSON 库

项目中同时存在:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.83</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>

但是在未手动配置 FastJsonHttpMessageConverter 的前提下,Spring Boot 默认用的是 Jackson

✅ Step 3:查看 ObjectMapper 配置

ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));

→ 没有特殊命名策略。


🎯 真正的问题原因:setter 方法名大小写

根据 JavaBean 规范:

  • private String cPlanId; 应该对应 setCPlanId(String),不是 setcPlanId(String)

  • Jackson 遵循此规范

  • Fastjson 则更“灵活”,会根据字段名匹配 setcPlanId(),哪怕违反 JavaBean 规范

但我们项目中,有可能某个地方通过工具类(如 JsonUtils)手动调用了 Fastjson 来解析参数,绕过了 Spring MVC 默认的 Jackson。


✅ 解决方案

✅ 推荐方式 1:统一命名,手动写标准方法

public class CPlanIdRequestDTO implements Serializable {

    private String cPlanId;

    public void setcPlanId(String cPlanId) {
        this.cPlanId = cPlanId;
    }
}

✅ 推荐方式 2:加注解双兼容

@JsonProperty("cPlanId")
@JSONField(name = "cPlanId")
private String cPlanId;

🧠 思考与总结

案例 结果
@Data 自动生成 + Jackson ✔️ 成功
setCPlanId()(规范写法) + Fastjson ❌ 有时失败
setcPlanId()(非规范) + Fastjson ✔️ 成功
未加 setter + Fastjson ❌ 很可能失败
DTO 中添加注解 ✔️ 通用成功

本次问题提醒我们:

  • ✅ 保持 setter 方法命名符合 JavaBean 规范

  • ✅ 项目中尽量统一使用一种 JSON 库(建议 Jackson)

  • ✅ 若存在多种工具(如 Fastjson 工具类、Jackson 配置),注意行为差异

  • ✅ 接口 DTO 中的重要字段,手动加 getter/setter 更保险


一个字段无法反序列化的 bug,背后牵涉 JSON 工具的默认策略、setter 方法的命名大小写、工具类使用方式、项目配置混杂等多个维度。

 


网站公告

今日签到

点亮在社区的每一天
去签到