spring5源码篇(12)——spring-mvc请求流程

spring-framework 版本:v5.3.19

文章目录

  • 一、请求流程
    • 1、处理器映射器
      • 1.1、 RequestMappingHandlerMapping
      • 1.2、获取对应的映射方法
      • 1.3、添加拦截器
    • 2、获取合适的处理器适配器
    • 3、通过处理器适配器执行处理器方法
      • 3.1、拦截器的前置后置
      • 3.2、处理器的执行
        • 3.2.1 参数解析器解析参数和执行处理器方法
        • 3.2.2 结果处理器处理结果
        • 3.2.3 消息转化器
    • 4、视图解析器
  • 二、请求流程中的一些问题
  • 1、各种组件如处理器映射器,处理器适配器,视图解析器等是如何注入到spring容器的?
  • 2、RequestMappingInfoHandlerMapping 处理器映射器中维护的映射map (即MapingRegister) 是如何初始化的?
  • 3、处理器映射器中的拦截器是如何添加的?
  • 4、处理器适配器的参数解析器和结果处理器是如何来的?
  • 5、参数解析器/结果处理器中的消息转化器是如何来的?

一、请求流程

总体流程在 DispatchServelt#doDispatch 方法
spring5源码篇(12)——spring-mvc请求流程_第1张图片

1、处理器映射器

首先会获取根据url去映射对应的处理器(即接口执行方法)
看到对应的 getHandler 方法
spring5源码篇(12)——spring-mvc请求流程_第2张图片
为方便阅读,进入debug。可以看到springmvc默认为我们注册了三个handlerMapping。

springMvc中的各个组件如处理器映射器,处理器适配器,视图解析器等是如何注入到容器的,先暂时不看,第二部分在看(问题1)。

1.1、 RequestMappingHandlerMapping

而其中我们最常用的一个就是RequestMappingHandlerMapping,所以这里只看这个类的 getHandler 方法(其实也是其抽象父类的方法)。
spring5源码篇(12)——spring-mvc请求流程_第3张图片

1.2、获取对应的映射方法

getHandlerInternal 方法入手,看看是如何把一个请求映射到对应的方法的。
spring5源码篇(12)——spring-mvc请求流程_第4张图片
这里首先会从 mappingRegister 中去找可以根据url直接得到的映射(即没有替换符),如果找不到再去所有注册的映射中找,然后返回一个最匹配的映射对应的处理器。

mappingRegister 可以简单的理解成 Map。实际上,为提供效率一共维护了3个map
spring5源码篇(12)——spring-mvc请求流程_第5张图片
这些map是如何初始化的,第二部分再讲(问题2)。

至于是如何匹配这些映射的,从 addMatchingMappings 入手
spring5源码篇(12)——spring-mvc请求流程_第6张图片
这里其实就是把匹配逻辑抽象成一个个condition,如果满足所有 condition 说明这是一个可用的处理器。注意,仅是可用而已,并不一定会用这个处理器。因为可能同时会匹配到多个可用的处理器,但是最终只会返回最匹配的那一个。

1.3、添加拦截器

在前面的总体流程中注释到,获取处理器的时候,返回的是一个处理器链。因为可能会配置有拦截器,而拦截器的添加从 getHandlerExecutionChain 入手。
spring5源码篇(12)——spring-mvc请求流程_第7张图片
可以看到,就只是遍历当前 handlerMapping 所添加的所有拦截器,若匹配,则将该拦截器添加到处理器链中而已。至于 handlerMapping 的拦截器是如何添加的,第二部分再讲(问题3)。

2、获取合适的处理器适配器

spring5源码篇(12)——spring-mvc请求流程_第8张图片
Spring MVC可能会有多种类型的处理器,例如控制器(Controller)、RESTful控制器等。处理器适配器负责选择并调用合适的处理器来处理请求。这一步就是去获取一个合适的处理器适配器,其实就是一个简单遍历获取,在spring mvc 默认提供的几种适配器中,最常用的还是 RequestMappingHandlerAdapter

3、通过处理器适配器执行处理器方法

3.1、拦截器的前置后置

在正式看处理器之前,顺便提一嘴拦截器的执行。
在执行实际业务方法之前会先执行处理器链中拦截器前置方法。同理,执行完业务方法后会执行处理器链中拦截器的后置方法。
spring5源码篇(12)——spring-mvc请求流程_第9张图片
拦截器的执行就是一个遍历,就不上代码了。

3.2、处理器的执行

ha.handle 入手
spring5源码篇(12)——spring-mvc请求流程_第10张图片
不管是哪种适配器,最终都会依次通过 参数解析器、处理器、结果处理器 将方法返回值封装成 ModelAndView 对象。

3.2.1 参数解析器解析参数和执行处理器方法

从 invokeForRequest 入手
spring5源码篇(12)——spring-mvc请求流程_第11张图片
处理器方法的执行就是反射实现的,没啥好看的。主要看参数解析器是如何解析参数的。
spring5源码篇(12)——spring-mvc请求流程_第12张图片
InvocableHandlerMethod 中维护了名为 resolvers 的 HandlerMethodArgumentResolverComposite 对象(多个参数解析器的封装),并且这个变量的值是处理器适配器传过来的。至于处理器适配器的参数解析器是哪来的,先暂时不看,后面第二部分再看(问题4)。这里只需知道参数解析器是在这里解析的以及值是处理器适配器传过来的就可以了。

3.2.2 结果处理器处理结果

