解读SpringMVC源码

自从Struts2彻底退出江湖之后,Spring MVC已经成为了表现层框架的扛把子。今天,我们就来窥探一下它的世界。

本文围绕Spring MVC的前端控制器DispatcherServlet展开,先介绍了它的类继承关系和初始化方法,然后介绍Spring MVC的九大组件和运行流程,最后带领大家分析从请求到响应的相关处理环节代码。

一、Spring MVC框架的接入点

最开始使用Spring MVC的时候,我们都在Java Web项目的web.xml文件写过这个东西(使用Spring Boot或者注解的请自动忽略)。

当请求的路径匹配到url-pattern时,就交由名为servlet-name的Servlet处理。这个时候,Spring MVC的前端控制器DispatcherServlet开始接手了。

二、 DispatcherServlet的继承关系

顶层接口Servlet提供了5个接口:init、getServletConfig、service、getServletInfo、destroy。

我们主要讲下init方法和service方法。但是由于service方法是核心,放到后面去重点讲,所以就简单说一下init方法。

官方解释:在实例化servlet之后,由servlet容器仅调用一次,以向servlet指示正在投入使用,在servlet接收到任何请求之前完成。

Spring MVC的DispatcherServlet在这个阶段要完成的事情有哪些呢?

由于GenericServlet的init什么都没做,由子类重写,所以初始化的工作由HttpServletBean-->FrameworkServlet-->DispatcherServlet逐级完成。

1、首先是爷爷类HttpServletBean:将配置参数映射到此servlet的bean属性上,以及调用子类的初始化(就是initServletBean方法)。

2、然后是DispatcherServlet的父类FrameworkServlet:在bean的任何属性被设置之后(HttpServletBean完成),调用重写的方法initServletBean,创建此servlet的WebApplicationContext。

initWebApplicationContext()方法代码就不贴了。值得注意的是,它会执行一个默认实现为空,由子类重写的方法onRefresh。

3、最后就是DispatcherServlet了:重写onRefresh方法,初始化Spring MVC传说中的九大组件。

三、Spring MVC的九大组件

来简单介绍一下九大组件的作用

  1. MultipartResolver:用于处理上传请求。处理方法是将普通的request包装成MultipartHttpServletRequest,后者可以直接调用getFile方法获取File。
  2. LocaleResolver:SpringMVC主要有两个地方用到了Locale:一是ViewResolver视图解析的时候;二是用到国际化资源或者主题的时候。
  3. ThemeResolver:用于解析主题。SpringMVC中一个主题对应一个properties文件,里面存放着跟当前主题相关的所有资源,如图片、css样式等。SpringMVC的主题也支持国际化。
  4. HandlerMapping:用来查找Handler的。(可以多个)
  5. HandlerAdapter:从名字上看,它就是一个适配器。Servlet需要的处理方法的结构却是固定的,都是以request和response为参数的方法。如何让固定的Servlet处理方法调用灵活的Handler来进行处理呢?这就是HandlerAdapter要做的事情。(可以多个)
  6. HandlerExceptionResolver:其它组件都是用来干活的。在干活的过程中难免会出现问题,出问题后怎么办呢?这就需要有一个专门的角色对异常情况进行处理,在SpringMVC中就是HandlerExceptionResolver。(可以多个)
  7. RequestToViewNameTranslator:有的Handler处理完后并没有设置View也没有设置ViewName,这时就需要从request获取ViewName了,如何从request中获取ViewName就是RequestToViewNameTranslator要做的事情了。
  8. ViewResolver:ViewResolver用来将String类型的视图名和Locale解析为View类型的视图。View是用来渲染页面的,也就是将程序返回的参数填入模板里,生成html(也可能是其它类型)文件。(可以多个)
  9. FlashMapManager:用来管理FlashMap的,FlashMap主要用在redirect重定向中传递参数。

四、Spring MVC的运行流程

看着上面的九大组件是不是一脸懵逼?没关系,图文并茂带你消化,重点来了

图片很精彩,但是鲜花也要绿叶相伴。针对图中的每一步骤做下解释

  1. 用户发送请求Request至前端控制器DispatcherServlet。
  2. DispatcherServlet遍历它的所有处理器映射器HandlerMapping,每一个HandlerMapping根据请求url找到具体的处理器Handler。如果找到了,将Handler对象封装成处理器执行链HandlerExecutionChain,并添加处理器拦截器HandlerInterceptor(如果有),然后将其返回给DispatcherServlet。
  3. DispatcherServlet根据HandlerExecutionChain的Handler找到合适的处理器适配器HandlerAdapter。
  4. DispatcherServlet通过HandlerAdapter去调用真正的处理器(后端控制器)Controller。
  5. Controller执行完成返回视图模型ModelAndView给HandlerAdapter,HandlerAdapter将其返回给DispatcherServlet。
  6. DispatcherServlet将ModelAndView传给视图解析器ViewReslover,ViewReslover解析后返回具体视图View。
  7. DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)。
  8. DispatcherServlet响应Response给用户。

