Lombok把我坑惨了!HTTP请求的JSON参数无法正确映射到Java类

背景

在使用Spring Boot框架的Spring MVC时,通过HTTP请求传递的JSON参数无法正确映射到Java类的对应字段上。进行了一系列的排查,发现问题出在使用了Lombok的@Data注解上。示例代码如下:

@RestController
@RequestMapping("/user")
public class UserController {

    /**
     * 保存用户
     */
    @PostMapping("/save")
    public void save(@RequestBody User user) {
        System.out.println(user.getName());
        System.out.println(user.getCName());
    }

}

// JSON参数
{
    "name":"英文名",
    "cName":"中文名"
}

排查思路

  • 第一,检查字段名称跟类型跟JSON参数是否一致,结果为一致
  • 第二,查看调用方(python)调用地址是否正确,确认调用地址一致
  • 第三,自己使用postman调用,无法正确映射,确认问题为Java侧的问题
  • 第四,找到Java类研究可能的原因,看到了@Data注解,尝试分析,最终确定是这个原因

原理及问题剖析

解决这个问题的关键在于知道SpringBoot是如何将JSON请求参数映射到Java类的。在上面的示例中User是请求对象类,它具有与JSON参数对应的属性。并@RequestBody注解告诉SpringBoot将请求体中的JSON参数映射到user对象上。
当你发送一个POST请求到/user/save路径时,SpringBoot将自动解析请求体中的JSON参数,并将其映射到user对象,执行具体的业务逻辑。
在SpringBoot中,使用@RequestBody注解可以实现请求体到对象映射的原理是通过SpringMVC的消息转换器(MessageConverter)机制实现的。SpringMVC在处理请求时会根据请求头中的Content-Type判断请求体的类型,然后选择合适的消息转换器来将请求体转换为Java对象。当使用 @RequestBody 注解时,Spring MVC 会使用消息转换器将请求体的内容转换为指定的对象类型。默认情况下,Spring Boot 集成了 Jackson 库作为 JSON 消息转换器,它可以将 JSON 数据转换为 Java 对象。具体的工作流程如下:

  1. 客户端发送一个带有请求体的请求到 Spring Boot 应用程序。
  2. Spring MVC 接收到请求后,根据请求头中的 Content-Type 判断请求体的类型。
  3. Spring MVC 使用合适的消息转换器(例如,使用 Jackson 库的转换器处理 JSON 请求体)将请求体的内容转换为指定的对象类型。
  4. 将转换后的对象作为参数传递给带有 @RequestBody 注解的方法。
  5. 方法可以在其中使用转换后的对象进行业务处理。

总结:对象的属性名与 JSON 数据的键名匹配是默认的映射规则,与对象的 set 方法紧密相关。如果属性名与键名一致并且存在对应的 set 方法,转换器会使用 set 方法将值设置到对象的属性中。如果属性是私有的或者没有对应的 set 方法,可以通过注解来指定其他的映射方式。

解决方案

以下是SpringBoot生成的set方法与Lombok生成的set方法,SpringBoot想用setcName映射属性,可是Lombok生成的是setCName,导致的问题。

// SpringBoot生成
@Data
public class User {
    private String name;
    @JsonAlias("cName")
    private String cName;

   private String URL;

   private String isOK;

    public void setcName(String cName) {
        this.cName = cName;
    }
}

// Lombok生成
public class User {
    private String name;
    @JsonAlias({"cName"})
    private String cName;
    private String URL;
    private String isOK;

    public void setCName(final String cName) {
        this.cName = cName;
    }
}
  1. 最优方案:保留@Data注解,与python同学协商,修改字段名。原有字段名是cName,改为cnName。
  2. 第二方案:保留@Data注解,增加@JsonAlias(“cName”)注解
  3. 第三方案:删除@Data注解,自己生成get,set方法

你可能感兴趣的:(java,http,json)