一、概述
整个Spring web MVC的设计思想都是围绕着 DispatcherServlet 分发requests 到 handlers 来设计的。
DispatcherServlet对于一个请求的处理工作流如下:
DispatcherServlet是一个真实的Servlet(继承自HttpServlet),所以在构建一个Spring MVC程序时,需要配置DispatcherServlet需要处理的请求,如在 web.xml里配置:
dispatcher
org.springframework.web.servlet.DispatcherServlet
1
dispatcher
/
在Web MVC框架中,每一个DispatcherServlet有自己的WebApplicationContext,这个Context会继承所有在root WebApplicationContext下定义的bean,一个典型的层次结构如下图:
在DispatcherServlet的初始化时,Spring MVC 会在WEB-INF 文件夹下寻找文件名为 [servlet-name]-servlet.xml的配置文件,这个Spring配置创建新的bean,或重写在Root Context下的Bean,所以我们创建配置文件 dispatcher-servlet.xml 如下:
二、DispacherServlet初始化
DispacherServlet的类图结构如下:
FrameworkServlet:Spring web 框架的基类,提供与Spring ApplicationContext集成的功能。
HttpServletBean: 继承自 HttpServlet,是其简单的扩展,而初始化即是从此类重写的init方法开始的。
初始化流程如下:
HttpServletBean的 init方法是DispatcherServlet初始化的入口方法:
// 将配置文件里的参数写入servlet的bean属性中,并且调用子类的初始化方法
@Override
public final void init() throws ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Initializing servlet '" + getServletName() + "'");
}
// 将必要的参数初始化进Bean中
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;
}
// frameworkServlet初始化
initServletBean();
if (logger.isDebugEnabled()) {
logger.debug("Servlet '" + getServletName() + "' configured successfully");
}
}
FrameworkServlet 的initServletBean方法:
// bean的Properties已经被设置好了,创建Servlet的WebApplicationContext
@Override
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
if (this.logger.isInfoEnabled()) {
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
}
long startTime = System.currentTimeMillis();
try {
// 初始化WebApplicationContext
this.webApplicationContext = initWebApplicationContext();
// 默认空实现,需要子类重写
initFrameworkServlet();
}
catch (ServletException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
catch (RuntimeException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
if (this.logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
elapsedTime + " ms");
}
}
可以看到,初始化主要调用FrameworkServlet的initWebApplicationContext()
// 初始化WebApplicationContext
protected WebApplicationContext initWebApplicationContext() {
// 得到此web app的 root WebApplicationContext
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
// 存在一个WebApplicationContext在Servlet创建时被注入
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
// 当前context还没有refreshed -> 设置parent context, 设置application_context_id
if (cwac.getParent() == null) {
// 没有父 context,设置为rootContext
cwac.setParent(rootContext);
}
// 配置wac,并且调用其refresh方法
// 此处完成IOC容器与ApplicationContext的初始化
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
// 若没有在servlet创建时被注入,则查看在当前servlet context下是否存在wac。
// 若有,意味着 已经完成了初始化,设置了Parent context和 contextId
wac = findWebApplicationContext();
}
if (wac == null) {
// 若servlet context没有wac,则创建一个
// 在此方法内部亦会调用configureAndRefreshWebApplicationContext(cwac)
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
// 判断有无 refresh过,调用 DispacherServlet的onRefresh方法
onRefresh(wac);
}
// 若需要发布wac作为servletContext的一个属性
if (this.publishContext) {
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
"' as ServletContext attribute with name [" + attrName + "]");
}
}
return wac;
}
在《Spring core 源码分析 ---- IoC容器(一) 初始化》已详细说明了ApplicationContext初始化的过程,而在整个Web App的WebApplicationContext初始化后,调用了DispacherServlet的一个很关键的方法OnRefresh(wac),此方法才真正的开始Spring MVC的相关初始化,初始化Spring MVC里的所有组件。
DispatcherServlet:
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
// 初始化所有servlet需要使用到的策略对象,此处初始化所有组件
// 可能会被子类重写
protected void initStrategies(ApplicationContext context) {
// 初始化 getBean("multipartResolver")
// 支持文件上传,spring mvc只提供一种CommonsFileupload
initMultipartResolver(context);
// 初始化 getBean("localeResolver")
// 用来支持国际化
initLocaleResolver(context);
// 初始化 getBean("themeResolver")
// 处理web页面的主题
initThemeResolver(context);
// 初始化 List
// 关键处理流程,映射requests到handlers 和一系列的pre-与post-处理器
initHandlerMappings(context);
// 初始化 List
// 帮助servlet直接调用handler
initHandlerAdapters(context);
// 初始化 List
// 处理所有unexpected exception,可以指定modelandView
// 也可捕获标准异常
initHandlerExceptionResolvers(context);
// 初始化 getBean("viewNameTranslator")
// 若没有viewName时,匹配逻辑上的viewName,如localhost/register.html,就是register
// 然后由ViewResolver可能变成localhost/register.jsp
initRequestToViewNameTranslator(context);
// 初始化 List
// viewResolver解决viewName与view的真实关系,如本例配置的suffix=".jsp"
initViewResolvers(context);
// 初始化 getBean("flashMapManager")
// 用于 存储与取回 input与output的FlashMap
// 通常在重定向时传递attributes到另一个请求
initFlashMapManager(context);
}
initMultipartResolver ,initLocaleResolver,initThemeResolver,initRequestToViewNameTranslator的实现都是简单的getBean("**") ,若抛出异常,则根据DispatcherServlet.properties 取默认的Strategy 。
所以我们仔细看initHandlerMappings(context)方法以及initHandlerAdapters(这也是后续处理request的关键初始化方法)。
// 初始化这个类使用的handlersMappings
// 如果BeanFactory 没有HandlerMapping, 我们默认使用BeanNameUrlHandlerMapping
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// 找到ApplicationContext的所有HandlerMappings ,包括祖先Context
Map matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList(matchingBeans.values());
// 排序handlerMappings
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// 之后会添加一个默认的 HandlerMapping
}
}
// 如果初始化handlerMappings为空,则添加一个默认的HandlerMapping
if (this.handlerMappings == null) {
// DispatcherServlet同个包有DispatcherServlet.properties文件,根据这个文件来创建
// 默认的HandlerMapping---- BeanNameUrlHandlerMapping 以及 DefaultAnnotationHandlerMapping
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isDebugEnabled()) {
logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
}
}
}
DispatcherServlet.properties如下:
# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
initHandlerAdapters,initHandlerExceptionResolvers,initViewResolvers与initHandlerMapping的逻辑一样。
至此,WebApplicationContext初始化完毕。