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 和 lombok对JavaBeans规范的定义理解并不一致导致识别字段结果不同,具体可以参考: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个呢?因为
lombok和Spring jackson对JavaBeans规范的定义理解不一致,导致从方法中推断出了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免费激活 网络下载加速器
把任务丢给软件,把时间还给自己,批量下载功能让下载变得更高效。它可以有效减少重复性操作,只需要一次简单的设置,就能把大量文件下载到电脑。有关网页文件批量下载工具有哪些,网页文件批量下载工具推荐的问题…...
嵌入式算法开发系列之图像处理算法
嵌入式系统中的图像处理算法及其应用 文章目录 嵌入式系统中的图像处理算法及其应用前言一、图像处理算法的原理二、图像处理算法的应用三、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语法 双标签:<标签 属性"属性值">内容</标签> 属性:给标签提供附加信息。大多数属性以键值对的形式存在。如果属性名和属性值一样,可以致谢属性值。 单标签:<标签 属性"属…...
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 对象
作者:来自 Elastic Enrico Zimuel 从 elasticsearch-php v8.13.0 开始,你可以执行 ES|QL 查询并将结果映射到 stdClass 或自定义类的 PHP 对象。 ES|QL ES|QL 是 Elasticsearch 8.11.0 中引入的一种新的 Elasticsearch 查询语言。 目前,它在…...
Stm32 HAL库 访问内部flash空间
Stm32 HAL库 访问内部flash空间 代码的部分串口配置申明文件main函数 在一些时候,需要存储一些数据,但是又不想接外部的flash,那我们可以知道,其实还有内部的flash可以使用, 需要注意的是内部flash,读写次数…...
线程池详解
线程池 什么是线程池:线程池就是管理一系列线程的资源池。 当有任务要处理时,直接从线程池中获取线程来处理,处理完之后线程并不会立即被销毁,而是等待下一个任务。 为什么要用线程池 / 线程池的好处: **降低资源消…...
mybatis(5)参数处理+语句查询
参数处理+语句查询 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实现
前言 说明 以用户实体为例,ID 类型包含 user_id 和 device_id。当然还有其他类型id。不同id可以获取到的阶段、生命周期均不相同。 device_id 生命周期通常指的是一个设备从首次被识别到不再活跃的整个时间段。 user_id是用户登录之后系统分配的唯一标识ÿ…...
第6章 6.2.3 : readlines和writelines函数 (MATLAB入门课程)
讲解视频:可以在bilibili搜索《MATLAB教程新手入门篇——数学建模清风主讲》。 MATLAB教程新手入门篇(数学建模清风主讲,适合零基础同学观看)_哔哩哔哩_bilibili 在MATLAB的文本数据处理任务中,导入和导出文件是常…...
Matlab应用层生成简述
基础软件层 目前接触到的几款控制器,其厂商并没有提供simulink的基础软件库一般为底层文件被封装为lib,留有供调用API接口虽然能根据API接口开发基础软件库,但耗费时间过长得不偿失 应用层 所以可以将应用层封装为一个子系统,其…...
React hook之useRef
React useRef 详解 useRef 是 React 提供的一个 Hook,用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途,下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...
【解密LSTM、GRU如何解决传统RNN梯度消失问题】
解密LSTM与GRU:如何让RNN变得更聪明? 在深度学习的世界里,循环神经网络(RNN)以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而,传统RNN存在的一个严重问题——梯度消失&#…...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...
Axios请求超时重发机制
Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式: 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...
处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的
修改bug思路: 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑:async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...
GitHub 趋势日报 (2025年06月06日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 590 cognee 551 onlook 399 project-based-learning 348 build-your-own-x 320 ne…...
redis和redission的区别
Redis 和 Redisson 是两个密切相关但又本质不同的技术,它们扮演着完全不同的角色: Redis: 内存数据库/数据结构存储 本质: 它是一个开源的、高性能的、基于内存的 键值存储数据库。它也可以将数据持久化到磁盘。 核心功能: 提供丰…...
API网关Kong的鉴权与限流:高并发场景下的核心实践
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 引言 在微服务架构中,API网关承担着流量调度、安全防护和协议转换的核心职责。作为云原生时代的代表性网关,Kong凭借其插件化架构…...
webpack面试题
面试题:webpack介绍和简单使用 一、webpack(模块化打包工具)1. webpack是把项目当作一个整体,通过给定的一个主文件,webpack将从这个主文件开始找到你项目当中的所有依赖文件,使用loaders来处理它们&#x…...
ArcPy扩展模块的使用(3)
管理工程项目 arcpy.mp模块允许用户管理布局、地图、报表、文件夹连接、视图等工程项目。例如,可以更新、修复或替换图层数据源,修改图层的符号系统,甚至自动在线执行共享要托管在组织中的工程项。 以下代码展示了如何更新图层的数据源&…...
