• SpringMvc 与 Lombok 碰撞导致 JSON 反序列化失败


    SpringMvc 与 Lombok 中 JSON 反序列化失败

    错误复现_1

    @Data
    public class User{
        private Long id;
        private boolean isOk;
    }
    
    @RequestMapping
    public R<User> getUser(@RequestBody User user){
        return R.success(user);
    }
    
    // 前端传参 - {"id": 123456789,"isOk": true}
    // 后端返回 - {"id": 123456789,"isOk": false}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    排查分析_1

    1. 查看 User 类 class 文件。

      public class User{
          pirvate Long id;
          private boolean isOk;
          public User() {}
          public Long getId() { return this.id; }
          public boolean isOk() { return this.isOk; }
          public void setId(final Long id) { this.id = id; }
          public void setOk(final boolean isOk) { this.isOk = isOk; }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    2. 提出猜测:Lombok 对于 boolean 类型生成的 Getter/Setter 方法与其他类型不同,该原因导致的数据不一致

    错误复现_2

    @Data
    public class User{
        private Long id;
        private String Name;
        private Long cId;
    }
    
    // 前端传参 - {"id": 1,"Name": "LXL","cId": 2}
    
    @RequestMapping
    public void getUser(@RequestBody User user){
        System.out.println(user.getId()); // 1
        System.out.println(user.getName()); // null
        System.out.println(user.getCId()); // null
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    排查分析_2

    1. 查看 User 类 class 文件。

      public class User {
          private Long id;
          private String Name;
          private Long cId;
          public User() {}
          public Long getId() { return this.id; }
          public String getName() { return this.Name; }
          public Long getCId() { return this.cId; }
          public void setId(final Long id) { this.id = id; }
          public void setName(final String Name) { this.Name = Name; }
          public void setCId(final Long cId) { this.cId = cId; }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
    2. 提出猜测:反序列化时,由于某种原因导致无法正常赋值。

    揭开谜团

    结合上述两个猜测,去了解了 SpringMvc 的反序列化机制、Lombok 的代码生成机制。

    先说一下 SpringMvc 的 @RequestBody 注解:

    1. SpringMvc 中通过 @RequestBody 注解实现将 json 数据转成 java 对象。
    2. 处理 @RequestBody 注解时,内部使用 jackson 框架完成该反序列化过程。

    接下来是 Jackson 框架的反序列化操作步骤:

    1. 仅有全参数构造器时,通过调用该构造器,映射 JSON 属性与 Construction 参数列表完成对象的实例化。
    2. 有无参数构造器时,优先使用无参数构造器 + Getter/Setter 方法完成序列化与反序列化。调用反序列化的目标类的无参构造函数,构造一个 java 对象。然后调用该类的成员变量的 set 方法,为该对象的每一个成员变量赋值。
    3. Jackson 遵守了 JavaBean 的规范:
      • 首字母为大写的属性名(如:Name, URL, SuV…),这种属性名直接忽略不注入。
      • 第一个字母是小写,第二个字母大写的情况(如:pId, sUV…),在生成 Getter/Setter 的时,直接在前面加上 set/get,比如 pId 生成 setpId()/getpId(),所以 pId 属性在注入的时候会寻找 setpId() 方法,而不是 setPId() 。

    最后是 Lombok 的 Getter/Setter 生成机制:

    1. 当成员变量为 boolean 类型时,属性名为 isXxx 生成的 Getter/Setter 方法的方法名为 isXxx/setXxx。
    2. 其他基本类型与所有引用数据类型,属性名为 aaBb 生成的 Getter/Setter 方法的方法名为 getAaBb/setAaBb,例如 isXxx 生成 getIsXxx/SetIsXxx。

    这几个知识点就可以解决错误 1、错误 2 啦!!!

    1. 错误 1 造成原因:为属性名 isOk 查找 Setter 方法时,找不到 setIsOk,所以无法注入,使用默认值 false。
    2. 错误 2 造成原因:属性名 Name 不符合 JavaBean 规范被直接忽略注入;属性名 cId 查找的 Setter 方法名应该为 setcId,而 Lombok 生成的是 setCId,也是注入不了的。

    方案与总结

    1. 规范且合理制定成员变量名称。
    2. 尽量使用包装类,既可以符合 Java 语言面向对象的特性,同时可以避免 Lombok 生成的 Setter/Getter 与 Jackson 框架规则间的冲突。
    3. 上述两种方案不适合的话,可以考虑手动生成 Setter/Getter 方法。
    4. 通过 @JsonProperty 显式指定 JSON 属性与 Java 属性的对应关系。
  • 相关阅读:
    1019 链表的下一个更大节点(单调栈)
    使用FFmpeg+ubuntu系统转化flac无损音频为mp3
    量子多体理论怎么样理解,多体系统的量子理论
    微服务技术栈SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式(四):消息队列MQ
    使用React Context和Hooks在React Native中共享蓝牙数据
    Python读取Excel工作表数据写入CSV、XML、文本
    MySql——性能分析
    【OpenPLC】在linux板卡上添加CANOpen功能
    京东云开发者|探寻软件架构的本质,到底什么是架构?
    便携式脑卒中检测仪是不是离现实不远了?
  • 原文地址:https://blog.csdn.net/LXL2790/article/details/132628890