当前位置: 首页 > news >正文

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编程。

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

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

相关文章:

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

文章目录 前言代码准备问题排查lombok自定义生成 get、set 结合源码解析使用 lombok使用 lombok 自定义生成 user 对象 get、set 方法 如何解决使用注解 JsonProperty("aTest")自定义实现符合 Spring 规范的 get set 方法 个人简介 前言 最近在使用 spring boot mvc…...

TiDB 慢查询日志分析

导读 TiDB 中的慢查询日志是一项 关键的性能监控工具,其主要作用在于协助数据库管理员追踪执行时间较长的 SQL 查询语句。 通过记录那些超过设定阈值的查询,慢查询日志为性能优化提供了关键的线索,有助于发现潜在的性能瓶颈,优化…...

网页文件批量下载工具有哪些 网页文件批量下载工具推荐 IDM免费激活 网络下载加速器

把任务丢给软件,把时间还给自己,批量下载功能让下载变得更高效。它可以有效减少重复性操作,只需要一次简单的设置,就能把大量文件下载到电脑。有关网页文件批量下载工具有哪些,网页文件批量下载工具推荐的问题&#xf…...

嵌入式算法开发系列之图像处理算法

嵌入式系统中的图像处理算法及其应用 文章目录 嵌入式系统中的图像处理算法及其应用前言一、图像处理算法的原理二、图像处理算法的应用三、C 语言实现总结 前言 在嵌入式系统中,图像处理算法是一项重要的技术,用于实现各种视觉应用,如机器视…...

HarmonyOS4-ArkUI组件动画