从 returnValueHandlers.handleReturnValue 入手
spring5源码篇(12)——spring-mvc请求流程_第13张图片
**跟参数解析器维护的复合对象一样,也维护了一个名为 returnValueHandlers 的 HandlerMethodReturnValueHandlerComposite 复合对象,并且这个变量的值也是处理器适配器传过来的。**至于处理器适配器的结果处理器是哪来的,也先暂时不看。

一般来说参数解析器的命名为 xxxMethodArgumentResolver 而结果处理器的命名为 xxxMethodReturnValueHandler。但是如果即是参数解析器又是结果处理器的命名就会xxxMethodProcessor。而我们常用的 RequestResponseBodyMethodProcessor 就是这种类型。
在这里插入图片描述

3.2.3 消息转化器

首先并不是所有的参数解析器或者结果处理器都会用到消息转化器。,但至少我们最常用的 **RequestResponseBodyMethodProcessor 无论是解析参数还是处理结果,都会用到消息转化器。**对应的方法分别是 readWithMessageConverters,writeWithMessageConverters。并且write是直接写到 http 的response,这也意味着 @ResponseBody 不需要通过视图解析器解析渲染视图。
spring5源码篇(12)——spring-mvc请求流程_第14张图片

在 RequestResponseBodyMethodProcessor 中无论是read还是write,都会去遍历参数解析器/结果处理器中维护的消息转化器列表。至于 参数解析器/结果处理器的消息转化器列表是哪里来的,也先暂时不看,后面在看(问题5)。

4、视图解析器

试想这么一个接口,没有 @ResponseBody 并且返回值是一个字符串。

如果有 @ResponseBody 注解,ModelAndView为null,无需解析渲染视图。
在这里插入图片描述
在我们配置如下的视图解析器时会访问到servlet容器下的mv.html页面。
spring5源码篇(12)——spring-mvc请求流程_第15张图片
从字符串到返回的具体页面,这正是视图解析器所做的事情。

关于视图解析器,从 processDispatchResult 入手
spring5源码篇(12)——spring-mvc请求流程_第16张图片
解析:如果 ModelAndView 有视图名(在上面的例子中视图名就是方法返回的字符串)就会调用视图解析器去解析视图名得到一个视图 View 对象,反之就从 ModelAndView 中直接获取 View 对象。总之,不管怎样,这里一定要有 View 对象,没有就报错。
渲染:然后再调用上一步得到的 View 对象 render 方法并将 ModelAndView 里的 Model 数据传入,从而将数据渲染带视图上(如:jsp)。最后将渲染后的结果返回至前端。


至此一个完整的请求流程就看完了。为更形象记忆 spring mvc 各组件之间的关系,这里附上一张网图
spring5源码篇(12)——spring-mvc请求流程_第17张图片

二、请求流程中的一些问题

1、各种组件如处理器映射器,处理器适配器,视图解析器等是如何注入到spring容器的?

答:@EnableWebMvc注解(相当于xml配置: )。
spring5源码篇(12)——spring-mvc请求流程_第18张图片
@EnableWebMvc 引入了 DelegatingWebMvcConfiguration,而这个类中就默认注入了 spring mvc 的各种组件。
比如:
spring5源码篇(12)——spring-mvc请求流程_第19张图片

2、RequestMappingInfoHandlerMapping 处理器映射器中维护的映射map (即MapingRegister) 是如何初始化的?

答:在 RequestMappingInfoHandlerMapping bean生命周期的 afterPropertiesSet。
spring5源码篇(12)——spring-mvc请求流程_第20张图片

3、处理器映射器中的拦截器是如何添加的?

答:以 RequestMappingInfoHandlerMapping 为例,在 DelegatingWebMvcConfiguration 注入处理器映射器的时就同时会去set拦截器。
spring5源码篇(12)——spring-mvc请求流程_第21张图片
其中 getInterceptors 代码如下
spring5源码篇(12)——spring-mvc请求流程_第22张图片
说白了就是 getInterceptors 方法,会依次调用所有的 WebMvcConfigurer.addInteceptors 方法将自定义配置的拦截器添加到处理器映射器中的 InterceptorRegistry。之后处理器映射器在映射处理器时,将其中匹配的拦截器添加到处理链,进而实现拦截器的效果。

正是因为自动注入的原理,所以我们平时配置spring mvc时只需注入一个实现 WebMvcConfigurer 接口的类。

但其实截至目前为止添加的还只是一个原始版本的拦截器,真正使用的拦截器是适配后的版本。
适配的时机是在处理器映射器生命周期的 setApplicationContext 方法。(最后会调用到 initApplicationContext)
spring5源码篇(12)——spring-mvc请求流程_第23张图片

4、处理器适配器的参数解析器和结果处理器是如何来的?

答:在 RequestMappingHandlerAdapter bean生命周期的 afterPropertiesSet。

spring5源码篇(12)——spring-mvc请求流程_第24张图片
具体添加了哪些参数解析器,结果处理器就不细看了,太多了…

5、参数解析器/结果处理器中的消息转化器是如何来的?

答:来自处理器适配器,而处理器适配器的消息转化器来自 WebMvcConfigurer 配置或者默认配置。

如下图:
(参数解析器/结果处理器中的消息转化器来自处理器适配器)
spring5源码篇(12)——spring-mvc请求流程_第25张图片
(适配器的来自 WebMvcConfigurer 配置或者默认配置)
spring5源码篇(12)——spring-mvc请求流程_第26张图片

至于this.configurers.configureMessageConverters(converters);
首先configurers在前面第三个问题已经看过了,就是自动注入的 WebMvcConfigurer 总和。所以这句总的来说就是依次调用所有的 WebMvcConfigurer.configureMessageConverters 方法将自定义配置的消息添加到处理器适配器中。

你可能感兴趣的:(Spring,spring,mvc,java)