Spring 源码揭秘之Spring MVC,小白都能看懂

前言

对于 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的资料

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

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容