前言
对于 Web 应用程序而言,我们从浏览器发起一个请求,请求经过一系列的分发和处理,最终会进入到我们指定的方法之中,这一系列的的具体流程到底是怎么样的呢?
Spring MVC 请求流程
记得在初入职场的时候,面试前经常会背一背 Spring MVC 流程,印象最深的就是一个请求最先会经过 DispatcherServlet 进行分发处理,DispatcherServlet 就是我们 Spring MVC 的入口类,下面就是一个请求的大致流转流程(图片参考自 Spring In Action):Spring MVC 请求流程
记得在初入职场的时候,面试前经常会背一背 Spring MVC 流程,印象最深的就是一个请求最先会经过 DispatcherServlet 进行分发处理,DispatcherServlet 就是我们 Spring MVC 的入口类,下面就是一个请求的大致流转流程(图片参考自 Spring In Action):

一个请求过来之后会到达 DispatcherServlet,但是 DispatcherServlet 也并不知道这个请求要去哪里。
DispatcherServlet 收到请求之后会去查询处理器映射(HandlerMapping),从而根据浏览器发送过来的 URL 解析出请求最终应该调用哪个控制器。
到达对应控制器(Controller)之后,会完成一些逻辑处理,而且在处理完成之后会生成一些返回信息,也就是 Model,然后还需要选择对应的视图名。
将模型(Model)和视图(View)传递给对应的视图解析器(View Resolver),视图解析器会将模型和视图进行结合。
模型和视图结合之后就会得到一个完整的视图,最终将视图返回前端。
上面就是一个传统的完整的 Spring MVC 流程,为什么要说这是传统的流程呢?因为这个流程是用于前后端没有分离的时候,后台直接返回页面给浏览器进行渲染,而现在大部分应用都是前后端分离,后台直接生成一个 Json 字符串就直接返回前端,不需要经过视图解析器进行处理,也就是说前后端分离之后,流程就简化成了 1-2-3-4-7(其中第四步返回的一般是 Json 格式数据)。
Spring MVC 两大阶段
Spring MVC主要可以分为两大过程,一是初始化,二就是处理请求。初始化的过程主要就是将我们定义好的 RequestMapping 映射路径和 Controller 中的方法进行一一映射存储,这样当收到请求之后就可以处理请求调用对应的方法,从而响应请求。移步主页获取联系方式 领取免费资料
初始化
初始化过程的入口方法是 DispatchServlet 的 init() 方法,而实际上 DispatchServlet 中并没有这个方法,所以我们就继续寻找父类,会发现 init 方法在其父类(FrameworkServlet)的父类 HttpServletBean 中。
HttpServletBean#init()
在这个方法中,首先会去家在一些 Servlet 相关配置(web.xml),然后会调用 initServletBean() 方法,这个方法是一个空的模板方法,业务逻辑由子类 FrameworkServlet 来实现。

FrameworkServlet#initServletBean
这个方法本身没有什么业务逻辑,主要是初始化 WebApplicationContext 对象,WebApplicationContext 继承自 ApplicationContext,主要是用来处理 web 应用的上下文。

FrameworkServlet#initWebApplicationContext
initWebApplicationContext() 方法主要就是为了找到一个上下文,找不到就会创建一个上下文,创建之后,最终会调用方法
configureAndRefreshWebApplicationContext(cwac) 方法,而这个方法最终在设置一些基本容器标识信息之后会去调用 refresh()方法,也就是初始化 ioc 容器。

当调用 refresh() 方法初始化 ioc 容器之后,最终会调用方法 onRefresh(),这个方法也是一个模板钩子方法,由子类实现,也就是回到了我们 Spring MVC 的入口类 DispatcherServlet。
getHandlerInternal 方法最终其会调用子类实现,而这里的子类实现会有多个,其中最主要的就是
AbstractHandlerMethodMapping 和 AbstractUrlHandlerMapping 两个抽象类,那么最终到底会调用哪个实现类呢?
这时候如果拿捏不准我们就可以看一下类图,有两个非常主要的实现类:
RequestMappingHandlerMapping 和 BeanNameUrlHandlerMapping。那么我们就分别来看一下这两个类的类图关系:


可以看到,这两个实现类的抽象父类正好对应了 AbstractHandlerMapping 的两个子类,所以这时候具体看哪个方法,那就看我们想看哪种类型了。
RequestMappingHandlerMapping:主要用来存储 RequestMapping 注解相关的控制器和 url 的映射关系。
BeanNameUrlHandlerMapping:主要用来处理 Bean name 直接以 / 开头的控制器和 url 的映射关系。
其实除了这两种 HandlerMapping 之外,Spring 中还有其他一些 HandllerMapping,如 SimpleUrlHandlerMapping 等。
提到的这几种 HandlerMapping,对我们来说最常用,最熟悉的那肯定就是
RequestMappingHandlerMapping ,在这里我们就以这个为例来进行分析,所以我们应该
AbstractHandlerMethodMapping#getHandlerInternal
这个方法本身也没有什么逻辑,其主要的核心查找 Handler 逻辑在 lookupHandlerMethod 方法中,这个方法主要是为了获取一个 HandlerMethod 对象,前面的方法都是 Object,而到这里变成了 HandlerMethod 类型,这是因为 Handler 有各种类型,目前我们已经基本跟到了具体类型之下,所以类型就变成了具体类型,而如果我们看的的另一条分支线,那么返回的就会是其他对象,正是因为支持多种不同类型的 HandlerMapping 对象,所以最终为了统一执行,才会需要在获得 Hanlder 之后,DispatcherServlet 中会再次通过调用 getHandlerAdapter 方法来进一步封装成 HandlerAdapter 对象,才能进行方法的调用
本文重点以
RequestMappingHandlerMapping 为例子分析了在 Spring 当中如何初始化 HandlerMethod,并最终在调用的时候又是如何根据 url 获取到对应的方法并进行执行最终完成整个流程。
最后
Spring MVC的资料





移步主页获取联系方式 领取免费资料