Spring源码学习之一 ContextLoaderListener及XmlWebApplicationContext

在web工程中,通过在web.xml中配置ContextLoaderListener来启动配置spring容器,常用配置实例如下:

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            classpath*:/applicationContext.xml
            classpath*:/applicationContext-haze.xml
        </param-value>
    </context-param>
    <context-param>
        <param-name>spring.profiles.default</param-name>
        <param-value>production</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

其中contextConfigLocation指定了spring配置文件位置,spring.profiles.default指定了加载哪种profile, 一般常用profile有以下三种,production,develop和test, profile可以通过在配置文件中指定<bean profile="">元素来指定(profile命名可以随便起).

ContextLoaderListener类定义如下:

public class ContextLoaderListener extends ContextLoader implements ServletContextListener
从类定义可以看出,ContextLoaderListener继承ContextLoader并实现了ServletContextListener,所以在容器启动时候会调用如下代码:

@Override
public void contextInitialized(ServletContextEvent event) {
	initWebApplicationContext(event.getServletContext());
} 
initWebApplicationContext方法为ContextLoader中定义的方法,该方法即完成Spring ApplicationContext配置启动入口.
进入ContextLoader源码中可以看到在ContextLoader中有一个static静态代码块,代码如下:

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, ContextLoader.class);
			defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
		}
		catch (IOException ex) {
			throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
		}
	}
以上代码加载默认 ContextLoader.properties, 

打开该文件可以看到该文件中内容如下:

# Default WebApplicationContext implementation class for ContextLoader.
# Used as fallback when no explicit context implementation has been specified as context-param.
# Not meant to be customized by application developers.

org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
因在web环境中,Spring使用WebApplicationContext作为Spring上下文,故该文件指定了使用XmlWebApplicationContext这个类作为WebApplicationContext接口的实现,当然你也可以通过在web.xml中通过context-param指定自定义实现类,如下代码所示:

<context-param>
        <param-name>contextClass</param-name>
        <param-value>
            自定义WebApplicationContext实现类
        </param-value>
    </context-param>
下面看initWebApplicationContext(ServletContext servletContext)这个方法:

该方法主要用来完成Spring WebApplicationContext上下文配置启动工作,代码流程如下:

1.首先判断在servletContext中是否包含key=WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE的上下文信息,如果不存在则通过

createWebApplicationContext(servletContext)方法创建WebApplicationContext类的实例对象并保存在ContextLoader中的this.context变量中.
2.createWebApplicationContext(servletContext)方法中通过contextClass获取并实例化WebApplicationContext对象,默认即实例化ContextLoader.properties
中指定的org.springframework.web.context.support.XmlWebApplicationContext对象.
3.判断this.context是否属于ConfigurableWebApplicationContext对象,如果是则执行
configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc)
方法,因XmlWebApplicationContext实现了ConfigurableWebApplicationContext接口,所以该方法会执行,该方法代码如下:
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
			// -> assign a more useful id based on available information
			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);
		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
		// is refreshed; do it eagerly here to ensure servlet property sources are in place for
		// use in any post-processing or initialization that occurs below prior to #refresh
		ConfigurableEnvironment env = wac.getEnvironment();
		if (env instanceof ConfigurableWebEnvironment) {
			((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
		}

		customizeContext(sc, wac);
		wac.refresh();
	}
其中,设置WebApplicationContext的ID和configLocation以及初始化Enviroment对象,在该方法的最下一行通过调用wac.refresh()来完成实际上下文初始化工作.
reshresh()方法是在ConfigurableApplicationContext接口中定义的,具体实现是在AbstractApplicationContext类中实现.

XmlWebApplicationContext中主要完成了如下功能:
  • 默认Spring上下文配置文件路径"/WEB-INF/applicationContext.xml",当找不到自定义的configLocation时会去默认路径读取.
实现方法是在AbstractRefreshableConfigApplicationContext中的getConfigLocations()中,代码如下:
protected String[] getConfigLocations() {
		return (this.configLocations != null ? this.configLocations : getDefaultConfigLocations());
	}
在XmlWebApplicationContext中重写了getDefaultConfigLocations(),代码如下:
@Override
	protected String[] getDefaultConfigLocations() {
		if (getNamespace() != null) {
			return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};
		}
		else {
			return new String[] {DEFAULT_CONFIG_LOCATION};
		}
	}
  • 重写loadBeanDefinitions()方法,该方法在AbstractApplicationContext中的refresh()过程中调用,具体完成beanDefinition的读取加载工作.
@Override
	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		// Create a new XmlBeanDefinitionReader for the given BeanFactory.
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

		// Configure the bean definition reader with this context's
		// resource loading environment.
		beanDefinitionReader.setEnvironment(getEnvironment());
		beanDefinitionReader.setResourceLoader(this);
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

		// Allow a subclass to provide custom initialization of the reader,
		// then proceed with actually loading the bean definitions.
		initBeanDefinitionReader(beanDefinitionReader);
		loadBeanDefinitions(beanDefinitionReader);
	}

	/**
	 * Initialize the bean definition reader used for loading the bean
	 * definitions of this context. Default implementation is empty.
	 * <p>Can be overridden in subclasses, e.g. for turning off XML validation
	 * or using a different XmlBeanDefinitionParser implementation.
	 * @param beanDefinitionReader the bean definition reader used by this context
	 * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader#setValidationMode
	 * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader#setDocumentReaderClass
	 */
	protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {
	}

	/**
	 * Load the bean definitions with the given XmlBeanDefinitionReader.
	 * <p>The lifecycle of the bean factory is handled by the refreshBeanFactory method;
	 * therefore this method is just supposed to load and/or register bean definitions.
	 * <p>Delegates to a ResourcePatternResolver for resolving location patterns
	 * into Resource instances.
	 * @throws IOException if the required XML document isn't found
	 * @see #refreshBeanFactory
	 * @see #getConfigLocations
	 * @see #getResources
	 * @see #getResourcePatternResolver
	 */
	protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
		String[] configLocations = getConfigLocations();
		if (configLocations != null) {
			for (String configLocation : configLocations) {
				reader.loadBeanDefinitions(configLocation);
			}
		}
	}

在loadBeanDefinitions方法中首先实例化一个XmlBeanDefinitionReader对象,然后通过XmlBeanDefinitionReader对象的loadBeanDefinitions(String configLocation)
方法来完成加载工作.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值