. 概述
对于Web开发者,MVC模型是大家再熟悉不过的了,SpringMVC中,满足条件的请求进入到负责请求分发的DispatcherServlet,DispatcherServlet根据请求url到控制器的映射(HandlerMapping中保存),HandlerMapping最终返回HandlerExecutionChain,其中包含了具体的处理对象handler(也即我们编程时写的controller)以及一系列的拦截器interceptors,此时DispatcherServlet会根据返回的HandlerExecutionChain中的handler找到支持这一处理器类型的适配器(handlerAdapter),在处理器适配器中最终会去调用控制器的请求响应方法并返回结果视图(ModelAndView),得到结果视图后,通过render方法完成结果的显示。
HanderMapping的继承体系:
HandlerAdapter的继承体系:
同样的视图解析器ViewResolver针对不同的输出格式也有一系列的实现类,具体可自己看。
2. 实现分析
以我自己的一个web项目中spring mvc的配置为例:
这里因为是采用全注解的方式,所以先通过context:component-scan配置让spring自定扫描的包路径,接着配置handlerMapping、handlerAdapter及ViewResolver,几乎包含了SpringMVC的配置中需要涉及的所有元素。后面需要涉及具体的HanderMapping等的实现时,将以这里配置中的实现为例进行分析,其它的大家“同理可解”。⊙﹏⊙b汗
2.1 Spring MVC初始化流程
DispatcherServlet的继承体系如:
看到它们继承自HttpServlet,你就知道初始化过程应该是从init方法开始了,整个初始化的流程为:
1、class=HttpServletBean&method=init初始化方法,里面调用了class=FrameworkServlet&method=initServletBean
public final void init() throws ServletException {
if (logger.isDebugEnabled())
logger.debug((new StringBuilder()).append("Initializing servlet '").append(getServletName()).append("'")
.toString());
try {
org.springframework.beans.PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(),
requiredProperties);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
org.springframework.core.io.ResourceLoader resourceLoader = new ServletContextResourceLoader(
getServletContext());
bw.registerCustomEditor(org / springframework / core / io / Resource,
new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
} catch (BeansException ex) {
logger.error((new StringBuilder()).append("Failed to set bean properties on servlet '")
.append(getServletName()).append("'").toString(), ex);
throw ex;
}
initServletBean();
if (logger.isDebugEnabled())
logger.debug((new StringBuilder()).append("Servlet '").append(getServletName())
.append("' configured successfully").toString());
}
2、class=FrameworkServlet&method=initServletBean调用了class=FrameworkServlet&method=initWebApplicationContext,获取配置文件,生成上下文
protected final void initServletBean() throws ServletException {
getServletContext().log((new StringBuilder()).append("Initializing Spring FrameworkServlet '")
.append(getServletName()).append("'").toString());
if (logger.isInfoEnabled())
logger.info((new StringBuilder()).append("FrameworkServlet '").append(getServletName())
.append("': initialization started").toString());
long startTime = System.currentTimeMillis();
try {
webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
} catch (ServletException ex) {
logger.error("Context initialization failed", ex);
throw ex;
} catch (RuntimeException ex) {
logger.error("Context initialization failed", ex);
throw ex;
}
if (logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
logger.info((new StringBuilder()).append("FrameworkServlet '").append(getServletName())
.append("': initialization completed in ").append(elapsedTime).append(" ms").toString());
}
}
3、class=FrameworkServlet&method=initWebApplicationContext调用了class=DispatcherServlet&method=onRefresh(在调用之前验证了一下上下文是否已经存在,存在的话就无需调用onRefresh),主要进行了初始化策略initStrategies
protected WebApplicationContext initWebApplicationContext() {
WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (webApplicationContext != null) {
wac = webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
if (cwac.getParent() == null)
cwac.setParent(rootContext);
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null)
wac = findWebApplicationContext();
if (wac == null)
wac = createWebApplicationContext(rootContext);
if (!refreshEventReceived)
onRefresh(wac);
if (publishContext) {
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
if (logger.isDebugEnabled())
logger.debug((new StringBuilder()).append("Published WebApplicationContext of servlet '")
.append(getServletName()).append("' as ServletContext attribute with name [").append(attrName)
.append("]").toString());
}
return wac;
}
class=DispatcherServlet&method=onRefresh(在调用之前验证了一下上下文是否已经存在,存在的话就无需调用onRefresh),主要进行了初始化策略initStrategies:
initMultipartResolver(context);initLocaleResolver(context);initThemeResolver(context);initHandlerMappings(context);
initHandlerAdapters(context); initHandlerExceptionResolvers(context);initRequestToViewNameTranslator(context);
initViewResolvers(context); initFlashMapManager(context);
这些相关的解析器,适配器、处理器等都是在mvc配置文件设置好的,就是将对应的配置进行初始化
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);
}
5、当前台请求post或者get,class=DispatchServlet&method=doService会处理对应的请求
protected void doService(HttpServletRequest request, HttpServletResponse response)
throws Exception
{
Map attributesSnapshot;
if(logger.isDebugEnabled())
{
String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
logger.debug((new StringBuilder()).append("DispatcherServlet with name '").append(getServletName()).append("'").append(resumed).append(" processing ").append(request.getMethod()).append(" request for [").append(getRequestUri(request)).append("]").toString());
}
attributesSnapshot = null;
if(WebUtils.isIncludeRequest(request))
{
attributesSnapshot = new HashMap();
Enumeration attrNames = request.getAttributeNames();
do
{
if(!attrNames.hasMoreElements())
break;
String attrName = (String)attrNames.nextElement();
if(cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet"))
attributesSnapshot.put(attrName, request.getAttribute(attrName));
} while(true);
}
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
FlashMap inputFlashMap = flashMapManager.retrieveAndUpdate(request, response);
if(inputFlashMap != null)
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, flashMapManager);
doDispatch(request, response);
if(WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted())
return;
if(attributesSnapshot != null)
restoreAttributesAfterInclude(request, attributesSnapshot);
break MISSING_BLOCK_LABEL_354;
Exception exception;
exception;
if(WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted())
return;
if(attributesSnapshot != null)
restoreAttributesAfterInclude(request, attributesSnapshot);
throw exception;
}
6、class=DispatchServlet&method=doService调用了class=DispatchServlet&method=doDispatch,判断当前请求method名称等
protected void doDispatch(HttpServletRequest request, HttpServletResponse response)
throws Exception
{
HttpServletRequest processedRequest;
HandlerExecutionChain mappedHandler;
boolean multipartRequestParsed;
WebAsyncManager asyncManager;
processedRequest = request;
mappedHandler = null;
multipartRequestParsed = false;
asyncManager = WebAsyncUtils.getAsyncManager(request);
ModelAndView mv;
Exception dispatchException;
mv = null;
dispatchException = null;
processedRequest = checkMultipart(request);
multipartRequestParsed = processedRequest != request;
mappedHandler = getHandler(processedRequest);
if(mappedHandler != null && mappedHandler.getHandler() != null)
break MISSING_BLOCK_LABEL_91;
noHandlerFound(processedRequest, response);
if(asyncManager.isConcurrentHandlingStarted())
{
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
return;
}
if(multipartRequestParsed)
cleanupMultipart(processedRequest);
return;
HandlerAdapter ha;
boolean isGet;
long lastModified;
ha = getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
isGet = "GET".equals(method);
if(!isGet && !"HEAD".equals(method))
break MISSING_BLOCK_LABEL_251;
lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if(logger.isDebugEnabled())
logger.debug((new StringBuilder()).append("Last-Modified value for [").append(getRequestUri(request)).append("] is: ").append(lastModified).toString());
if((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet)
{
if(asyncManager.isConcurrentHandlingStarted())
{
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
return;
}
if(multipartRequestParsed)
cleanupMultipart(processedRequest);
return;
}
if(!mappedHandler.applyPreHandle(processedRequest, response))
{
if(asyncManager.isConcurrentHandlingStarted())
{
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
return;
}
if(multipartRequestParsed)
cleanupMultipart(processedRequest);
return;
}
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if(asyncManager.isConcurrentHandlingStarted())
{
if(asyncManager.isConcurrentHandlingStarted())
{
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
return;
}
if(multipartRequestParsed)
cleanupMultipart(processedRequest);
return;
}
break MISSING_BLOCK_LABEL_379;
Exception exception;
exception;
if(asyncManager.isConcurrentHandlingStarted())
{
if(asyncManager.isConcurrentHandlingStarted())
{
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
return;
}
if(multipartRequestParsed)
cleanupMultipart(processedRequest);
return;
}
throw exception;
applyDefaultViewName(request, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
break MISSING_BLOCK_LABEL_404;
Exception ex;
ex;
dispatchException = ex;
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
if(asyncManager.isConcurrentHandlingStarted())
{
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
return;
}
if(multipartRequestParsed)
cleanupMultipart(processedRequest);
break MISSING_BLOCK_LABEL_558;
Exception ex;
ex;
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
if(asyncManager.isConcurrentHandlingStarted())
{
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
return;
}
if(multipartRequestParsed)
cleanupMultipart(processedRequest);
break MISSING_BLOCK_LABEL_558;
Error err;
err;
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
if(asyncManager.isConcurrentHandlingStarted())
{
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
return;
}
if(multipartRequestParsed)
cleanupMultipart(processedRequest);
break MISSING_BLOCK_LABEL_558;
Exception exception1;
exception1;
if(asyncManager.isConcurrentHandlingStarted())
{
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
return;
}
if(multipartRequestParsed)
cleanupMultipart(processedRequest);
throw exception1;
}
看起来貌似有点复杂,其实理解了IOC容器的实现原理就很简单,函数一开始会去获取WebApplicationContext对象,这个对象在ContextLoaderListener初始化IOC容器时就已经把它set到ServletContext的属性中,而且它也正是ConfigurableWebApplicationContext的实例,第一个if语句其实就是如果此时SpringIOC容器没有初始化的话就在这里启动IOC容器的初始化过程,因为看“省略(1)”中的代码你就知道,它会在这里调用refresh函数,“世人”都知道这就是IOC容器启动的入口,这里会解析配置文件springmvc-servlet.xml。
这里最终要执行onRefresh(),而这个就是SpringMVC初始化的入口。
(注:其实这里也可以配置log4j,通过其打印的info信息来看IOC与MVC的初始化顺序)