Spring MVC —— 关于 DispatcherServlet
前言
DispatcherServlet 负责驱动 web 子容器,同时基于此创建对应的 web 组件,本章节结合部分代码理解 DispatcherServlet 的部分细节
DispatcherServlet

整体的继承关系是这样的,因为内容过多,先简单的总结下然后展开:
DispatcherServlet本身是一个Servlet,继承自javax.servlet.http.HttpServletHttpServletBean复写init方法,留出initServletBean交给子类拓展- 抽象基类
FrameworkServlet实现initServletBean方法驱动web 子容器 FrameworkServlet同时会被注册为web 子容器的监听器,从而回调onRefresh,DispatcherServlet基于此方法初始化web 组件FrameworkServlet收口诸如doGetdoPost等方法到processRequest,并提供方法doService给子类实现DispatcherServlet实现doService,进行一些前置处理后,由doDispatch方法基于web 组件处理web 请求
FrameworkServlet#initServletBean
@Override
protected final void initServletBean() throws ServletException {
// ...
try {
// 初始化 web子容器
this.webApplicationContext = initWebApplicationContext();
// 针对 FrameworkServlet 的钩子
initFrameworkServlet();
}
catch (ServletException | RuntimeException ex) {
throw ex;
}
// ...
}
----------- initWebApplicationContext ----------
protected WebApplicationContext initWebApplicationContext() {
// 从 ServletContext 获取根容器
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
// 指定根容器为 parent
if (cwac.getParent() == null) {
cwac.setParent(rootContext);
}
// 配置并启动 web 子容器
configureAndRefreshWebApplicationContext(cwac);
}
}
}
// ...
return wac;
}
- 在
DispatcherServlet的init的生命周期阶段,驱动之前(见上章节)创建的web 子容器 - 如果存在
根容器,会被指定为父容器(之所以为什么叫根容器) configureAndRefreshWebApplicationContext方法对web 子容器进行必要的配置并启动(refresh)它,其中包括将DispatcherServlet本身注册为web 子容器的ContextRefreshListener
DispatcherServlet#onRefresh
@Override
protected void onRefresh(ApplicationContext context) {
// 组件初始化
initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
DispatcherServlet本身作为web 子容器的ContextRefreshListener,在容器发布ContextRefreshedEvent后,回调onRefresh方法onRefresh方法中,DispatcherServlet基于启动好的web 子容器,初始化对应的web 组件- 此处针对组件的初始化代码了解这种模式,对于其中有必要的组件细节会在后续章节学习
initMultipartResolver
private void initMultipartResolver(ApplicationContext context) {
try {
// 从容器中获取 MultipartResolver 实例
this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
}
// 没有就算了
catch (NoSuchBeanDefinitionException ex) {
this.multipartResolver = null;
if (logger.isTraceEnabled()) {
logger.trace("...");
}
}
}
- 这是初始化
MultipartResolver组件,用来把HttpServletRequest转换为MultipartHttpServletRequest - 初始化模式就是从
web 子容器中获取对应的实例,允许不存在
initLocaleResolver
private void initLocaleResolver(ApplicationContext context) {
try {
// 从容器中获取 LocaleResolver 实例
this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
}
catch (NoSuchBeanDefinitionException ex) {
// 如果没有,则基于 DispatcherServlet.properties 配置文件获取默认组件
this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);
}
}
- 这是初始化
LocaleResolver组件,用来解析从请求中解析Locale以处理国际化相关 - 不同于
MultipartResolver组件的初始化,如果当前容器中没有对应实例,则会基于Spring提供的DispatcherServlet.properties来实例化默认组件 - 默认的
LocaleResolver是AcceptHeaderLocaleResolver,即根据Accept请求头解析Locale实例
小结
- 基本上上述组件的初始化都是这种模式:从
web 子容器中获取对应Bean实例 - 如果不存在,对于必须的组件则依据
Spring的默认配置创建
DispatcherServlet#doDispatch
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
/**
* 如果有必要,把 HttpServletRequest 转换为 MultipartHttpServletRequest
*/
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 获取请求对应的 HandlerExecutionChain
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 推断对应的 HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// ...
/**
* 执行所有的 HandlerInterceptor#preHandle
* 如果被拦截了就 return
*/
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 请求处理
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 视图名称转换
applyDefaultViewName(processedRequest, mv);
// 执行所有 HandlerInterceptor#postHandle
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
// ...
}
catch (Throwable err) {
// ...
}
// 请求结果的处理
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
// ...
}
- 正如之前所说,
DispatcherServlet作为核心Servlet,所有的请求处理最终都收口到doDispatch方法 - 粗略地说,这里就是基于之前初始化的
web 组件来处理客户端的请求,比如HttpServletRequest转换、@RequestMapping的匹配、请求结果的处理 等等 - 这里是
DispatcherServlet处理请求的大体流程,旨在了解DispatcherServlet的角色和作用,更多的细节实际是委托给对应的web 组件的
总结
本章节针对 DispatcherServlet 的部分功能做了描述:
- 驱动
web 子容器 - 基于
web 子容器初始化web 组件 - 基于
web 组件处理客户端请求










