目录
解析@RequestMapping
问题:如果让我们自己实现一个MVC,通过url能够路由到某个方法上,我们会怎么做?
答:我们会先维护一个URL 与 Controller和其具体执行方法之间的一个关系,然后在调用的时候找到对应的controller,执行对应的方法。
注册URL与方法之间的映射关系。
具体执行逻辑如下图
代码时序图如下图
时序图重点地方代码解析
-
需要部分校验工作基本上都是在 processCandidateBean(beanName)中做的,比方说校验controller上是否带有@Controoer注解,或者@RequestMapping注解,这个bean是否已经注册到上下文中等等。
-
核心执行功能如下:
1. 满足条件方法执行`getMappingForMethod(Method,Class<?>)`,构造一个执行方法与url映射的对象,一般情况下这个对象就是 RequestMappingInfo
需要注意的是,有时候我们Controller Bean 上面有@RequestMapping,有时候没有,所以这个地方还得解析 Controller Bean 上的@RequestMapping,如果有就将其合并起来
2. 将满足要求的对象放在一个Map中,其中泛型T 其实就是 RequestMappingInfo ,
3. 遍历刚才的Map,将其中的信息注册起来,其实就是将信息细化,放到各自的内存map中,这个注册的过程又会将RequestMappingInfo 转换成换成HandlerMethod,后期在DispartcherServlet中处理的都是HandlerMethod,如下图,红框中内容就是核心操作
以上步骤基本将就是将url与方法的映射关系维护起来了,整体操作还是比较简单,方法调用也没有多复杂,就只用到了一个模板方法模式 基本实现都是在抽象类AbstractHandlerMethodMapping中都完成了的。
通过URL 调用@RequestMapping对应方法
下面两个时序图来分析spring mvc中执行@RequestMapping方法的全过程
时序图关键节点代码讲解
时序图中1处代码如下,getHandlerInternal获取到就是在系统启动时候放在配置类Map中的HandlerMethod,实现很简单,从原先配置过的Registry的全局Map中通过URL找到HandlerMethod
2处代码获取的就是执行HandlerMethod的适配器,所有的操作都是通过适配器类操作的,
在这里留下一个问题:为什么spring这里需要创建一个适配器,不直接通过invoke的方式调用HandlerMethod 中的controller呢?
在3之后4之前都是处理环境,解析参数等等,真正执行的就是4 处了
执行的逻辑就是先解析参数,然后通过动态代理的方式调用到代理对象就玩了
以上就是解析@RequestMapping 以及调用@RequestMapping的整体过程,虽然还有很多细节性的东西还没有讲到,比如说跨域、异步、参数解析、类名解析、异常处理等,但是些都是让程序更健壮的地方。
解析@RequestBody
问题:同样,如果是让我们设计解析过程,我们会怎么做?
答:首先我会校验,带有@RequestBody的参数是否必传,然后再将request中的参数转换成对应的参数类型,然后传参执行
实际情况: 其实基本差不多,但是实际实现的逻辑会更加健壮。有一点区别就是他会先解析参数,如果没有解析到参数,再来判断参数是否必传。
按照传统线上序列图
核心逻辑代码解析
上面序列图核心逻辑代码已经用红框框标识出来了
1处:很重要将RequestMappingHandlerAdapter中的HandlerMethodArgumentResolverComposite设置到ServletInvocableHandlerMethod中去
有个问题:
问题:为什么需要用HandlerMethodArgumentResolverComposite 而不直接使用HandlerMethodArgumentResolver ?
在实际项目中传参的形式千变万化,怎么样能够做到最大限度的兼容呢?通过这个HandlerMethodArgumentResolverComposite 其实就是 HandlerMethodArgumentResolver的装饰类,来丰富功能
,将其所有的HandlerMethodArgumentResolver都放在其私有属性argumentResolvers这个集合中。
HandlerMethodArgumentResolver继承图
那么在实际使用的时候,不同的场景就用其不同实现类来解析参数就可以了
找到真正解析@RequestBody的 参数解析器
在上面说到的装饰类中有一个私有getArgumentResolver(parameter) 来找到真正执行解析参数的 HandlerMethodArgumentResolver
不同的解析器解析逻辑是很不一样,
解析@RequestBody的参数解析器为
RequestResponseBodyMethodProcessor,代码如下,实质上还是交给其父类AbstractMessageConverterMethodArgumentResolver在处理,
依据上面的逻辑,解析@RequestParam @PathVariable这两个注解的其实
也是HandlerMethodArgumentResolver的子类
解析 @PathVariable 的是 PathVariableMapMethodArgumentResolver
解析@RequestParam的是 RequestParamMethodArgumentResolver
以上就是解析@RequestMapping 以及 @RequestBody @RequestParam @PathVariable的整体逻辑
配上 官方 SPRING MVC 文档