spring mvc 解析 @RequestMapping @RequestBody 过程

1,909 阅读5分钟

解析@RequestMapping

问题:如果让我们自己实现一个MVC,通过url能够路由到某个方法上,我们会怎么做?

**答:**我们会先维护一个URL 与 Controller和其具体执行方法之间的一个关系,然后在调用的时候找到对应的controller,执行对应的方法。

注册URL与方法之间的映射关系。

具体执行逻辑如下图

图片

代码时序图如下图

图片

时序图重点地方代码解析

  1. 需要部分校验工作基本上都是在 processCandidateBean(beanName)中做的,比方说校验controller上是否带有@Controoer注解,或者@RequestMapping注解,这个bean是否已经注册到上下文中等等。

  2. 核心执行功能如下:

图片

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 文档