springmvc源碼解析之配置加載ContextLoadListener

說在前面web

本次主要介紹springmvc配置解析,更多源碼解析文章請關注「天河聊技術」微信公衆號。spring

 

springmvc配置解析微信

本次介紹org.springframework.web.context.ContextLoaderListener初始化,進入到這個方法org.springframework.web.context.ContextLoaderListener#contextInitialized,監聽器收到一個servletContext上下文初始化完畢的事件後初始化WebApplicationContext,進入到這個方法org.springframework.web.context.ContextLoader#initWebApplicationContextmvc

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
//從servletContext中獲取綁定key值是org.springframework.context.ApplicationContext.WebApplicationContext.ROOT的webApplicationContext對象
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
         throw new IllegalStateException(
               "Cannot initialize context because there is already a root application context present - " +
               "check whether you have multiple ContextLoader* definitions in your web.xml!");
}

      Log logger = LogFactory.getLog(ContextLoader.class);
servletContext.log("Initializing Spring root WebApplicationContext");
if (logger.isInfoEnabled()) {
         logger.info("Root WebApplicationContext: initialization started");
}
      long startTime = System.currentTimeMillis();
try {
         // Store context in local instance variable, to guarantee that 將上下文存儲在本地實例變量中,以保證這一點
 // it is available on ServletContext shutdown.它在ServletContext關閉時可用。
 if (this.context == null) {
//建立web上下文 ->
this.context = createWebApplicationContext(servletContext);
 }
         if (this.context instanceof ConfigurableWebApplicationContext) {
            ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) {
               // The context has not yet been refreshed -> provide services such as 上下文還沒有刷新—>提供瞭如下服務
 // setting the parent context, setting the application context id, etc 設置父上下文、設置應用程序上下文id等等
 if (cwac.getParent() == null) {
                  // The context instance was injected without an explicit parent -> 上下文實例在沒有顯式父>的狀況下被注入
// determine parent for root web application context, if any.肯定根web應用程序上下文的父級(若是有的話)。
//->
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
 }
// 配置和刷新web上下文 ->
 configureAndRefreshWebApplicationContext(cwac, servletContext);
}
         }
// web上下文綁定到servlet上下文中,key=org.springframework.web.context.WebApplicationContext.ROOT
 servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
 ClassLoader ccl = Thread.currentThread().getContextClassLoader();
 if (ccl == ContextLoader.class.getClassLoader()) {
            currentContext = this.context;
 }
         else if (ccl != null) {
            currentContextPerThread.put(ccl, this.context);
 }

         if (logger.isDebugEnabled()) {
            logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
                  WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
 }
         if (logger.isInfoEnabled()) {
            long elapsedTime = System.currentTimeMillis() - startTime;
logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
 }

         return this.context;
}
      catch (RuntimeException ex) {
         logger.error("Context initialization failed", ex);
 servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
 throw ex;
}
      catch (Error err) {
         logger.error("Context initialization failed", err);
 servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
 throw err;
}
   }

進入到這個方法org.springframework.web.context.ContextLoader#createWebApplicationContextapp

protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
//找到web上下文初始化類 ->
Class<?> contextClass = determineContextClass(sc);
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
         throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
               "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
}
//初始化web上下文
return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
 }

進入到這個方法org.springframework.web.context.ContextLoader#determineContextClasside

protected Class<?> determineContextClass(ServletContext servletContext) {
//從servlet上下文獲取contextClass屬性值,指定web上下文的類
String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
if (contextClassName != null) {
         try {
            return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
 }
         catch (ClassNotFoundException ex) {
            throw new ApplicationContextException(
                  "Failed to load custom context class [" + contextClassName + "]", ex);
 }
      }
      else {
// 若是servlet上下文沒有配置這個屬性值就從ContextLoader.properties這個配置文件中加載 ->
 contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
 try {
            return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
 }
         catch (ClassNotFoundException ex) {
            throw new ApplicationContextException(
                  "Failed to load default context class [" + contextClassName + "]", ex);
 }
      }
   }

找到這裏post