一、ArkUI组件属性动画和显示动画 显示动画: 案例:上下左右箭头控制小鱼的游动 具体代码如下: import router from ohos.routerEntry Component struct AnimationPage {// 小鱼坐标State fishX: number 200State fishY: number 180// 小鱼…...

模块化——如何导入模块?(内置模块与自定义模块)

在Node.js中,要导入另一个模块,我们可以使用require函数。这个函数接受一个文件路径参数,并返回导入的模块。 注意:require导入包场景:内置模块、自定义模块、npm包的导入... 下面介绍内置模块与自定义模块。npm包的…...

element-ui的按需引入报错解决:MoudleBuildFailed,完整引入和按需引入

官网: Element - The worlds most popular Vue UI framework 1.完整引入 (1)下载: npm i element-ui -S (2)引入: 在 main.js 中写入以下内容: import Vue from vue; impor…...

面向低碳经济运行目标的多微网能量互联优化调度matlab程序

微❤关注“电气仔推送”获得资料(专享优惠) 运用平台 matlabgurobi 程序简介 该程序为多微网协同优化调度模型,系统在保障综合效益的基础上,调度时优先协调微网与微网之间的能量流动,将与大电网的互联交互作为备用…...

FORM的引入与使用

FORM的引入与使用 【0】引入 ​ 表单(Form)是网页中用于收集用户输入数据的一种交互元素。通过表单,用户可以输入文本、选择选项、上传文件等操作。表单通常由一个或多个输入字段(Input Field)组成,每个字…...

酷开会员丨古偶悬疑剧《花间令》在酷开系统热播中!

酷开系统一直致力于为用户提供卓越的大屏娱乐体验。随着三月新剧《花间令》的上线,酷开系统再次展现了其在内容更新上的迅速响应能力和对高质量视听体验的不懈追求。 《花间令》的故事背景设定在一个充满神秘色彩的古代王朝,鞠婧祎饰演的女主角与刘学义饰…...

html骨架以及常见标签

推荐一个网站mdn。 html语法 双标签&#xff1a;<标签 属性"属性值">内容</标签> 属性&#xff1a;给标签提供附加信息。大多数属性以键值对的形式存在。如果属性名和属性值一样&#xff0c;可以致谢属性值。 单标签&#xff1a;<标签 属性"属…...

Vue3学习01 Vue3核心语法

Vue3学习 1. Vue3新的特性 2. 创建Vue3工程2.1 基于 vue-cli 创建项目文件说明 2.2 基于 vite 创建具体操作项目文件说明 2.3 简单案例(vite) 3. Vue3核心语法3.1 OptionsAPI 与 CompositionAPIOptions API 弊端Composition API 优势 ⭐3.2 setup小案例setup返回值setup 与 Opt…...

Spring Boot实现跨域的5种方式

Spring Boot实现跨域的5种方式 为什么会出现跨域问题什么是跨域非同源限制java后端实现CORS跨域请求的方式返回新的CorsFilter(全局跨域)重写WebMvcConfigurer(全局跨域)使用注解(局部跨域)手动设置响应头(局部跨域)使用自定义filter实现跨域 为什么会出现跨域问题 出于浏览器…...

Elasticsearch:从 ES|QL 到 PHP 对象

作者&#xff1a;来自 Elastic Enrico Zimuel 从 elasticsearch-php v8.13.0 开始&#xff0c;你可以执行 ES|QL 查询并将结果映射到 stdClass 或自定义类的 PHP 对象。 ES|QL ES|QL 是 Elasticsearch 8.11.0 中引入的一种新的 Elasticsearch 查询语言。 目前&#xff0c;它在…...

Stm32 HAL库 访问内部flash空间

Stm32 HAL库 访问内部flash空间 代码的部分串口配置申明文件main函数 在一些时候&#xff0c;需要存储一些数据&#xff0c;但是又不想接外部的flash&#xff0c;那我们可以知道&#xff0c;其实还有内部的flash可以使用&#xff0c; 需要注意的是内部flash&#xff0c;读写次数…...

线程池详解

线程池 什么是线程池&#xff1a;线程池就是管理一系列线程的资源池。 当有任务要处理时&#xff0c;直接从线程池中获取线程来处理&#xff0c;处理完之后线程并不会立即被销毁&#xff0c;而是等待下一个任务。 为什么要用线程池 / 线程池的好处&#xff1a; **降低资源消…...

mybatis(5)参数处理+语句查询

参数处理&#xff0b;语句查询 1、简单单个参数2、Map参数3、实体类参数4、多参数5、Param注解6、语句查询6.1 返回一个实体类对象6.2 返回多个实体类对象 List<>6.3 返回一个Map对象6.4 返回多个Map对象 List<Map>6.5 返回一个大Map6.6 结果映射6.6.1 使用resultM…...

数据应用OneID:ID-Mapping Spark GraphX实现

前言 说明 以用户实体为例&#xff0c;ID 类型包含 user_id 和 device_id。当然还有其他类型id。不同id可以获取到的阶段、生命周期均不相同。 device_id 生命周期通常指的是一个设备从首次被识别到不再活跃的整个时间段。 user_id是用户登录之后系统分配的唯一标识&#xff…...

第6章 6.2.3 : readlines和writelines函数 (MATLAB入门课程)

讲解视频&#xff1a;可以在bilibili搜索《MATLAB教程新手入门篇——数学建模清风主讲》。​ MATLAB教程新手入门篇&#xff08;数学建模清风主讲&#xff0c;适合零基础同学观看&#xff09;_哔哩哔哩_bilibili 在MATLAB的文本数据处理任务中&#xff0c;导入和导出文件是常…...

Matlab应用层生成简述

基础软件层 目前接触到的几款控制器&#xff0c;其厂商并没有提供simulink的基础软件库一般为底层文件被封装为lib&#xff0c;留有供调用API接口虽然能根据API接口开发基础软件库&#xff0c;但耗费时间过长得不偿失 应用层 所以可以将应用层封装为一个子系统&#xff0c;其…...

java 实现excel文件转pdf | 无水印 | 无限制

文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

如何在看板中有效管理突发紧急任务

在看板中有效管理突发紧急任务需要&#xff1a;设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP&#xff08;Work-in-Progress&#xff09;弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中&#xff0c;设立专门的紧急任务通道尤为重要&#xff0c;这能…...

镜像里切换为普通用户

如果你登录远程虚拟机默认就是 root 用户&#xff0c;但你不希望用 root 权限运行 ns-3&#xff08;这是对的&#xff0c;ns3 工具会拒绝 root&#xff09;&#xff0c;你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案&#xff1a;创建非 roo…...

ServerTrust 并非唯一

NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...

【Java学习笔记】BigInteger 和 BigDecimal 类

BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点&#xff1a;传参类型必须是类对象 一、BigInteger 1. 作用&#xff1a;适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...

九天毕昇深度学习平台 | 如何安装库?

pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子&#xff1a; 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf

FTP 客服管理系统 实现kefu123登录&#xff0c;不允许匿名访问&#xff0c;kefu只能访问/data/kefu目录&#xff0c;不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...

【Linux系统】Linux环境变量:系统配置的隐形指挥官

。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量&#xff1a;setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...

前端高频面试题2:浏览器/计算机网络

本专栏相关链接 前端高频面试题1&#xff1a;HTML/CSS 前端高频面试题2&#xff1a;浏览器/计算机网络 前端高频面试题3&#xff1a;JavaScript 1.什么是强缓存、协商缓存&#xff1f; 强缓存&#xff1a; 当浏览器请求资源时&#xff0c;首先检查本地缓存是否命中。如果命…...

数据结构第5章:树和二叉树完全指南(自整理详细图文笔记)

名人说&#xff1a;莫道桑榆晚&#xff0c;为霞尚满天。——刘禹锡&#xff08;刘梦得&#xff0c;诗豪&#xff09; 原创笔记&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 上一篇&#xff1a;《数据结构第4章 数组和广义表》…...