⭐表示重要。

第一章:总体阶段

1.1 流程描述

  • 目标 handler 方法执行
    • 建立调用链,确定整个执行流程。
    • 拦截器的 preHandle() 方法。
    • 注入请求参数。
    • 准备目标 handler 方法所需要的所有参数。
  • 调用 目标 handler 方法:
  • 目标 handler 方法执行后:
    • 拦截器的 postHandle() 方法。
    • 渲染视图。
    • 拦截器的 afterCompletion() 方法。

1.2 核心代码

  • 整个请求处理过程都是 doDispatch() 方法在宏观上协调和调度,把握了这个方法就理解了 SpringMVC 总体上是如何处理请求的。
  • 所在类: org.springframework.web.servlet.DispatcherServlet 。
  • 所在方法:doDispatch() 。
  • 核心方法中的核心代码:
  1. // Actually invoke the handler.
  2. mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

第二章:调用前阶段

2.1 建立调用链

2.1.1 相关组件

  • 所在类:org.springframework.web.servlet.HandlerExecutionChain 。

建立调用链相关组件.png

  • 拦截器的索引默认是 -1,说明开始的时候,它指向的是第一个拦截器前面的位置,每执行一个拦截器,就将索引向后移动一个位置,所以这个索引每次都是指向当前的拦截器,所以它相当于拦截器的 指针

2.1.2 对应操作

  • 所在类:org.springframework.web.servlet.handler.AbstractHandlerMapping 。

建立调用链的对应操作.png

  • 调用链是由拦截器和目标 handler 对象组成的。

2.2 调用拦截器 preHandle()

  • 所在类:org.springframework.web.servlet.DispatcherServlet 。
  • 所在方法:doDispatch() 。

调用拦截器 preHandle() 的 doDispatch().png

  • 具体调用细节:正序调用。
  • 所在类:org.springframework.web.servlet.HandlerExecutionChain 。
  • 所在方法:applyPreHandle 。

applyPreHandle.png

  • 从这部分代码我们也能看到,为什么拦截器中的 preHandle() 方法通过返回布尔值能够控制是否放行。
  • 每一个拦截器的 preHandle() 方法都返回 true:applyPreHandle() 方法返回 true,被取反就不执行 if 分支,继续执行后续操作,这就是放行。
  • 任何一个拦截器的 preHandle() 方法返回 false:applyPreHandle() 方法返回 false,被取反执行 if 分支,return,导致 doDispatch() 方法结束,不执行后续操作,就是不放行。

2.3 注入请求参数

2.3.1 相关组件

  • 接口:org.springframework.web.servlet.HandlerAdapter 。
  • 作用:字面含义是适配器的意思,具体功能有三个
    • 将请求参数绑定到实体类对象中。
    • 给目标 handler 方法准备所需的其他参数,例如:
      • Model、ModelMap、Map……
      • 原生 Servlet API:request、response、session……
      • BindingResult。
      • @RequestParam 注解标记的零散请求参数。
      • @PathVariable 注解标记的路径变量。
    • 调用目标 handler 方法。
  • 所以 HandlerAdapter 这个适配器是将底层的 HTTP 报文、原生的 request 对象进行解析和封装,『适配』到我们定义的 handler 方法上。

2.3.2 创建并获取这个组件

  • 所在类:org.springframework.web.servlet.DispatcherServlet 。
  • 所在方法:doDispatch() 。

创建并获取 HandlerAdapter.png

2.3.3 具体操作:调用目标 handler 方法

  • 所在类:org.springframework.web.servlet.DispatcherServlet 。
  • 所在方法:doDispatch() 。

调用目标 handler 方法.png

2.3.4 具体操作:注入请求参数

  • 通过反射给对应属性注入请求参数应该是下面的过程:
  • ① 获取请求参数名称。
  • ② 将请求参数名称首字母设定为大写。
  • ③ 在首字母大写后的名称前附加 set,得到目标方法名。
  • ④ 通过反射调用 setXxx() 方法。

2.4 准备其它参数

2.4.1 概述

  • 以 Model 为例进行说明。

2.4.2 背景

  • 在 handler 方法中,如果需要 Model、ModelMap、Map 等对象用来存放模型数据,那么直接在 handler 方法中声明这些类型的形参即可。
  • 而不管我们声明 Model、ModelMap、Map 三者中的任何一个,其实实际传入的对象都是 BindingAwareModelMap 类型的。

2.4.3 相关组件

  • 组件类:org.springframework.web.method.support.ModelAndViewContainer 。
  • 相关属性:defaultModel 。
  1. private final ModelMap defaultModel = new BindingAwareModelMap();
  • 从这个属性的声明能够看出:defaultModel 直接就是用 BindingAwareModelMap 对象来初始化的。

2.4.4 相关操作

  • 相关接口:org.springframework.web.servlet.HandlerAdapter。
  • 所在类:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter 。
  • 所在方法:invokeHandlerMethod()。

  • 操作1:创建 ModelAndViewContainer 对象

创建ModelAndViewContainer对象.png

  • 操作2:把 ModelAndViewContainer 对象传给 invokeAndHandle() 方法

把 ModelAndViewContainer 对象传给 invokeAndHandle() 方法.png

第三章:调用后阶段

3.1 调用拦截器的 postHandle() 方法

  • 所在类:org.springframework.web.servlet.DispatcherServlet
  • 所在方法:doDispatch()

调用拦截器的 postHandle() 方法.png

  • 调用细节:从拦截器集合长度 - 1 开始循环,循环到 0 为止。所以是倒序执行。

调用拦截器的 postHandle() 方法的细节.png

3.2 渲染视图

3.2.1 所有后续操作的入口

  • 所在类:org.springframework.web.servlet.DispatcherServlet 。
  • 所在方法:doDispatch() 。

渲染视图所有后续操作的入口.png

3.2.2 后续细节:处理异常

  • 所在类:org.springframework.web.servlet.DispatcherServlet 。
  • 所在方法:processDispatchResult() 。

所有后续操作的入口之处理异常.png

3.2.3 后续细节:渲染视图

  • 所在类:org.springframework.web.servlet.DispatcherServlet 。
  • 所在方法:processDispatchResult() 。

所有后续操作的入口之渲染视图1.png

  • 补充细节:模型数据存入请求域的具体位置 。
  • 所在类:org.thymeleaf.context.WebEngineContext.RequestAttributesVariablesMap 。
  • 所在方法:setVariable() 。

所有后续操作的入口之渲染视图2.png

3.3 调用拦截器的 afterCompletion() 方法

  • 所在类:org.springframework.web.servlet.DispatcherServlet 。
  • 所在方法:processDispatchResult()。

调用拦截器的 afterCompletion() 方法.png

  • 调用细节:从拦截器索引开始循环,直到循环变量 i 被减到 0 为止。这样的效果是前面执行拦截器到哪里,就从哪里倒回去执行;前面没有执行的拦截器,现在也不执行。

调用拦截器的 afterCompletion() 方法的细节.png