SpringMVC源码探索之RequestBody的工作原理

阳光穿透心脏的1/2处 2022-03-22 14:56 724阅读 0赞

遇到一个很奇怪的问题,后面发现了问题所在,原因是自己太过匆忙、连快捷键都被复制粘贴省略了。虽然出现问题的原因有点傻逼,但是之所以出现这种问题的原因却更加引人入胜。

问题现象描述

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呀,不然就很容易就要从头再来。
to be continued...
从上面的代码来看,对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完成全部内容。

发表评论

表情:
评论列表 (有 0 条评论,724人围观)

还没有评论,来说两句吧...

相关阅读

    相关 springmvc工作原理

    好久木有来更新了,最近一直在整前端相关的东西,一直打算写点前端各种JS或大数据相关的东西。 先来更新并记录下spingmvc的工作原理: a、用户向服务器发送请求,请求被s