初始化

DispatcherServlet是SpringMVC的核心,所以初始化也是围绕着他展开。在上一篇文章从Servlet说起中讲到,Servlet提供了初始化的方法init,我们先看一下DispatcherServlet的类图:

我们从图中所示的继承关系,我们去找一下哪里实现了这个init方法。

GenerericServlet

GenerericServlet实现了Servlet接口,它定义了一个通用的,无关协议的Servlet实现,我们可以看到它的init方法体是空的,说明他将具体的实现交给了它的子类

public void init() throws ServletException {    // NOOP by default}

HttpServlet

HttpServlet继承了GenerericServlet,他和GenerericServlet不一样,他是一个抽象类,基于HTTP协议,提供了doGet() doPost() doPut() doDelete() 等方法,这些也就是我们熟悉的get,post,put,delete等请求方式,我们发现他没有覆盖GenerericServletinit方法

HttpServletBean

HttpServletBean,他也是一个抽象类,我们看一下他实现的init方法

public final void init() throws ServletException {     // 我们在配置web.xml时候,我们会配置   // <init-param>     //      <param-name>contextConfigLocation</param-name>     //        <param-value>/xxxx/xxxx.xml</param-value>     // </init-param>   // 这个方法就是用来加载配置文件   // Set bean properties from init parameters.   PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);   if (!pvs.isEmpty()) {      try {         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) {         if (logger.isErrorEnabled()) {            logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);         }         throw ex;      }   }   // Let subclasses do whatever initialization they like.   // 为他的子类提供初始化的方法,他是一个空实现,需要子类覆盖实现   initServletBean();}

到这里,我们可以知道HttpServletBeaninit方法实现了Servlet的init方法,即DispatcherServlet初始化的入口,他又提供了initServletBean()方法,这个方法体是空,说明需要子类去覆盖实现。接下来我们去找他的子类的实现

FrameworkServlet

FrameworkServlet,同样他是一个抽象类,继承了HttpServletBean,该类提供了Servlet上下文的初始化,我们可以看到,他同时实现了HttpServletBeaninitServletBean()方法,我们看一下这个方法

protected final void initServletBean() throws ServletException {        ......              try {            this.webApplicationContext = initWebApplicationContext();            initFrameworkServlet();        }        catch (ServletException | RuntimeException ex) {            logger.error("Context initialization failed", ex);            throw ex;        }              .......    }                

我们先看initWebApplicationContext(), 从方法名我们可以猜出,他应该是初始化上下文的,看一下他的源码

protected WebApplicationContext initWebApplicationContext() {        // 拿到spring容器        WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());        WebApplicationContext wac = null;        if (this.webApplicationContext != null) {            // A context instance was injected at construction time -> use it            // 如果webApplicationContext已经注入,使用它            wac = this.webApplicationContext;            if (wac instanceof ConfigurableWebApplicationContext) {                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;                if (!cwac.isActive()) {                    // The context has not yet been refreshed -> provide services such as                    // setting the parent context, setting the application context id, etc                    if (cwac.getParent() == null) {                        // The context instance was injected without an explicit parent -> set                        // the root application context (if any; may be null) as the parent                        // 把spring 容器作为该servlet容器的父容器                        cwac.setParent(rootContext);                    }                    configureAndRefreshWebApplicationContext(cwac);                }            }        }        if (wac == null) {            // No context instance was injected at construction time -> see if one            // has been registered in the servlet context. If one exists, it is assumed            // that the parent context (if any) has already been set and that the            // user has performed any initialization such as setting the context id            // 如果webApplicationContext没有注入,继续查询            wac = findWebApplicationContext();        }        if (wac == null) {            // No context instance is defined for this servlet -> create a local one            // 如果还是没有,就新建            wac = createWebApplicationContext(rootContext);        }        if (!this.refreshEventReceived) {            // Either the context is not a ConfigurableApplicationContext with refresh            // support or the context injected at construction time had already been            // refreshed -> trigger initial onRefresh manually here.            synchronized (this.onRefreshMonitor) {                onRefresh(wac);            }        }        if (this.publishContext) {            // Publish the context as a servlet context attribute.            String attrName = getServletContextAttributeName();            getServletContext().setAttribute(attrName, wac);        }        return wac;

SpringMVC中存在两种上下文,一个是spring容器,一个是springMVC容器,spring容器作为springMVC容器的父容器,接下来我们一步步分析

  • 获取根容器,即Spring 容器

    WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());

    public static WebApplicationContext getWebApplicationContext(ServletContext sc) {   return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);}String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";public static WebApplicationContext getWebApplicationContext(ServletContext sc, String attrName) {        ......        Object attr = sc.getAttribute(attrName);        ......    }

    可以看到,根据WebApplicationContext.class.getName() + ".ROOT"从ServletContext中获取,那问题来了,他是什么时候放进去的呢?我们在配置web.xml的时候会配置这样一段

    <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener>

    我们还是看一下他的源码,ContextLoaderListenerServletContextListener的实现,我们看下关键性的代码

    public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {         ......              servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);      .......}
  • 获取SpringMVC容器

    第一种是我们可以把DispatcherServlet作为spring的bean,将SpringMVC容器注入,这里我们直接拿来作为SpringMVC容器,第一个if就是在做这事,第二种是我们可以把SpringMVC容器注册到ServletContext中去,根据context id去获取,这是第二个if做的事

    如果以上两种情况都不是,就自己创建SpringMVC容器

  • SpringMVC容器初始化成功后,我们接下来看onRefresh(wac);方法,他是又是一个空实现,我们去找他的子类

    protected void onRefresh(ApplicationContext context) {   // For subclasses: do nothing by default.}

DispatcherServlet

我们中去来到了SpringMVC的核心DispatcherServlet,我们看下他的onRefresh方法

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在这里初始化他的组件,我们先看一下,以下的代码

private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";static {   // Load default strategy implementations from properties file.   // This is currently strictly internal and not meant to be customized   // by application developers.   try {      ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);      defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);   }   catch (IOException ex) {      throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());   }}

我们可以看到DispatcherServlet默认了一些策略,这里通过classPath下的DispatcherServlet.properties文件进行配置

MultipartResolver

我们先看一下他的初始化代码

public static final String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver";private void initMultipartResolver(ApplicationContext context) {   try {      this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);      ......   }   catch (NoSuchBeanDefinitionException ex) {      // Default is no multipart resolver.      this.multipartResolver = null;      ......   }}

MultipartResolver用于实现SpringMVC的文件上传功能,可以看到要实现文件上传功能,必须注册MultipartResolver的bean,而且默认是不支持文件上传的

LocaleResolver

同样的我们先看代码

private void initLocaleResolver(ApplicationContext context) {   try {      this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);      ......   }   catch (NoSuchBeanDefinitionException ex) {      // We need to use the default.      this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);      ......   }}

同样的会先从容器中找LocaleResolver的bean,如果未找到,则加载默认策略,配置在classPath下的DispatcherServlet.properties文件,org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver, LocaleResolver默认AcceptHeaderLocaleResolver,即根据request的请求头Accept-Language获取地区

ThemeResolver

private void initThemeResolver(ApplicationContext context) {        try {            // 先去找名称为themeResolver的bean            this.themeResolver = context.getBean(THEME_RESOLVER_BEAN_NAME, ThemeResolver.class);            ......        }        catch (NoSuchBeanDefinitionException ex) {            // We need to use the default.            //加载默认策略            this.themeResolver = getDefaultStrategy(context, ThemeResolver.class);            ......        }    }

先去找名称为themeResolver的bean,如果不存在,加载默认策略,默认为FixedThemeResolver

HandlerMapping

private void initHandlerMappings(ApplicationContext context) {   this.handlerMappings = null;   if (this.detectAllHandlerMappings) {     // 默认去找所有实现了HandlerMapping的bean      // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.      Map<String, HandlerMapping> matchingBeans =            BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);      if (!matchingBeans.isEmpty()) {         this.handlerMappings = new ArrayList<>(matchingBeans.values());         // We keep HandlerMappings in sorted order.         // 按照优先级排序         AnnotationAwareOrderComparator.sort(this.handlerMappings);      }   }   else {      try {            // 只找名称为handlerMapping的bean         HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);         this.handlerMappings = Collections.singletonList(hm);      }      catch (NoSuchBeanDefinitionException ex) {         // Ignore, we'll add a default HandlerMapping later.      }   }   // Ensure we have at least one HandlerMapping, by registering   // a default HandlerMapping if no other mappings are found.   if (this.handlerMappings == null) {         // 加载默认策略      this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);      ......   }}

SpringMVC默认加载所有实现了HandlerMapping的bean,并按照优先级排序,或者只找名称为handlerMapping的bean,这个可以在web.xml中通过detectAllHandlerMappings参数进行配置,如果前两种都没有找到bean,则加载默认策略,默认为BeanNameUrlHandlerMapping RequestMappingHandlerMapping RouterFunctionMapping

HandlerAdapter

private void initHandlerAdapters(ApplicationContext context) {   this.handlerAdapters = null;   if (this.detectAllHandlerAdapters) {      // Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.      // 默认去找所有实现了HandlerAdapter的bean      Map<String, HandlerAdapter> matchingBeans =            BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);      if (!matchingBeans.isEmpty()) {         this.handlerAdapters = new ArrayList<>(matchingBeans.values());         // We keep HandlerAdapters in sorted order.         // 按照优先级排序         AnnotationAwareOrderComparator.sort(this.handlerAdapters);      }   }   else {      try {         // 找名称为handlerAdapter的bean         HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);         this.handlerAdapters = Collections.singletonList(ha);      }      catch (NoSuchBeanDefinitionException ex) {         // Ignore, we'll add a default HandlerAdapter later.      }   }   // Ensure we have at least some HandlerAdapters, by registering   // default HandlerAdapters if no other adapters are found.   // 加载默认策略   if (this.handlerAdapters == null) {      this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);      if (logger.isTraceEnabled()) {         logger.trace("No HandlerAdapters declared for servlet '" + getServletName() +               "': using default strategies from DispatcherServlet.properties");      }   }}

和HandlerMapping一样,先去找所有的实现了HandlerAdapter的bean,或者找名称为handlerAdapter的bean,这个可以在web.xml中通过detectAllHandlerAdapters参数进行配置,都没有再加载默认策略,默认HttpRequestHandlerAdapter SimpleControllerHandlerAdapter RequestMappingHandlerAdapter HandlerFunctionAdapter

HandlerExceptionResolver

private void initHandlerExceptionResolvers(ApplicationContext context) {   this.handlerExceptionResolvers = null;   if (this.detectAllHandlerExceptionResolvers) {      // Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts.      Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils            .beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);      if (!matchingBeans.isEmpty()) {         this.handlerExceptionResolvers = new ArrayList<>(matchingBeans.values());         // We keep HandlerExceptionResolvers in sorted order.         AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);      }   }   else {      try {         HandlerExceptionResolver her =               context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);         this.handlerExceptionResolvers = Collections.singletonList(her);      }      catch (NoSuchBeanDefinitionException ex) {         // Ignore, no HandlerExceptionResolver is fine too.      }   }   // Ensure we have at least some HandlerExceptionResolvers, by registering   // default HandlerExceptionResolvers if no other resolvers are found.   if (this.handlerExceptionResolvers == null) {      this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);      if (logger.isTraceEnabled()) {         logger.trace("No HandlerExceptionResolvers declared in servlet '" + getServletName() +               "': using default strategies from DispatcherServlet.properties");      }   }}

和HandlerMapping一样,先去找所有的实现了HandlerExceptionResolver的bean,或者找名称为handlerExceptionResolver的bean,这个可以在web.xml中通过detectAllHandlerExceptionResolvers参数进行配置,都没有再加载默认策略,默认ExceptionHandlerExceptionResolver ResponseStatusExceptionResolver DefaultHandlerExceptionResolver

RequestToViewNameTranslator

private void initRequestToViewNameTranslator(ApplicationContext context) {   try {      this.viewNameTranslator =            context.getBean(REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME, RequestToViewNameTranslator.class);      ......   }   catch (NoSuchBeanDefinitionException ex) {      // We need to use the default.      this.viewNameTranslator = getDefaultStrategy(context, RequestToViewNameTranslator.class);      ......   }}

先去找名称为viewNameTranslator的bean,如果不存在,加载默认策略,默认为DefaultRequestToViewNameTranslator

ViewResolver

private void initViewResolvers(ApplicationContext context) {   this.viewResolvers = null;   if (this.detectAllViewResolvers) {      // Find all ViewResolvers in the ApplicationContext, including ancestor contexts.      Map<String, ViewResolver> matchingBeans =            BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);      if (!matchingBeans.isEmpty()) {         this.viewResolvers = new ArrayList<>(matchingBeans.values());         // We keep ViewResolvers in sorted order.         AnnotationAwareOrderComparator.sort(this.viewResolvers);      }   }   else {      try {         ViewResolver vr = context.getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class);         this.viewResolvers = Collections.singletonList(vr);      }      catch (NoSuchBeanDefinitionException ex) {         // Ignore, we'll add a default ViewResolver later.      }   }   // Ensure we have at least one ViewResolver, by registering   // a default ViewResolver if no other resolvers are found.   if (this.viewResolvers == null) {      this.viewResolvers = getDefaultStrategies(context, ViewResolver.class);      if (logger.isTraceEnabled()) {         logger.trace("No ViewResolvers declared for servlet '" + getServletName() +               "': using default strategies from DispatcherServlet.properties");      }   }}

和HandlerMapping一样,先去找所有的实现了ViewResolver的bean,或者找名称为viewResolver的bean,这个可以在web.xml中通过detectAllViewResolvers参数进行配置,都没有再加载默认策略,默认InternalResourceViewResolver

FlashMapManager

private void initFlashMapManager(ApplicationContext context) {   try {      this.flashMapManager = context.getBean(FLASH_MAP_MANAGER_BEAN_NAME, FlashMapManager.class);      ......   }   catch (NoSuchBeanDefinitionException ex) {      // We need to use the default.      this.flashMapManager = getDefaultStrategy(context, FlashMapManager.class);      ......   }}

先去找名称为flashMapManager的bean,如果不存在,加载默认策略,默认为SessionFlashMapManager