一.介绍
DispatcherServlet(前端控制器)是整个MVC框架中最为核心的一块,它主要用来拦截符合要求的外部请求,并把请求分发到不同的控制器去处理,根据控制器处理后的结果,生成相应的响应发送到客户端。前端控制器既可以使用Filter实现(Struts2采用这种方式),也可以使用Servlet来实现(spring MVC框架)。同时它与Spring IOC容器无缝集成, 从而可以获得Spring的所有好处。如图
二.DispatchServlet处理流程
- Tomcat 启动,对 DispatcherServlet 进行实例化,然后调用它的 init() 方法进行初始化,在这个初始化过程中完成了对 web.xml 中初始化参数的加载;建立 WebApplicationContext (SpringMVC的IOC容器);进行组件的初始化;
- 客户端发出请求,由 Tomcat 接收到这个请求,如果匹配 DispatcherServlet 在 web.xml 中配置的映射路径,Tomcat 就将请求转交给 DispatcherServlet 处理;
- DispatcherServlet 从容器中取出所有 HandlerMapping 实例(每个实例对应一个 HandlerMapping 接口的实现类)并遍历,每个 HandlerMapping 会根据请求信息,通过自己实现类中的方式去找到处理该请求的 Handler (执行程序,如Controller中的方法),并且将这个 Handler 与一堆 HandlerInterceptor (拦截器) 封装成一个 HandlerExecutionChain 对象,一旦有一个 HandlerMapping 可以找到 Handler 则退出循环
- DispatcherServlet 取出 HandlerAdapter 组件,根据已经找到的 Handler,再从所有 HandlerAdapter 中找到可以处理该 Handler 的 HandlerAdapter 对象;
- 执行 HandlerExecutionChain 中所有拦截器的 preHandler() 方法,然后再利用 HandlerAdapter 执行 Handler ,执行完成得到 ModelAndView,再依次调用拦截器的 postHandler() 方法;
- 利用 ViewResolver 将 ModelAndView 或是 Exception(可解析成 ModelAndView)解析成 View,然后 View 会调用 render() 方法再根据 ModelAndView 中的数据渲染出页面;
- 最后再依次调用拦截器的 afterCompletion() 方法,这一次请求就结束了。
三.DispatchServlet结构类图
四.DispatchServlet源码分析
Servlet生命周期分为三个阶段:
1,初始化阶段 调用init()方法,
2,响应客户请求阶段 调用service()方法
3,终止阶段 调用destroy()方法
1.初始化init
DispatcherServlet 的 init() 方法在 HttpServletBean() 中实现,代码为:
public final void init() throws ServletException {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Initializing servlet '" + this.getServletName() + "'");
}
//解析DispatcherServlet 配置的参数,封装成 ServletConfigPropertyValues对象
PropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
//将当前Servlet 类型的实例转换成BeanWrapper类型的实例,以便使用Spring中提供的注入功能进行对应的属性注入
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment()));
this.initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
} catch (BeansException var4) {
if (this.logger.isErrorEnabled()) {
this.logger.error("Failed to set bean properties on servlet '" + this.getServletName() + "'", var4);
}
throw var4;
}
}
//调用 initServletBean() 初始化servletBean
this.initServletBean();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Servlet '" + this.getServletName() + "' configured successfully");
}
}
2.Servlet初始化
在初始化servletBean的时候,期间会调用 onRefresh() 方法,该方法主要是刷新Spring在web功能实现中所必须使用的全局变量。
protected void onRefresh(ApplicationContext context) {
this.initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
this.initMultipartResolver(context);
this.initLocaleResolver(context);
this.initThemeResolver(context);
this.initHandlerMappings(context);
this.initHandlerAdapters(context);
this.initHandlerExceptionResolvers(context);
this.initRequestToViewNameTranslator(context);
this.initViewResolvers(context);
this.initFlashMapManager(context);
}
1.initMultipartResolver 文件上传解析
MultipartResolver 用于处理文件上传,当收到请求时 DispatcherServlet 的 checkMultipart() 方法会调用 MultipartResolver 的 isMultipart() 方法判断请求中是否包含文件。如果请求数据中包含文件,则调用 MultipartResolver 的 resolveMultipart() 方法对请求的数据进行解析,然后将文件数据解析成 MultipartFile 并封装在 MultipartHttpServletRequest (继承了 HttpServletRequest) 对象中,最后传递给 Controller。
2.initLocaleResolver 本地化解析器
在spring容器启动时,会在上下文中寻找LocaleResolver本地化解析器,spring提供了3个本地化解析器和1个本地化拦截器:AcceptHeaderLocaleResolver、CookieLocaleResolver、SessionLocaleResolver、LocaleContextResolver、LocaleChangeInterceptor
3.initThemeResolver 主题解析
4.initHandlerMappings 将请求映射到处理器
5.initHandlerAdapters 处理适配器(支持多种类型的处理器)
HandlerAdapter字面上的意思就是处理适配器,它的作用用一句话概括就是调用具体的方法对用户发来的请求来进行处理。当handlerMapping获取到执行请求的controller时,DispatcherServlte会根据controller对应的controller类型来调用相应的HandlerAdapter来进行处理。
6.initHandlerExceptionResolvers 异常解析
所有用于处理在请求处理过程中抛出的异常,都要实现HandlerExceptionResolver接口。HandlerExceptionResolver是Spring MVC提供的非常好的通用异常处理工具,不过需要注意的是,它只能处理请求过程中抛出的异常,异常处理本身所抛出的异常和视图解析过程中抛出的异常它是不能处理的。
7.initRequestToViewNameTranslator 解析请求到视图名
8.initViewResolvers 解析逻辑视图名到具体视图实现
9.initFlashMapManager flash映射管理器
3.DispatchServlet的请求处理逻辑
service() 方法是执行实际任务的主要方法。Servlet 容器(即 Web 服务器)调用 service() 方法来处理来自客户端(浏览器)的请求,并把格式化的响应写回给客户端。
每次服务器接收到一个 Servlet 请求时,服务器会产生一个新的线程并调用服务。service() 方法检查 HTTP 请求类型(GET、POST、PUT、DELETE 等),并在适当的时候调用 doGet、doPost、doPut,doDelete 等方法。
servlet 接受到请求后,会调用service() 方法,然后service根据HTTP响应的请求方法进行调用响应的逻辑处理。
我们知道servlet处理方法都是通过HttpServlet的service方法开始,FrameworkServlet重写了父类HttpServlet的service方法。代码如下:
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
//判断是不是patch请求
if (HttpMethod.PATCH != httpMethod && httpMethod != null) {
//不是:调用父类(HttpServlet)的service方法
super.service(request, response);
} else {
//是:就交由本类(FrameworkServlet)内的doPatch方法运行
//processRequest()->doService()->doDispatch()
//通过跟踪代码,最终处理请求的核心代码在doDispatch()方法中。
this.processRequest(request, response);
}
}
4.doDispatch() 方法
doDispatch方法主要用作职责调度工作,本身主要用于控制流程,主要职责如下:
1、文件上传解析,如果请求类型是multipart将通过MultipartResolver进行文件上传解析;
2、通过HandlerMapping,将请求映射到处理器(返回一个HandlerExecutionChain,它包括一个处理器、多个HandlerInterceptor拦截器);
3、通过HandlerAdapter支持多种类型的处理器(HandlerExecutionChain中的处理器);
4、调用HandlerExecutionChain的interceptor和handler
5、解析视图、处理异常,渲染具体的视图等;
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
try {
ModelAndView mv = null;
Object dispatchException = null;
try {
//检查该请求是否是文件上传请求,如果是则把request转换为 MultipartHttpServletRequest类型
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;
//根据request信息查找对应的Handler,如果没有找到对应的Handler,则通过response反馈错误信息
mappedHandler = this.getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
this.noHandlerFound(processedRequest, response);
return;
}
//通过当前的handler 查找对应的HandlerAdapter
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
boolean isGet = "GET".equals(method);
//判断该HTTP请求是不是HEAD或GET请求
if (isGet || "HEAD".equals(method)) {
//如果是则检查HTTP请求头部的LastModified属性
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (this.logger.isDebugEnabled()) {
this.logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
//它用来判断页面是否发生过更改,并会设置相应的响应头
if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
return;
}
}
//applyPreHandle() 执行该请求所匹配的拦截器,并调用所有匹配拦截器的preHandle()方法,进行处理
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//调用handle() 方法去根据请求路径去调用用户实现的Controller实现逻辑
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
this.applyDefaultViewName(processedRequest, mv);
//applyPostHandle()执行该请求所有匹配的拦截器,并调用所有匹配拦截器的postHandle()方法,进行处理
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception var20) {
dispatchException = var20;
} catch (Throwable var21) {
dispatchException = new NestedServletException("Handler dispatch failed", var21);
}
//解析视图、处理异常
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
} catch (Exception var22) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
} catch (Throwable var23) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
}
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else if (multipartRequestParsed) {
//清理多部分请求使用的资源。
this.cleanupMultipart(processedRequest);
}
}
}
5.processDispatchResult() 方法
该方法负责处理请求结果,并渲染视图
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
//处理异常信息
if (exception instanceof ModelAndViewDefiningException) {
this.logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException)exception).getModelAndView();
} else {
Object handler = mappedHandler != null ? mappedHandler.getHandler() : null;
mv = this.processHandlerException(request, response, handler, exception);
errorView = mv != null;
}
}
if (mv != null && !mv.wasCleared()) {
//如果Controller成功返回了ModelAndView,则通过render()方法进行响应相应的页面信息
this.render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
} else if (this.logger.isDebugEnabled()) {
this.logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + this.getServletName() + "': assuming HandlerAdapter completed request handling");
}
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
//执行完毕后,执行该请求所有匹配的拦截器,并调用所有匹配拦截器的afterCompletion()方法,进行处理
mappedHandler.triggerAfterCompletion(request, response, (Exception)null);
}
}
}