DispatcherServlet源码分析
SpringMVC核心的就是DispatcherServlet,所有的请求都会转发到DispatcherServlet,让后在通过DispatcherServlet执行具体的控制层(Handler) 返回modelAndView给客户端视图展示。
DispatcherServlet与Servlet关系
关系:DispatcherServlet继承FrameworkServlet继承HttpServlet
流程执行关系:
HttpServlet service方法 判断请求方法的类型
FrameworkServlet doService
DispatcherServlet doService
DispatcherServlet源码流程分析
- 1.执行doDispatch
- 2.调用getHandler方法获取请求目标的方法 也就是 请求url映射路径对应的控制层具体的方法
- handlerMappings的作用查找控制器位置,比如xml和注解方式。
- 3.调用getHandlerAdapter获取控制层适配器 RequestMappingHandlerAdapter
- 4.执行拦截器前置方法 preHandle() 如果返回为true的话
- 5.执行实际请求目标方法 返回modeAndView对象
- 6.执行拦截器PostHandle()方法
- 7.设置渲染视图层内容
- 8.执行拦截器afterCompletion方法
现在先从最开始的web.xml文件进行分析,我们首先配置了contextConfigLocation如下所示:
继承关系如下图所示:
上图中的ServletContextListener该类位于servlet-api.jar包中(再tomcat的自带lib中可以找到)
ContextLoaderListener实现了ServletContextListener接口,该接口保证了在能够为客户端提供服务之前向ServletContext中添加任何对象。每个Web应用都有一个ServletContext与之相关联,启动时被创建,关闭时被销毁,并且ServletContext在全局范围内有效。ServletContextListener的核心逻辑就是初始化WebApplicationContext实例并放入ServletContext中。
我们之所以使用了ContextLoaderListener是为了避免下面这种硬编码配置:
ApplicationContext ac = new ClassPathXmlApplecation("applicationContext.xml")
再来说说ContextLoaderListener的实际步骤是:
- webApplication存在性验证(配置中只能有一个ServletContextListener接口,验证的方法就是检查该属性是否存在)
- 创建webApplication实例
- 将属性注入(例如contextConfigLocation)webApplication
- 将实例记录到servletContext中
- 映射当前的类加载器与创建的实例到全局变量currentContextPerThread中
讲完了ContextLoaderListener再来讲讲web.xml中另一个重要配置DispatcherServlet,对于DispatcherServlet的配置如下所示:
接下来我们先给出DispatcherServlet的类层次结构,如下图所示:
注册url与HandlerMethod映射关系链
Spring容器启动时, 在org.springframework.web.servlet.handler.AbstractHandlerMethodMapping类
遍历IOC容器中所有bean
判断是否有@Controller和@RequestMapping注解
DispatcherServlet的初始化过程
调用链关系如下图:
- HttpServletBean init ()方法
- FrameworkServlet initServletBean方法→ initWebApplicationContext();
- DispatcherServlet onRefresh方法→ initStrategies()方法
容器启动时, 在HttpServletBean类里面的init方法里面进行初始化
最终执行DispatcherServlet的initStrategies方法进行组件的初始化
初始化上传文件解析器(或者是多部分请求解析器)
初始化本地化解析器
初始化主题解析器
初始化处理器映射器
初始化处理器适配器
初始化处理器异常解析器
初始化请求到视图名翻译器
初始化视图解析器
初始化重定向数据管理器
DispatcherServlet的处理逻辑
虽然HTTP1.1报文头一共有八种,常用的:GET、POST、PUT、DELETE,不太常用的:HEAD、TRACE、CONNECT、OPTIONS,但是实际上FrameworkServlet并没有实现CONNECT和HEAD。他们分别对应着doXXX()方法。
对于不同的方法,Spring统一的将他们引入到processRequest方法中,该方法完成了以下操作:
- 提取当前线程的LocaleContext和RequestAttribute以便在当前请求后还能够恢复
- 根据当前request创建LocaleContext和RequestAttribute,并绑定到当前线程
- 委托doService方法执行doDispatch(request, response)方法
仍然是准备工作,将webApplicationContext,localeResolver等属性注入request中,然后继续由doDispatch方法进行处理,
doDispatch方法的处理逻辑为:
第一步:将MultipartContent类的request转化为MultipartHttpServletRequest类型的request
第二步:首先获取到HandlerExecutionChain,HandlerExecutionChain结构很简单,包括一个我们具体处理请求的Controller层方法的对象handler,以及拦截器interceptors、interceptorList。如果没有获取到相应的HandlerExecutionChain,则会返回404。
第三步:若如果没有找到对应的Handler则通过response向用户报错
第四步:根据当前的Handler寻找对应的HandlerAdapter(遍历寻找)
第五步:缓存处理,根据Last-Modified缓存机制(第一次成功请求后如果在第二次请求之间内容没有改变,那么返回304状态码),Controller需要实现LastModified接口
第六步:顺序调用拦截器的preHandle()方法,如果不符合拦截器的拦截规则则会同时调用afterCompletion()方法
第七步:逻辑处理,通过适配器中转调用Handler(处理用户定义的逻辑)并返回视图
第八步:调用拦截器的postHandle()
第九步:调用processDispatchResult()对请求结果进行处理。如果业务处理过程中有异常抛出,调用processHandlerException()对异常进行处理,这里会调用我们自定义的HandlerExceptionResolvers进行处理,然后返回一个异常处理结果的ModelAndView对象。
第十步:执行拦截器afterCompletion方法