从上面可以看出,DispatcherServlet充当一个全局控制器的角色,有接收请求,响应结果,转发等作用。有了DispatcherServlet之后,可以减少组件之间的耦合度

五、看源码看源码看源码

这里,我们使用一个get请求(对应处理器使用Controller和RequestMapping注解)加一个自定义处理器拦截器来示范。

1、请求怎么进入核心方法doDispatch的?

一个请求来了,毫无疑问先调用的是Servlet的service方法,官方对其解释:Called by the servlet container to allow the servlet to respond to a request. 翻译就是:由servlet容器调用,以允许servlet响应一个请求。

但是在DispatcherServlet没有找到service方法哎,怎么办?没关系,它老子FrameworkServlet有,看吧

除了PATCH方式的请求外,其它委托父类处理,但是HttpServletBean没有重写该方法,所以就到了HttpServlet处理。但是,这玩意没做什么处理,官方对这个方法的解释:接收来自public的service方法的标准HTTP请求,并将其分派给定义在这个类里面的指定请求方式的方法处理;这个service方法是HTTP协议特定版本的实现,没有必要重写。屁,PATCH方式你们不就不支持嘛。

所以,HttpServlet的service方法的作用就是:针对不同的http请求方式,将其交给对应的方法处理。至于对应的方法,由子类重写。

这下,回到了FrameworkServlet的doGet方法,它又委派给processRequest方法处理。仔细瞄了一下,其它的doPost、doPut、doDelete等方法都是如此。

然后processRequest又做了一些事情(大概是记录一下和处理requestAttributes什么的),就交给doService方法了。

这下,终于回到了我们收悉的DispatcherServlet类了,不容易啊。我们只关注里面的调用doDispatch方法,其它的看注释好了。

进入doDispatch方法,才算真正来到了我们Spring MVC运行流程图的第1步。先把这个方法的核心源码贴上,压一下经

2、判断是否为文件上传的请求

首先是checkMultipart方法,这个方法是检查是否是二进制的请求(文件上传的请求)。是的话转为multipart request,否则返回原request。我们这里不是,所以multipartRequestParsed是false。

3、高潮1:遍历处理器映射器查找匹配的处理器

获取处理器,但是返回值为什么是处理器执行链HandlerExecutionChain?因为除了要执行处理器(Controller)之外,可能还有若干拦截器Interceptor。

怎么查找处理器呢,先遍历注册的HandlerMappings(我使用的是Spring Boot默认的,一共有第二张图里面的7个)。然后按顺序每一个HandlerMapping去根据请求找合适的处理器Handler,谁先找到合适的之后,就继续找符合的拦截器Interceptor,然后转成一个HandlerExecutionChain对象返回。后续的HandlerMapping也不再找了。

 

因为我的处理器是用Controller和RequestMapping注解的,所以最终被RequestMappingHandlerMapping匹配到了。下面贴一下它的查找过程,不做解释。

 

4、高潮2:根据处理器获取处理器适配器

跟获取Handler一样,遍历处理器适配器,看哪个先支持处理器就返回谁。我的Controller是被RequestMappingHandlerAdapter匹配到了。

 

 

 

5、执行所有拦截器的preHandle方法

只要有一个返回false,就停止往下执行。

 

这个是我自定义的处理器拦截器 

 

6、高潮3:调用处理器

invokeHandlerMethod方法主要是用到了反射,代码就不贴了。

 

7、设置默认视图名称和执行拦截器的postHandle 方法

applyDefaultViewName:如果mv为空,就设置默认viewName(如果有的话)。

applyPostHandle:执行所有拦截器的后处理回调方法postHandle。

 

8、处理结果

渲染结果和调用全部拦截器的整个请求处理完毕回调方法afterCompletion。

 

 

9、其它处理

异常时调用全部拦截器的整个请求处理完毕回调方法afterCompletion。

做一些资源清理工作。

到此为止,Spring MVC的运行流程算是过了一遍了。可能有些地方没有解释得很深入和很明白,大家可以自行去研究下对应源码。

后续打算抄袭一下,写一篇“自己动手写Spring MVC框架”。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章