我反正开发三年多,最常用的还是正常参数的形式传递获取。不过这个路径传参据说是restful风格的常用方式(我是个土鳖,restful风格也没实际用过)。
所以针对多种传参方式,这里也简单的记录一下:
路径传参
这个不仅仅是restful风格可以用,其实本质就是从路径的某段中获取参数,只要使用一个注释:
@PathVariable
下面是简单的使用 demo:
然后我们去接口访问这个方法:
事实证明,路径上带参数,我们用@PathVariable注解获取到了。
至于最后一个获取map的用法,其实在注解中是有说到的。
获取请求头信息
这个其实也挺有用的,很多时候token都是放在请求头的,当然了还有别的东西也都ok。而获取请求头的方法除了我们在方法中用request获取,也可以直接获取。
@RequestHeader
其实这个方法和上面那个差不多。可以指定获取,也可以用map获取全部的。如下demo:
虽然看上去不怎么好看,但是起码证明确实获取到请求头了。这个就过了。
获取参数
这个就是正常参数了,依然一个注解:
@RequestParam
使用方法和上面一样,指定参数名称,或者获取全部参数(全部参数的话必须是String,String 的)。
获取cookie
依旧一个注解开始:
@CookieValue
需要注意的是这个cookie是没有map的,看官网文档的说法,要用cookie接收:
因为我没前端页面都,所以这个访问要用postman来添加cookie再测试:
cookie的接收测试完毕。
常用的就这几个,接下来重点说参数获取的原理(这里用debug一步一步调试):
首先我们的测试代码是这样的:
其实这个是继续上文说找到那个请求处理器以后的发展了,上文说通过DispatcherServlet类的getHandler方法,找到url所对应的方法全名称。这里仍然debug一步一步走:
这里有一个小窍门:一般带注释的方法都是值得一看的。我是习惯性百度翻译一下看看的。然后继续往下走到下一个方法:
ps:这里很多走到接口,然后进入实现方法的。反正见到方法就进入就行了
其实这个类应该是个很重要的类,毕竟大量invoke方法。然后我们一步一步往下走:
因为我这个方法就是普通方法,所以走到这个方法中。其实从它的名字中午文翻译:反射 处理器 方法。大概可以猜测是方法反射的处理器。
点进去以后继续一步一步走:
这个方法中代码很多,但是首先我觉得set本身不会存在赋值行为,所以说前面所有的set我都一路往下。最终走到一个看名字就高大上是方法:
然后点进这个方法:
往里走接下来我手欠走过了,如下截图:
到我当前断点的位置,参数都已经赋值了,因为我断点已经过了, 所以直接看看走过的代码:
其实这个代码逻辑还算是简单,首先创建了一个参数长度的Object数组用来存参数。然后还是一个个 去找参数的值(中间用到了参数解析器)。找到以后continue,去找下一个参数。这里有一点要注意:找不到的话,会报错的!就是我常见的那个错:
哎,因为断点跟丢了现在好难受,我从新走一遍吧。
继续上面说的,给参数赋值的方法如下:
点进去查看:
继续点进去:
这个就用到了我们上面说的spring boot自带的26个参数类型解析器。可以点进去瞅瞅:
进方法中看:
这里重点的是我圈起来的:argumentResolvers这个参数就是spring boot默认的所有解析器的集合。
如果这里返回null说明当前参数类型不支持,所以为空,最终出去就直接报错了。
当有这个类型以后去赋值,继续往下走到了赋值的方法:
其实这里又查找了下,确定是有这个类型的。继续往下走:
其实这里很有意思,大家可以注意这个解析器和注解的名称密切相关啊。比如我demo中用的两个注解:一个@PathVariable一个@RequestParam。都分别有map和普通单个的解析。而且大多数都挺见名知意的,有空可以详细琢磨琢磨这26个解析器。
这里单独说一下我们的实体类是怎么解析的。正常来讲传一个实体对象,然后debug就行了,我这里直接说结果了:
实体类的参数解析器是这个:ServletModelAttributeMethodProcessor
接下来咱们在代码中一步一步走:
debug代码走向:
然后我们看看哪个类型解析器解析了这个实体类参数:
这个因为类型解析器有26个,比较多。所以debug 的时候千万要看好。我就跟丢两次。然后有些一看就不是的注解就不用进去了。比如这个第一个@RequestParam类型的,绝对不可能直接往下走,省的耽误时间。我直接说比对完是哪个类型了:ServletModelAttributeMethodProcessor。我们可以看下走进去的代码:
然后我们走进去看看是怎么绑定属性的:
这个方法我也是跟着走了好几次代码。。这里调试一定要慢,宁可每个方法都进去走无用功也别贪快错过去。
这里主要用反射创建这个类的实例
这个时候这个实例是空的,里面没有任何值:
然后重点来了,赋值的方法是这:bindRequestParameters(binder, webRequest);我来回调试了三四次才确定了这么一个方法。执行前attribute没值,执行后有了。。一眨眼就错过:
反正是点点点进到了这个方法,获取实体类的属性并赋值
从doBind方法进入,然后再进入父类的doBind方法:
到这里可以一步一步进入看了,也算做到了见名知意(虽然我是进了方法才知道干嘛用的)。简单说下,第一步看有没有不许出现的属性。第二步需要的属性。
这个方法是比对属性是否可以传。我们可以设置实体对象种某些属性不能这么传入。如果不能传的属性现在传过来就会报错:
第二步检查需要的属性:
第三步走进来赋值:
就是这一步把实现了给attribute赋值。
接下来我们继续往下走:
这里只要数据类型是对的,一定是可以转化的吧。我暂时没走过不是的分支。。
最后一直往后走,return attribute,然后我们的参数已经是这个对象了。就完事了。
其实这里可能是因为框架完善的原因,很多设计是为了扩展性或者维护性啥。所以debug的时候要不断父子类方法跳,而且还有我上面说的都是大致走向,很多细节还有重要的方法都没怎么说。总结一下我对这块的理解吧:
- 获取参数个数。
- 判断参数是不是spring boot能解析的类型(这里涉及到spring boot默认的解析器)。
- 获取参数上的注释和指定名称(如果指定名称的参数不存在则报错)。
- 将参数名称和实际值对应起来。
- 有些复杂类型数据用web数据绑定器,将请求参数的值绑定到指定的JavaBean里面。WebDataBinder 利用它里面的 Converters 将请求数据转成指定的数据类型。再次封装到JavaBean中。
- 所有的参数都这么走一遍。
然后就over了。说起来简简单单,各种断点跳转方法几十个。来回走经常容易丢。。真的佩服写spring 的大佬。
本篇笔记就到这里,如果稍微帮到你了记得点个喜欢点个关注,也祝大家工作顺顺利利!另外如果文中哪里表述不严瑾或者有问题欢迎指出!