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

 

说到最后

本次源码解析仅表明我的观点,仅供参考。

相关文章
相关标签/搜索