我们开门见山,首先看看DispatcherServlet这个类的类继承关系:
可以看见DispatcherServlet继承自Java Servlet API的HttpServlet类,是一个标准的Servlet,它的初始化是从HttpServletBean开始的,HttpServletBean的init方法覆盖了父类的同名方法:
public final void init() throws ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Initializing servlet '" + getServletName() + "'");
}
// 将init-param参数绑定到上下文中,方便后续取用
try {
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
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) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
throw ex;
}
//由子类提供各自的初始化实现
initServletBean();
if (logger.isDebugEnabled()) {
logger.debug("Servlet '" + getServletName() + "' configured successfully");
}
}
initServletBan是由HttpServletBean的子类FrameworkServlet来实现的:
protected final void initServletBean() throws ServletException {
//省略代码若干...
try {
//初始化web上下文
this.webApplicationContext = initWebApplicationContext();
//提供给子类实现自己的初始化逻辑,此处是开发者可以扩展的一个方法
initFrameworkServlet();
}
//省略代码若干...
}
protected WebApplicationContext initWebApplicationContext() {
//加载ROOT上下文(ContextLoaderListener加载的上下文)
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
//创建DispatcherServlet注入的上下文
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
//将当前上下文绑定到ROOT上下文
cwac.setParent(rootContext);
}
//更新当前上下文的状态
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
//如果构造时期没有初始化上下文,则查找已绑定的上下文
wac = findWebApplicationContext();
}
if (wac == null) {
//如果没有绑定,则从ROOT上下文创建一个当前上下文
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
// 如果当前上下文不是支持refresh操作的 ConfigurableApplicationContext
//或者构造时期注入的context已经refresh则手动触发onRefresh操作
onRefresh(wac);
}
if (this.publishContext) {
//将当前上下文作为Servlet上下文的一个属性存储起来
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
//省略日志代码若干...
}
return wac;
}
经过以上处理后就完成了父子上下文关系的绑定,如图所示:
从上面的代码不难看出当ROOT上下文和Dispatcher的上下文都创建了以后会调用onRefresh来完成一些子类的逻辑。DispatcherServlet类正好覆盖了这个方法:
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
//初始化文件上传解析器
initMultipartResolver(context);
//初始化国际化解析器
initLocaleResolver(context);
//初始化主题解析器
initThemeResolver(context);
//初始化URL匹配控制器
initHandlerMappings(context);
//初始化URL匹配控制器关联的适配器
initHandlerAdapters(context);
//初始化匹配异常处理器
initHandlerExceptionResolvers(context);
//初始化视图匹配解释器
initRequestToViewNameTranslator(context);
//初始化视图渲染处理器
initViewResolvers(context);
//初始化FlashMap管理器,通常与重定向转存model等使用场景有关
initFlashMapManager(context);
}
DispatcherServlet覆盖了父类的doService方法,这个方法会在Servlet处理请求时调用,doService会用到上述代码所初始化的各种策略对象。在doService中设置好了各项策略对象后会调用DispatcherServlet的核心方法doDispatch,接下来我们就详细了解下这个方法:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
//这里是一个并发请求管理器,spring使用了多线程来处理并发请求
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
//检查请求中是否有上传文件
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 为request寻找合适的handler处理器
mappedHandler = getHandler(processedRequest);
//省略未找到异常处理代码
// 根据handler处理器选择合适的适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
//如果handler支持http报文头的last-modified字段解析则处理之
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//启用handler的前置过滤器,如果未达到要求则立即返回
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//调用handle方法处理请求返回ModelAndView对象
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//如果mv中未设置返回的视图名则启用默认视图名
applyDefaultViewName(request, mv);
//启用handler的后置过滤器
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
//处理返回的ModelAndView,用合适的ViewResolver渲染页面再将页面返回给浏览器
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
//省略代码若干...
}
doDispatch这个核心方法大致描述了整个web请求流程,也道出了springmvc比较核心的两部分request->model和model->response。
此外,再说点题外话,SpringMVC提供了一些的比如RequestHandler,HandlerAdapter等类的默认实现类,关于这些默认类的信息都配置在DispatcherServlet同名目录下的DispatcherServlet.properties文件中。
下节预告
下节中我们将单独抽离出文件上传模块细讲,还将着重分析下SpringMVC的数据绑定、验证、转换部分