在整个 Spring MVC 框架中,DispatcherServlet 处于核心位置,它负责协调和组织不同组件完成请求处理并返回响应工作。DispatcherServlet 是 SpringMVC统一的入口,所有的请求都通过它。
DispatcherServlet 是前端控制器,配置在web.xml文件中,Servlet依自已定义的具体规则拦截匹配的请求,分发到目标Controller来处理。 初始化 DispatcherServlet时,该框架在web应用程序WEB-INF目录
中寻找一个名为[servlet-名称]-servlet.xml
的文件,并在那里定义相关的Beans,重写在全局中定义的任何Beans。
可以通过contextConfigLocation参数修改加载的]
[servlet-名称]-servlet.xml
的路径
配置示例:
<servlet>
<servlet-name>dispatcherservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>/WEB-INF/spring/spring-servlet.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
在看DispatcherServlet 类之前,我们先来看一下请求处理的大致流程:
Tomcat 启动,对 DispatcherServlet 进行实例化,然后调用它的初始化方法进行初始化,在这个初始化过程中完成了:对 web.xml 中初始化参数的加载;建立 WebApplicationContext(SpringMVC的IOC容器);进行组件的初始化;
初始化方法并不是单指initStrategies(),因为DispatcherServlet有好多父类,初始化的源头在父类中定义的,initStrategies()只是初始化过程的一部分
客户端发出请求,由 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() 方法,这一次请求就结束了。
由上图得知,DispatcherServlet本质上也是一个Servlet,那么也会有初始化和响应请求的方法。
DispatcherServlet 继承自 HttpServlet,它遵循 Servlet 里的“init-service-destroy”三个阶段,首先我们先来看一下它的 init() 阶段。
DispatcherServlet 的 init()
方法在其父类 HttpServletBean
中实现的,它覆盖了 GenericServlet 的 init() 方法,主要作用是加载 web.xml 中 DispatcherServlet 的 配置,并调用子类的初始化。
下面是 init() 方法的具体代码:
@Override
public final void init() throws ServletException {
try {
// ServletConfigPropertyValues 是静态内部类,使用 ServletConfig 获取 web.xml 中配置的参数
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
// 使用 BeanWrapper 来构造 DispatcherServlet
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
} catch (BeansException ex) {}
// [2]让子类实现的方法,这种在父类定义在子类实现的方式叫做模版方法模式
initServletBean();
}
[2]处的代码,让子类实现的方法initServletBean();
在 HttpServletBean 的 init() 方法中调用了 initServletBean() 这个方法,它是在 FrameworkServlet 类中实现的,主要作用是建立 WebApplicationContext 容器(有时也称上下文),并加载 SpringMVC 配置文件中定义的 Bean 到改容器中,最后将该容器添加到 ServletContext 中。
FrameworkServlet 初始化WebApplicationContext 容器时,会把web IOC容器(),即根容器作为自己的父容器,也叫父上下文
下面是 initServletBean() 方法的具体代码:
@Override
protected final void initServletBean() throws ServletException {
try {
// 初始化 WebApplicationContext (即SpringMVC的IOC容器)
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
} catch (ServletException ex) {
} catch (RuntimeException ex) {
}
}
WebApplicationContext 继承于 ApplicationContext 接口,从容器中可以获取当前应用程序环境信息,它也是 SpringMVC 的 IOC 容器。
下面是 initWebApplicationContext() 方法的具体代码:
protected WebApplicationContext initWebApplicationContext() {
// 获取 ContextLoaderListener 初始化并注册在 ServletContext 中的根容器,即 Spring 的容器
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
// 因为 WebApplicationContext 不为空,说明该类在构造时已经将其注入
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
// 将 Spring 的容器设为 SpringMVC 容器的父容器
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
// 如果 WebApplicationContext 为空,则进行查找,能找到说明上下文已经在别处初始化。
wac = findWebApplicationContext();
}
if (wac == null) {
// 如果 WebApplicationContext 仍为空,则以 Spring 的容器为父上下文建立一个新的。
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
//[2] 模版方法,由 DispatcherServlet 实现
onRefresh(wac);
}
if (this.publishContext) {
// 发布这个 WebApplicationContext 容器到 ServletContext 中
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}
[2] 模版方法,由 DispatcherServlet 实现onRefresh()
建立好 WebApplicationContext(上下文) 后,通过 onRefresh(ApplicationContext context) 方法回调,进入 DispatcherServlet 类中。onRefresh() 方法,提供 SpringMVC 的初始化,具体代码如下:
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
initStrategies()方法完成SpringMVC 的初始化。
具体初始化了什么,可以在其initStrategies()方法中知晓,这个方法如下:
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context); //'重要'
initHandlerAdapters(context); //'重要'
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
需要做的八件事情如下所述:
initHandlerMappings() 方法从 SpringMVC 的容器及 Spring 的容器中查找所有的 HandlerMapping 实例,并把它们放入到 handlerMappings 这个 list 中。
这个方法并不是对 HandlerMapping 实例的创建,这里只是查找并放到一个集合中。HandlerMapping 实例化是在之前步骤 WebApplicationContext 容器初始化中,即 SpringMVC 容器初始化的时候创建的。
如果不在 springmvc.xml 文件中配置,就会使用默认的。参见《SpringMVC 默认配置 DispatcherServlet.properties 文件》
这个方法的逻辑和initHandlerMappings 方法差不多,这里就不在单独列出了。
HttpServlet 提供了 doGet()、doPost() 等方法,DispatcherServlet 中这些方法是在其父类 FrameworkServlet 中实现的,代码如下:
FrameworkServlet .doGet()
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
这些方法又都调用了 processRequest()
方法,我们来看一下代码:
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
// 返回与当前线程相关联的 LocaleContext
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
// 根据请求构建 LocaleContext,公开请求的语言环境为当前语言环境
LocaleContext localeContext = buildLocaleContext(request);
// 返回当前绑定到线程的 RequestAttributes
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
// 根据请求构建ServletRequestAttributes
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
// 获取当前请求的 WebAsyncManager,如果没有找到则创建
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
// 使 LocaleContext 和 requestAttributes 关联
initContextHolders(request, localeContext, requestAttributes);
try {
//[2] 由 DispatcherServlet 实现
doService(request, response);
} catch (ServletException ex) {
} catch (IOException ex) {
} catch (Throwable ex) {
} finally {
// 重置 LocaleContext 和 requestAttributes,解除关联
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}// 发布 ServletRequestHandlerEvent 事件
publishRequestHandledEvent(request, startTime, failureCause);
}
}
核心代码是 [2] 由 DispatcherServlet 实现 的 doService(request, response);
DispatcherServlet 的 doService() 方法主要是设置一些 request 属性,并调用 doDispatch() 方法进行请求分发处理,doDispatch() 方法的主要过程是通过 HandlerMapping 获取 Handler,再找到用于执行它的 HandlerAdapter,执行 Handler 后得到 ModelAndView ,ModelAndView 是连接“业务逻辑层”与“视图展示层”的桥梁,接下来就要通过 ModelAndView 获得 View,再通过它的 Model 对 View 进行渲染。doDispatch() 方法如下:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
// 获取当前请求的WebAsyncManager,如果没找到则创建并与请求关联
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// 检查是否有 Multipart,有则将请求转换为 Multipart 请求
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
//[1] 遍历所有的 HandlerMapping 找到与请求对应的 Handler,并将其与一堆拦截器封装到 HandlerExecution 对象中。
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
//[2] 遍历所有的 HandlerAdapter,找到可以处理该 Handler 的 HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 处理 last-modified 请求头
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// [3] 遍历拦截器,执行它们的 preHandle() 方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
try {
//[4] 执行实际的处理程序
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
}
applyDefaultViewName(request, mv);
// [5] 遍历拦截器,执行它们的 postHandle() 方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception ex) {
dispatchException = ex;
}
//[6] 处理执行结果,是一个 ModelAndView 或 Exception,然后进行渲染
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
} catch (Exception ex) {
} catch (Error err) {
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
//[7] 遍历拦截器,执行它们的 afterCompletion() 方法
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
return;
}
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
核心逻辑是[1]-[6]
《DispatcherServlet详解》