Spring 中类似 aBbb 单字母单词序列化与反序列问题

文章目录

  • 前言
  • 代码准备
    • 问题排查
    • lombok
    • 自定义生成 `get、set`
  • 结合源码解析
    • 使用 `lombok`
    • 使用 `lombok` 自定义生成 `user 对象 get、set` 方法
  • 如何解决
    • 使用注解 `@JsonProperty("aTest")`
    • 自定义实现符合 `Spring` 规范的 `get set` 方法
  • 个人简介

前言

  • 最近在使用 spring boot mvc 实现 HTTP 接口时出现了大小写异常转换的神秘现象,比如下面的案例:
@Data
public class User {private int id;private String name;private String aTest;
}请求参数:
{"name": "小明","aTest": "测试" 
}响应参数:
{"id": 1,"name": "小明","atest": null  // aTest 未成功接收
}1、前端字段序列化异常2、aTest 字段被序列化为了 atest

代码准备

  • Spring-boot-parent 2.6.4
@Data
public class User {private int id;private String name;private String aTest;public User(int id, String name, String aTest) {this.id = id;this.name = name;this.aTest = aTest;}
}@Repository
public class UserRepository {public User createUser(User user) {System.out.println(user);return user;}
}@RestController
public class UserController {@Autowiredprivate UserRepository userRepository;@PostMapping("/users")public User createUser(@RequestBody User user) {return userRepository.createUser(user);}
}

问题排查

  • 经过一系列排查发现是对象序列化和反序列化导致的问题,一个是使用 lombok 生成 get、set 方法,一个使用自定义生成 get、set 方式实现,下面我们来看一下两种情况的差异:

lombok

  • User 对象使用 lombok 生成 get、set 方法
@Data
public class User {private int id;private String name;private String aTest;
}
  • 测试结果:
POST http://localhost:8080/users
Content-Type: application/json{"name": "小明","aTest": "测试"
}// 打印日志 aTest 字段未被成功接收
User(id=0, name=小明, aTest=null)// 响应日志 aTest 字段被转换为 atest
{"id": 1,"name": "小明","atest": null
}
  • 可以发现接口请求传递过来的 aTest 没有被正常反序列,响应时 aTest 字段被序列为了 atest

自定义生成 get、set

  • 自定义生成 user 对象 get、set 方法。
public class User {private int id;private String name;private String aTest;public User(int id, String name, String aTest) {this.id = id;this.name = name;this.aTest = aTest;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getaTest() {return aTest;}public void setaTest(String aTest) {this.aTest = aTest;}@Overridepublic String toString() {return "User{" +"id=" + id +", name='" + name + '\'' +", aTest='" + aTest + '\'' +'}';}
}
  • 测试结果:
POST http://localhost:8080/users
Content-Type: application/json{"name": "小明","aTest": "测试"
}// 打印日志 aTest 字段被成功接收
User{id=0, name='小明', aTest='测试'}// 响应日志 aTest 字段被转为预期的 aTest
{"id": 1,"name": "小明","aTest": "测试"
}
  • 可以发现请求时 aTest 被正常解析,响应时 aTest 被序列化为预期的 aTest

结合源码解析

  • 这里我们可以对比 lombok 和我们自定义生成 get、set 方法的差异:
// lombokpublic String getATest() {return this.aTest;}public void setATest(final String aTest) {this.aTest = aTest;}// 自定义public String getaTest() {return aTest;}public void setaTest(String aTest) {this.aTest = aTest;}
  • 我们知道 Spring 默认使用 jackson 进行序列化和反序列,在构建 BeanDeserializer 时会通过方法和字段获取对应的 属性properties,由于 Spring 和 lombokJavaBeans 规范的定义理解并不一致导致识别字段结果不同,具体可以参考:https://github.com/projectlombok/lombok/issues/757

使用 lombok

  • 我们先看看,lombok 生成的 BeanDeserializer
  • com.fasterxml.jackson.databind.deser.BeanDeserializerFactory#buildBeanDeserializer 中我们可以看到调用了 buildBeanDeserializer 生成 BeanDeserializer

  • 一直断点,我们可以来到 com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector#collectAll 获取 properties map,这里是重点:

  • 执行 _addFields、_addMethods 我们推断出了来 4 个字段:

  • 这里为什么是4个呢?因为 lombokSpring jacksonJavaBeans 规范的定义理解不一致,导致从方法中推断出了 atest 字段。
  • 然后执行 _removeUnwantedProperties 字段去除了 aTest 字段,因为它是不可见的。

使用 lombok 自定义生成 user 对象 get、set 方法