static {
      // Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
// by application developers.
try {
// 加載web上下文默認初始化類org.springframework.web.context.support.XmlWebApplicationContext從ContextLoader.properties這個配置文件
 ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
 defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
      catch (IOException ex) {
         throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
}
   }

往上返回到這個方法org.springframework.web.context.ContextLoader#loadParentContextthis

protected ApplicationContext loadParentContext(ServletContext servletContext) {
      ApplicationContext parentContext = null;
//從servlet上下文獲取locatorFactorySelector參數值
String locatorFactorySelector = servletContext.getInitParameter(LOCATOR_FACTORY_SELECTOR_PARAM);
//從servlet上下文獲取parentContextKey參數值
String parentContextKey = servletContext.getInitParameter(LOCATOR_FACTORY_KEY_PARAM);
if (parentContextKey != null) {
         // locatorFactorySelector may be null, indicating the default "classpath*:beanRefContext.xml" locatorFactorySelector能夠是null,表示默認的「classpath*:beanRefContext.xml」
 BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);
 Log logger = LogFactory.getLog(ContextLoader.class);
 if (logger.isDebugEnabled()) {
            logger.debug("Getting parent context definition: using parent context key of '" +
                  parentContextKey + "' with BeanFactoryLocator");
 }
         this.parentContextRef = locator.useBeanFactory(parentContextKey);
 parentContext = (ApplicationContext) this.parentContextRef.getFactory();
}

      return parentContext;
 }

往上返回進入到這個方法org.springframework.web.context.ContextLoader#configureAndRefreshWebApplicationContextspa

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
      if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
         // The application context id is still set to its original default value 應用程序上下文id仍然設置爲其原始默認值
 // -> assign a more useful id based on available information ->根據可用信息分配一個更有用的id
// 從servlet上下文中獲取contextId參數值
 String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
 if (idParam != null) {
            wac.setId(idParam);
 }
         else {
            // Generate default id...
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                  ObjectUtils.getDisplayString(sc.getContextPath()));
 }
      }

      wac.setServletContext(sc);
//從servlet上下文獲取contextConfigLocation參數值 spring上下文的配置文件
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
         wac.setConfigLocation(configLocationParam);
}

      // The wac environment's #initPropertySources will be called in any case when the context wac環境的#initPropertySources將在上下文中的任何狀況下被調用
// is refreshed; do it eagerly here to ensure servlet property sources are in place for 刷新;在這裏急切地確保servlet屬性源的位置是合適的嗎
// use in any post-processing or initialization that occurs below prior to #refresh 用於任何後處理或初始化中,發生在#刷新以前
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
// 初始化servlet參數 ->
 ((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
}

//定製化spring上下文 ->
customizeContext(sc, wac);
//刷新spring上下文,這裏在以前的spring源碼解析中有詳細介紹
wac.refresh();
 }

進入到這個方法org.springframework.web.context.ContextLoader#customizeContextdebug

protected void customizeContext(ServletContext sc, ConfigurableWebApplicationContext wac) {
//找到上下文初始化類 ->
List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses =
            determineContextInitializerClasses(sc);
//判斷上下文初始化類是不是ConfigurableWebApplicationContext類型
for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) {
         Class<?> initializerContextClass =
               GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
 if (initializerContextClass != null && !initializerContextClass.isInstance(wac)) {
            throw new ApplicationContextException(String.format(
                  "Could not apply context initializer [%s] since its generic parameter [%s] " +
                  "is not assignable from the type of application context used by this " +
                  "context loader: [%s]", initializerClass.getName(), initializerContextClass.getName(),
wac.getClass().getName()));
 }
// 初始化上下文初始化類
 this.contextInitializers.add(BeanUtils.instantiateClass(initializerClass));
}

      AnnotationAwareOrderComparator.sort(this.contextInitializers);
for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : this.contextInitializers) {
// 調用初始化器的初始化方法,初始化器能夠本身實現
 initializer.initialize(wac);
}
   }

進入到這個方法org.springframework.web.context.ContextLoader#determineContextInitializerClasses

protected List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>
         determineContextInitializerClasses(ServletContext servletContext) {

      List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> classes =
            new ArrayList<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>();
//從servlet上下文中獲取globalInitializerClasses參數值
String globalClassNames = servletContext.getInitParameter(GLOBAL_INITIALIZER_CLASSES_PARAM);
if (globalClassNames != null) {
         for (String className : StringUtils.tokenizeToStringArray(globalClassNames, INIT_PARAM_DELIMITERS)) {
//加載初始化類,多個類用,或者;分開
classes.add(loadInitializerClass(className));
 }
      }

//從servlet上下文獲取contextInitializerClasses參數值
String localClassNames = servletContext.getInitParameter(CONTEXT_INITIALIZER_CLASSES_PARAM);
if (localClassNames != null) {
         for (String className : StringUtils.tokenizeToStringArray(localClassNames, INIT_PARAM_DELIMITERS)) {
//加載初始化類,多個類用,或者;分開
classes.add(loadInitializerClass(className));
 }
      }

      return classes;
 }

往上返回到這個方法org.springframework.web.context.ContextLoaderListener#contextInitialized

 

說到最後

本次源碼解析僅表明我的觀點,僅供參考。

相關文章
相關標籤/搜索