Spring源码(2)Context篇之WebApplicationContext

上一篇讲解了Spring加载Context的起源来自工程下web.xml配置的Listener

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

这一篇接着往下探索Spring Context的神秘…

上一篇讲到了ContextLoader类以及它的方法:initWebApplicationContext(),看方法名就知道是用来初始化一个WebApplicationContext

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
 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.
	 if (this.context == null) {
		 //且看这一句,用来生成一个WebApplicationContext
		 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
		  if (cwac.getParent() == null) {
		   // The context instance was injected without an explicit parent ->
			// determine parent for root web application context, if any.
			ApplicationContext parent = loadParentContext(servletContext);
			  cwac.setParent(parent);
			}
			configureAndRefreshWebApplicationContext(cwac, servletContext);
		  }
	 }
	 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;
  }
}

请注意上面源码中

this.context = createWebApplicationContext(servletContext);

用来创建一个WebApplicationContext,但如果你查看WebApplicationContext的源码,它是一个接口,那这里到底是创建的哪个实现类呢?
我们进行代码跟进,跟进createWebApplicationContext方法(debug一下很清楚)

protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
  Class<?> contextClass = determineContextClass(sc);
  if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
    throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
				"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
  }
  return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}

再次跟进方法determineContextClass

protected Class<?> determineContextClass(ServletContext servletContext) {
	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 {
		//这里就有意思了,这里是先获得要生成的context的类名
		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);
		}
	}
}

从上面的代码可以看出来,要获取要生成的context的类名的时候,用到了defaultStrategies变量,该变量就是ContextLoader类的静态变量

private static final Properties defaultStrategies;
  static {
	// Load default strategy implementations from properties file.
	// This is currently strictly internal and not meant to be customized
	// by application developers.
	try {
		//这里DEFAULT_STRATEGIES_PATH值为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());
	}
}

defaultStrategies变量的初始化过程可以看到,读取到了ContextLoader.properties文件,该文件跟ContextLoader类文件在同一目录下:
在这里插入图片描述

打开ContextLoader.properties文件

org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

看到这就很清楚了, 最终创建的WebApplicationContext,其实就是XmlWebApplicationContext实例, 对应项目起动日志一下:

十二月 26, 2018 11:00:16 上午 org.apache.catalina.core.ApplicationContext log
信息: Initializing Spring root WebApplicationContext
[2018-12-26 11:00:16  INFO org.springframework.web.context.ContextLoader:304] Root WebApplicationContext: initialization started
[2018-12-26 11:00:16  INFO org.springframework.web.context.support.XmlWebApplicationContext:583] Refreshing Root WebApplicationContext: startup date [Wed Dec 26 11:00:16 CST 2018]; root of context hierarchy
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值