Spring MVC —— 关于 DispatcherServlet
前言
DispatcherServlet
负责驱动 web 子容器
,同时基于此创建对应的 web 组件
,本章节结合部分代码理解 DispatcherServlet
的部分细节
DispatcherServlet
整体的继承关系是这样的,因为内容过多,先简单的总结下然后展开:
DispatcherServlet
本身是一个Servlet
,继承自javax.servlet.http.HttpServlet
HttpServletBean
复写init
方法,留出initServletBean
交给子类拓展- 抽象基类
FrameworkServlet
实现initServletBean
方法驱动web 子容器
FrameworkServlet
同时会被注册为web 子容器
的监听器,从而回调onRefresh
,DispatcherServlet
基于此方法初始化web 组件
FrameworkServlet
收口诸如doGet
doPost
等方法到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 组件
处理客户端请求