目录
一、前言
二、源码跟踪
三、总结
一、前言
之前写项目遇到的一个Bug,下面是模拟代码。
新建一个springboot的项目,Person一个实体类,定义一个方法传一个JSON数据
@Data
public class Person {
private String name;
private String aName;
private String aaName;
}
@SpringBootApplication
@RestController
public class LombokTestApplication {
public static void main(String[] args) {
SpringApplication.run(LombokTestApplication.class, args);
}
@PostMapping("/user")
public String userSearch(@RequestBody Person person){
System.out.println(person);
return "okk~";
}
}
springboot启动之后postman发送一次请求。
请求路径:http://localhost:8080/user
JSON数据:
{
"name": "aaa",
"aName": "bbb",
"aaName": "ccc"
}
后台输出结果
我们会发现,aName字段为null?居然接收不了,而aaName可以接受,这是为什么呢?
二、源码跟踪
分析这个源码要对SpringMVC框架有一定的理解,它的最基础的请求处理也是基于原生的servlet去进行了一个扩展,它扩展的一个核心叫做 DispatcherServlet ,它的所有的http请求都会去走 doDispatch 这个方法。
在大概540行左右的handle,这里在调用,我们关注的是 为什么那个aName参数它没有解析出来,后面执行会调用 InvocableHandlerMethod 里面的 getMethodArgumentValues 方法。
在方法的return部分我们打了个断点,看看会返回啥?
发现这个方法返回就使aName变成了null(这里就已经完成了参数解析了,说明问题出在这一块)。
然后进过一系列的操作,我们最终来到了 POJOPropertiesCollector 的 collectAll 这个方法,我们在 _addFields 与 _addMethods 打个断点。
跑一下,在_addFields他的size为0,我们再来看看_addMethods,它的size为3。
我们点击下一步(不是下一个断点)发现他的长度变成了4了,而且还多了个aname的字段
按住ctrl键点击_addMethods我们进去看看,这个方法填加了一个字段。
protected void _addMethods(Map<String, POJOPropertyBuilder> props) {
Iterator var2 = this._classDef.memberMethods().iterator();
while(var2.hasNext()) {
AnnotatedMethod m = (AnnotatedMethod)var2.next();
int argCount = m.getParameterCount();
if (argCount == 0) {
this._addGetterMethod(props, m, this._annotationIntrospector);
} else if (argCount == 1) {
this._addSetterMethod(props, m, this._annotationIntrospector);
} else if (argCount == 2 && Boolean.TRUE.equals(this._annotationIntrospector.hasAnySetter(m))) {
if (this._anySetters == null) {
this._anySetters = new LinkedList();
}
this._anySetters.add(m);
}
}
}
我们再点击下一步,一直到_removeUnwantedAccessor这个,发现size变成3了,这里就删除了我们的aName字段,保留了aname,这就是问题的关键了。
这里我们要看看这个@Data到底生成了什么东西,我们在终端敲入命令:mvn clean package
我们看看反编译出来的到底是个啥?
左边是手动生成的get set 方法,右边是@Data 生成的,我们发现了自己生成的 getaName 它的a是小写的,而@Data生成的getAName的A是大写的。
而getAName可能反推出aName和aname两个可能,所以在_removeUnwantedAccessor这里就删除了aName这个字段,就后台接受不到了。
三、总结
这个bug虽然不算什么,但是这也是个细节,虽然像aName这种不规范的命名字段很少用,但是这里模拟当初项目的出错,这里还是建议大家使用手动或者快捷键生成的实体类的方法(当然不是Lombok不好用,它也有它的优点)