SpringMVC源码探索之RequestBody的工作原理
遇到一个很奇怪的问题,后面发现了问题所在,原因是自己太过匆忙、连快捷键都被复制粘贴省略了。虽然出现问题的原因有点傻逼,但是之所以出现这种问题的原因却更加引人入胜。
问题现象描述
Controller中没有逻辑,只有一个@RequestBody
注释的form表单
然而这个TestBean有点特殊,非一般的get/set方法
按照常理,发送post请求,body中塞入{"param":"hello"}
即可成功注入到TestBean的实例中
高潮在后面,如果我改成{"paramA":"hello"}
,结果却能成功接收:
综上所述,paramA
为之前定义的一个变量,我把它改成了新的变量param
,然后顺便把paramA
的get/set方法中的this.paramA
改成了this.param
。然后就出现了上面的情况。要知道,这个现象很反常,并且不常见。非常想知道为什么!
怎么通向背后的原因
从源码中看spring是如何处理@RequestBody
站在巨人肩膀上
从网上的参考资料来看,处理相关内容的源码可能位于一个叫做readWithMessageConverters()
方法中。打开该方法所在的类(双击shift,然后输入方法名),大致是八九不离十的,如下:
打开至该方法后,使用debug模式,在可能的地方打上断点,发送postman请求,等待请求的到来:
接着往下看,寻找到一个能将JSON转换成对象的方法:
此时的messageConverters和body内容如下:
这里的read(),才是真正的开始。
向着jackson-databind的源码出发
跟着上面的read()方法,一路打断点达到了这里,这可以说是最终setter方法的执行处,代码如下:
所以,为什么setter方法会变成setParamA()呢? 目前为止,可行的解释为:jackson是根据已解析的出来的json的键,去寻找对应键的setter方法,与对应的Form表单类中的成员变量的值无关,因此也解释了刚开头那一幕戏剧性的现象。但是,源代码在哪里?
如何寻找对应的setter方法
经过一次又一次的打断点、跟踪代码运行,终于将规则锁定在了POJOPropertiesCollector.java
这个类上了。这里面还涉及到了打断点、跟踪代码的一些技巧,一定要分清F7、F8、F9呀,不然就很容易就要从头再来。
从上面的代码来看,对TestForm
的解析,变成了只有一个变量为paramA
的bean类。所以,按照paramA
的键,去JSON中取数据,然后调用对应的setter方法,最后赋值给了param
,过程就是这样。
总结
对这个流程的跟踪,发现:只有第一次请求的时候,会初始化一个把JSON转化成对象的这样一个类,缓存使用的是LRUMap。其中desc是一个解析器,包含了变量与getter/setter方法之间的映射关系。关键的代码如下:
调试跟踪的过程中,主要的时间都花在了desc的初始化上。因为后续的调用,就是根据desc的映射关系去执行。
如何根据getter/setter去解析出成员变量名称?
其实这个流程并不难,我们自己也很容易想到,但是就是想看框架里面是怎么写的。在传给这个函数之前,已经保证了basename是以get开头,且此时的offset为3。
什么情况下删除param?
param即没有setter/getter方法的那个属性。这里因为param被private修饰,然后也未找到setter/getter方法。
具体判断是否可见的代码如下:
以上。于2019/01/31 00:51完成全部内容。
还没有评论,来说两句吧...