Lombok
Lombok 和 Jackson 矛盾错误
- @getter,@setter在遇到形如aName的时候,jakson无法将其正确反序列化
- 当 RESTful 接口接收到客户端发送的 JSON 请求体 时,Spring Boot 使用 Jackson 将 JSON 数据反序列化为 Java 对象。
- 得出结论Lombok在转换的时候将其转换为AName,符合直觉,但是jakson无法识别。
- 示范如下:
- 用Lombok实现getter,setter等配置的自动注入:
@Data
public class User {
private String name;
private String aName;
private String aaName;
}
发现get,set方法正确实现,符合直觉
@Generated
public void setName(final String name) {
this.name = name;
}
@Generated
public void setAName(final String aName) {
this.aName = aName;
}
@Generated
public void setAaName(final String aaName) {
this.aaName = aaName;
}
json请求字段
{
"name" : "name",
"aname" : "aname",
"aaName" : "aaName",
"aName" : "aName"
}
发现传过来的只有aname: aname, 却被识别为aName的字段,但其实是aname的字段。
这个显然是jakson传入时候的问题
User(name=name, aName=aname, aaName=aaName)

改成这个就好了
@Data
public class User {
private String name;
@JsonProperty("aName") // 明确指定 JSON 键名
private String aName;
private String aaName;
}
debug流程图展示
这里要跟的是 @RequestBody User user 的参数解析过程。进入 Controller 时,JSON 已经转换成 User,所以断点要打在 Controller 之前。
下面按 Spring MVC 6.1.8、Jackson 2.17.1 源码列关键调用点,省略部分辅助调用;实际调试以项目依赖版本为准。
DispatcherServlet.doDispatch
-> getHandler:找到 /user 对应的 HandlerMethod
-> getHandlerAdapter:取得 RequestMappingHandlerAdapter
-> RequestMappingHandlerAdapter.handleInternal
-> invokeHandlerMethod
-> ServletInvocableHandlerMethod.invokeAndHandle
-> InvocableHandlerMethod.invokeForRequest
-> getMethodArgumentValues:准备 Controller 的参数
-> HandlerMethodArgumentResolverComposite.resolveArgument
-> RequestResponseBodyMethodProcessor.resolveArgument:处理 @RequestBody
-> readWithMessageConverters:根据类型和 Content-Type 选择转换器
-> MappingJackson2HttpMessageConverter
(read、readJavaType 实现在父类 AbstractJackson2HttpMessageConverter 中)
-> ObjectReader.readValue:把请求体交给 Jackson
这条链路可从 RequestMappingHandlerAdapter、RequestResponseBodyMethodProcessor 和 AbstractJackson2HttpMessageConverter 接着往下跟。
对于这里普通 JavaBean 的 setter 赋值路径,继续跟:
ObjectReader.readValue(InputStream / Reader)
-> _bindAndClose
-> DefaultDeserializationContext.readRootValue
-> BeanDeserializer.deserialize
-> vanillaDeserialize:普通 Bean 的快速路径
-> _beanProperties.find(propName):用 JSON 键名查找属性
-> MethodProperty.deserializeAndSet:解析属性值
-> _setter.invoke(instance, value)
-> User.setAName(value)
-> 参数解析返回 User 对象
-> InvocableHandlerMethod.doInvoke
-> Controller 的 user 方法
vanillaDeserialize 不是所有对象都会经过;字段直接赋值、构造器绑定、自定义反序列化器会走不同分支。看 BeanDeserializer 和 MethodProperty 时,要先确认当前实际进入的分支。