  • 我们采取自定义的写法生成 user 对象 get、set 方法,对象属性可以正常被识别:

如何解决

  • 知道了问题产生原因,解决就很简单了,只要让我们字段属性被正常推断即可。

使用注解 @JsonProperty("aTest")

自定义实现符合 Spring 规范的 get set 方法

    public String getaTest() {return aTest;}public void setaTest(String aTest) {this.aTest = aTest;}

个人简介

👋 你好,我是 Lorin 洛林,一位 Java 后端技术开发者!座右铭:Technology has the power to make the world a better place.

🚀 我对技术的热情是我不断学习和分享的动力。我的博客是一个关于Java生态系统、后端开发和最新技术趋势的地方。

🧠 作为一个 Java 后端技术爱好者,我不仅热衷于探索语言的新特性和技术的深度,还热衷于分享我的见解和最佳实践。我相信知识的分享和社区合作可以帮助我们共同成长。

💡 在我的博客上,你将找到关于Java核心概念、JVM 底层技术、常用框架如Spring和Mybatis 、MySQL等数据库管理、RabbitMQ、Rocketmq等消息中间件、性能优化等内容的深入文章。我也将分享一些编程技巧和解决问题的方法,以帮助你更好地掌握Java编程。

🌐 我鼓励互动和建立社区,因此请留下你的问题、建议或主题请求,让我知道你感兴趣的内容。此外,我将分享最新的互联网和技术资讯,以确保你与技术世界的最新发展保持联系。我期待与你一起在技术之路上前进,一起探讨技术世界的无限可能性。

📖 保持关注我的博客,让我们共同追求技术卓越。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/797931.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux下Qt生成程序崩溃文件

文章目录 1.背景2.Qt编译生成程序2.1.profile模式的本质 3.执行程序,得到core文件4.代码定位4.1.直接使用gdb4.2.使用QtCreator 5.总结6.题外话6.1.profile模式和debug模式的区别 1.背景 在使用Qt时,假如在windows,当软件崩溃时,…

【整数二分】难题选讲

对应洛谷题单里面【整数二分】几道同学们不太好理解的题目,写个解析 A-1数对题目链接 实际上本题方法很多,我先说个好做的办法 我们可以用map标记所有数的个数,枚举数字A[i],在map里面查找C-A[i]数字的个数,统计答案即可&#xff…

C++11右值引用

传统的C语法中就有引用的语法,而C11中新增了的右值引用语法特性,所以从现在开始我们之前学习的引用就叫做左值引用。无论左值引用还是右值引用,都是给对象取别名。 左值与左值引用 左值是一个表示数据的表达式(如变量名或解引用的指针)&…

Qt-线程1-Run

0. 写在前面 一个应用程序一般只有一个线程,一个线程内的操作是顺序执行的,如果有某个比较耗时间的计算或操作,比如图像处理大数据图像、网络通信中的文件传输;在一个线程内操作时,用户界面就能冻结而不能及时响应。这…

34-2 SSRF(服务器端请求伪造)漏洞 - SSRF相关函数

简介 file_get_contents(): 功能: 该函数用于将整个文件读取到一个字符串中,可以用于获取本地或远程文件的内容。风险: 如果未经验证的用户输入作为参数传递给该函数,并且允许读取远程文件,则可能导致SSRF漏洞,攻击者可以利用该漏洞读取服务器内部的文件或者执行未经授权…

C++面试经典问题

常见问题:智能指针、多态、虚函数、STL原理、链表、排序、二叉树、设计模式、线程进程、内存 对象所有权 在接触智能指针之前首先要理解对象的所有权是什么,在这之前我们总是用new和delete来进行内存的申请与释放,在这种堆内存分配的方式中…

【退役之重学Java】关于lambda表达式和函数式接口

使用 lambda 表达式为作为实参,传递给形参,形参为函数式接口,此lambda表达式没有指定类型,如何能匹配函数式接口的形参类型呢? 函数式接口作为参数,实参可以直接用lambda表达式吗,是什么原理 函…

【嵌入式DIY实例】-MODBUS串行通信

MODBUS串行通信 文章目录 MODBUS串行通信1、什么是RS-4852、MAX485 TTL转RS485转换器3、硬件准备4、代码实现4.1 主机和从机之间简单通信4.2 主/从机之间LED控制在本文中,我们将介绍如何使用 MAX485 MODBUS 在Arduino之间进行串行通信。 我们将使用 Arduino nano 板和 MODBUS …

springboot国际化多语言

1,新建国际化多语言文件 在resources目录下新建 messages.properties 其他语言的文件 编辑messages.properties文件,下方从text切换到Resource Bundle ,即可对照着编辑多语言文件 (如果没有找到Resource Bundle,先在settings->plugins中安装Resource Bundle Editor) 2,配…

ComfyUI如何操作?借鉴文章有官方ComfyUI 手册、COmfyui初学者教程等

ComfyUI 是一个基于节点的用户界面,用于构建生成图像的工作流程。它通过将不同的节点连接在一起来实现这一功能。 ComfyUI 的基本操作步骤(“精”简版) 1. 安装 ComfyUI 访问 ComfyUI 的 GitHub 页面,根据您的操作系统&#xff08…

Lumos学习王佩丰Excel第二讲:单元格格式设置

今天学会GIF录制了,分享知识会更简便一些,话不多说,开始吧~ 一、美化表格 1、设置单元格格式的路径 从菜单栏进入: 选中区域(单元格)- 右键“设置单元格格式”: 2、合并单元格 合并一行 批量…

5.3 用栈翻转数组,动态规划求斐波那契数列

5.3 用栈翻转数组,动态规划求斐波那契数列 1. 用栈翻转数组 assume cs:code,ds:data,ss:stack data segmentarr dw 1111h,2222h,3333h,4444h,5555h,6666h,7777h,8888hres db 800 dup(0) data endsstack segmentdb 100 dup(0) stack endscode segmentstart:mov ax,…

YOLOv8模型剪枝实战:Network Slimming网络瘦身方法

课程链接:YOLOv8模型剪枝实战:Network Slimming网络瘦身方法_在线视频教程-CSDN程序员研修院 YOLOv8是一个当前非常流行的目标检测器,本课程使用Network Slimming(网络瘦身)剪枝方法对YOLOv8进行模型剪枝,…

力扣347. 前 K 个高频元素

思路:记录元素出现的次数用map; 要维护前k个元素,不至于把所有元素都排序再取前k个,而是新建一个堆,用小根堆存放前k个最大的数。 为什么是小根堆?因为堆每次出数据时只出堆顶,每次把当前最小的…

手动实现Tomcat底层机制+自己设计Servlet

文章目录 1.Tomcat整体架构分析自己理解 2.第一阶段1.实现功能2.代码1.TomcatV1.java 3.调试阶段1.阻塞在readLine导致无法返回结果 4.结果演示 3.第二阶段1.实现功能2.代码1.RequestHander.java2.TomcatV2.java 3.调试阶段1.发现每次按回车会接受到两次请求 4.结果演示 4.第三…

《牛客》-C小红的字符串构造

思路:我以符合条件的最极限情况来安排回文,即两个两个字符为一组回文,保证其不参与其他回文字符串的构造,以这样子的形式输出k个,剩下的都只输出不连续的当个字符(‘a’i%26) 看不到&#xff1…

使用Python实现K近邻算法

K近邻(K-Nearest Neighbors,简称KNN)是一种简单而有效的分类和回归算法,它通过比较新样本与训练样本的距离来进行预测。在本文中,我们将使用Python来实现一个基本的K近邻算法,并介绍其原理和实现过程。 什…

[dvwa] Command Injection

命令注入 0x01 low 没有过滤,直接利用 127.0.0.1 && ip a 函数 php_uname(mode) 动态地检查服务器的操作系统 ‘s’:操作系统名称 ‘n’:网络主机名 ‘r’:操作系统发行版本号 ‘v’:操作系统版本 ‘m’&…

书籍《笔记的方法》读后感

读完《笔记的方法》有几周的时间,书里有些记录的内容,觉得非常有价值的,自己的观点,当下读书,其实并没有那么高大尚,就是存粹陶冶下情操,读书还是有一定作用的,毕竟看书只能慢慢来&a…

淘宝API接口详解:如何高效利用API进行电商开发

淘宝API接口详解:如何高效利用API进行电商开发 请求示例,API接口接入Anzexi58 在电商行业蓬勃发展的今天,淘宝作为国内最大的电商平台之一,为商家和开发者提供了丰富的API接口。这些接口使得电商开发变得更加高效和便捷。本文将详…