【SpringMVC】 第二篇 DispatcherServlet初始化

初始化

DispatcherServlet是SpringMVC的核心,因此初始化也是圍繞着他展開。在上一篇文章從Servlet提及中講到,Servlet提供了初始化的方法init,咱們先看一下DispatcherServlet的類圖:java

1231231232222

咱們從圖中所示的繼承關係,咱們去找一下哪裏實現了這個init方法。web

GenerericServlet

GenerericServlet實現了Servlet接口,它定義了一個通用的,無關協議的Servlet實現,咱們能夠看到它的init方法體是空的,說明他將具體的實現交給了它的子類spring

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

HttpServlet

HttpServlet繼承了GenerericServlet,他和GenerericServlet不同,他是一個抽象類,基於HTTP協議,提供了doGet() doPost() doPut() doDelete() 等方法,這些也就是咱們熟悉的get,post,put,delete等請求方式,咱們發現他沒有覆蓋GenerericServletinit方法app

HttpServletBean

HttpServletBean,他也是一個抽象類,咱們看一下他實現的init方法ide

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()方法,這個方法體是空,說明須要子類去覆蓋實現。接下來咱們去找他的子類的實現post

FrameworkServlet

FrameworkServlet,一樣他是一個抽象類,繼承了HttpServletBean,該類提供了Servlet上下文的初始化,咱們能夠看到,他同時實現了HttpServletBeaninitServletBean()方法,咱們看一下這個方法ui

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

咱們先看initWebApplicationContext(), 從方法名咱們能夠猜出,他應該是初始化上下文的,看一下他的源碼this

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容器的父容器,接下來咱們一步步分析spa

  • 獲取根容器,即Spring 容器code

    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

相關文章
相關標籤/搜索