Spring 通篇源码 之 启动 之 ContextLoaderListener源码分析

欢迎大家关注本博,同时欢迎大家评论交流,可以给个赞哦!!!

Spring 通篇源码 基于 spring-framework-4.1.7.RELEASE 版本。

  ContextLoaderListener 源码

  ContextLoaderListener类实现很简单,主要针对ServletContextListener做了实现,详细的解析请查看下面代码部分注释。

package org.springframework.web.context;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

/**
 * 启动和关闭Spring的WebApplicationContext的主监听器.
 * 若存在Log4jConfigListener(org.springframework.web.util.Log4jConfigListener),
 * 	则ContextLoaderListener应在Log4jConfigListener之后注册.
 */
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
    
    

	/**
	 * 根据contextClass和contextConfigLocation配置创建WebApplicationContext.
	 * ContextLoader承担着ContextLoaderListener的业务实现.
	 * WebApplicationContext会会被注册到ServletContext的属性中,属性名为:
	 * 	WebApplicationContext的ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE属性.
	 * 	其生命周期同ServletContextListener的生命周期.
	 */
	public ContextLoaderListener() {
    
    
	}

	/**
	 * 使用传入的WebApplicationContext创建一个新的ContextLoaderListener.
	 * 此构造在Servlet3.0+环境中非常有用,可以通过javax.servlet.ServletContext的addListener添加.
	 * 传入的WebApplicationContext不一定被org.springframework.context.ConfigurableApplicationContext的refresh方法刷新.
	 * 如果传入的WebApplicationContext是ConfigurableWebApplicationContext的实现,且没有被刷新,那么会发生如下情况:
	 *  ·如果传入的WebApplicationContext没有被分配id,则会被分配id、ServletContext和ServletConfig委托给此上下文.
	 *  ·传入的WebApplicationContext对象的customizeContext方法被调用.
	 *  ·ApplicationContextInitializer通过contextInitializerClasses初始化参数指定.
	 *  ·org.springframework.context.ConfigurableApplicationContext的refresh方法被调用.
	 * 如果传入的WebApplicationContext没有被刷新或非ConfigurableWebApplicationContext的实现,
	 *  假设用户已根据其特定需求执行(或未执行)这些操作,则不会发生上述任何情况.
	 *  
	 * 无论哪种情况,传入的WebApplicationContext都会被注册到ServletContext的属性中,属性名为:
	 * 	WebApplicationContext的ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE属性.
	 *  将会在此监听器的contextDestroyed中关闭.
	 * <li>Any {@link org.springframework.context.ApplicationContextInitializer ApplicationContextInitializer}s
	 * specified through the "contextInitializerClasses" init-param will be applied.</li>
	 */
	public ContextLoaderListener(WebApplicationContext context) {
    
    
		super(context);
	}


	/**
	 * 初始化WebApplicationContext.
	 */
	@Override
	public void contextInitialized(ServletContextEvent event) {
    
    
		initWebApplicationContext(event.getServletContext());
	}


	/**
	 * 销毁WebApplicationContext.
	 */
	@Override
	public void contextDestroyed(ServletContextEvent event) {
    
    
		// 销毁WebApplicationContext.
		closeWebApplicationContext(event.getServletContext());
		// 清除实现了DisposableBean接口的Bean.
		ContextCleanupListener.cleanupAttributes(event.getServletContext());
	}

}

  ContextLoader 源码

  ContextLoaderListener的主要业务逻辑都集中在ContextLoader中,主要解决的问题就是如何初始化WebApplicationContext(其实谈不上初始化,因为整个过程中都是在根据配置选择和查找应该使用的WebApplicationContext实现类)。

package org.springframework.web.context;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.ServletContext;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.access.BeanFactoryLocator;
import org.springframework.beans.factory.access.BeanFactoryReference;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextException;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.access.ContextSingletonBeanFactoryLocator;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

/**
 * 执行根应用程序上下文的实际初始化工作.由调用ContextLoaderListener.
 *
 * web.xml中contex-param可配置contextClass参数名,指定WebApplicationContext的实现类.
 * 若未指定的情况下,使用的是org.springframework.web.context.support.XmlWebApplicationContext.
 * 无论自定义或默认的实现,都必须使用ConfigurableWebApplicationContext.
 * 
 * web.xml中contex-param可配置contextConfigLocation参数名,用来指定配置文件路径,配置格式可为:
 * 	WEB-INF/applicationContext1.xml, WEB-INF/applicationContext2.xml
 *  WEB-INF/*Context.xml,WEB-INF/spring*.xml
 *  WEB-INF/&#42;&#42;/*Context.xml
 * 如果没有指定时,默认是/WEB-INF/applicationContext.xml.
 *
 * Spring3.1版本开始,ContextLoader增加了ContextLoader(WebApplicationContext)构造方法,
 *  可以指定根WebApplicationContext.
 *  需要在Servlet API 3.x的版本之上.
 */
public class ContextLoader {
    
    

	/**
	 * WebApplicationContext的id参数名. 用作基础BeanFactory的序列化id.
	 */
	public static final String CONTEXT_ID_PARAM = "contextId";

	/**
	 * web.xml配置时的初始化参数名contextConfigLocation.
	 * 表示Spring配置文件路径.
	 */
	public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";

	/**
	 * WebApplicationContext实现类的配置参数.
	 */
	public static final String CONTEXT_CLASS_PARAM = "contextClass";

	/**
	 * web.xml配置时的初始化参数名contextInitializerClasses.
	 * 用来配置ApplicationContextInitializer,去初始化根WebApplicationContext.
	 */
	public static final String CONTEXT_INITIALIZER_CLASSES_PARAM = "contextInitializerClasses";

	/**
	 * web.xml配置时的初始化参数名globalInitializerClasses.
	 * 用来配置ApplicationContextInitializer,去初始化当前应用中所有WebApplicationContext.
	 */
	public static final String GLOBAL_INITIALIZER_CLASSES_PARAM = "globalInitializerClasses";

	/**
	 * 通过locatorFactorySelector上下文初始化参数指定父容器所在的配置文件路径.
	 * 默认文件路径是:classpath*:beanRefContext.xml.指定实现为ContextSingletonBeanFactoryLocator.
	 */
	public static final String LOCATOR_FACTORY_SELECTOR_PARAM = "locatorFactorySelector";

	/**
	 * 通过parentContextKey上下文初始化参数指定父容器的名称.
	 */
	public static final String LOCATOR_FACTORY_KEY_PARAM = "parentContextKey";

	/**
	 * 定义init-param参数中多个值之间的分隔符.
	 */
	private static final String INIT_PARAM_DELIMITERS = ",; \t\n";

	/**
	 * 定义ContextLoader默认策略名称的类路径资源(相对于ContextLoader类)的名称.
	 * 也就是ContextLoader类路径下的ContextLoader.properties.
	 */
	private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";

	/**
	 * 默认策略存储.
	 */
	private static final Properties defaultStrategies;

	static {
    
    
		// 加载默认策略.
		try {
    
    
			// 利用ClassPathResource加载资源.
			ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH,
					ContextLoader.class);
			// 加载properties文件.
			defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
		}
		catch (IOException ex) {
    
    
			throw new IllegalStateException(
					"Could not load 'ContextLoader.properties': " + ex.getMessage());
		}
	}

	/**
	 * 类加载器-WebApplicationContext的映射.
	 */
	private static final Map<ClassLoader, WebApplicationContext> currentContextPerThread = new ConcurrentHashMap<ClassLoader, WebApplicationContext>(
			1);

	/**
	 * 当前WebApplicationContext.
	 */
	private static volatile WebApplicationContext currentContext;

	/**
	 * 此加载程序管理的根WebApplicationContext实例.
	 */
	private WebApplicationContext context;

	/**
	 * 通过ContextSingletonBeanFactoryLocator加载父工厂是保留的BeanFactoryReference.
	 */
	private BeanFactoryReference parentContextRef;

	/**
	 * 根据contextClass和contextConfigLocation配置创建WebApplicationContext.
	 * ContextLoader承担着ContextLoaderListener的业务实现.
	 * WebApplicationContext会会被注册到ServletContext的属性中,属性名为:
	 * 	WebApplicationContext的ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE属性.
	 * 	其生命周期同ServletContextListener的生命周期.
	 */
	public ContextLoader() {
    
    
	}

	/**
	 * 使用传入的WebApplicationContext创建一个新的ContextLoaderListener.
	 * 此构造在Servlet3.0+环境中非常有用,可以通过javax.servlet.ServletContext的addListener添加.
	 * 传入的WebApplicationContext不一定被org.springframework.context.ConfigurableApplicationContext的refresh方法刷新.
	 * 如果传入的WebApplicationContext是ConfigurableWebApplicationContext的实现,且没有被刷新,那么会发生如下情况:
	 *  ·如果传入的WebApplicationContext没有被分配id,则会被分配id、ServletContext和ServletConfig委托给此上下文.
	 *  ·传入的WebApplicationContext对象的customizeContext方法被调用.
	 *  ·ApplicationContextInitializer通过contextInitializerClasses初始化参数指定.
	 *  ·org.springframework.context.ConfigurableApplicationContext的refresh方法被调用.
	 * 如果传入的WebApplicationContext没有被刷新或非ConfigurableWebApplicationContext的实现,
	 *  假设用户已根据其特定需求执行(或未执行)这些操作,则不会发生上述任何情况.
	 *  
	 * 无论哪种情况,传入的WebApplicationContext都会被注册到ServletContext的属性中,属性名为:
	 * 	WebApplicationContext的ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE属性.
	 *  将会在此监听器的contextDestroyed中关闭.
	 * <li>Any {@link org.springframework.context.ApplicationContextInitializer ApplicationContextInitializer}s
	 * specified through the "contextInitializerClasses" init-param will be applied.</li>
	 */
	public ContextLoader(WebApplicationContext context) {
    
    
		this.context = context;
	}

	/**
	 * 为给定的ServletContext初始化Spring的WebApplicationContext.
	 * 使用构造时提供的WebApplicationContext
	 * 	或根据contextClass和contextConfigLocation参数创建一个新的.
	 */
	public WebApplicationContext initWebApplicationContext(
			ServletContext servletContext) {
    
    
		// 已初始化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!");
		}
		// 获取Spring的Log框架实例.
		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 {
    
    
			// WebApplicationContext存在本地变量中,以便在ServletContext关闭时使用.
			if (this.context == null) {
    
    
				// 创建上下文类实例.ConfigurableWebApplicationContext或其子类.
				this.context = createWebApplicationContext(servletContext);
			}
			// context 是ConfigurableWebApplicationContext实例.
			if (this.context instanceof ConfigurableWebApplicationContext) {
    
    
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
				// 当前WebApplicationContext是否活动.
				if (!cwac.isActive()) {
    
    
					// 当前WebApplicationContext尚未refresh.
					if (cwac.getParent() == null) {
    
    
						// 上下文实例被注入时没有显式的父级-> 确定根web应用程序上下文的父级(如果有).
						ApplicationContext parent = loadParentContext(servletContext);
						cwac.setParent(parent);
					}
					// 配置且刷新WebApplicationContext.
					configureAndRefreshWebApplicationContext(cwac, servletContext);
				}
			}
			// WebApplicationContext设置到ServletContext中.
			servletContext.setAttribute(
					WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,
					this.context);
			// 获得线程上下文类加载器.
			ClassLoader ccl = Thread.currentThread().getContextClassLoader();
			// 线程上下文类加载器   是   ContextLoader使用的类加载器.
			if (ccl == ContextLoader.class.getClassLoader()) {
    
    
				// 设置为当前WebApplicationContext.
				currentContext = this.context;
			}
			// 线程上下文类加载器   不是   ContextLoader使用的类加载器.且线程上下文类加载器不为空时.
			else if (ccl != null) {
    
    
				// 存储到线程上下文类加载器与WebApplicationContext的映射中.
				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中设置的异常信息.
			servletContext.setAttribute(
					WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
			throw ex;
		}
		catch (Error err) {
    
    
			logger.error("Context initialization failed", err);
			// 发生异常时,ServletContext中设置的异常信息.
			servletContext.setAttribute(
					WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
			throw err;
		}
	}

	/**
	 * 实例化此加载程序的根WebApplicationContext,上下文类可以自行指定或使用默认策略配置的上下文类即:
	 * 	org.springframework.web.context.support.XmlWebApplicationContext.
	 * 配置的上下文类必须是ConfigurableWebApplicationContext或其子类.
	 * 
	 * 此外,customizeContext在刷新上下文之前被调用,允许子类对上下文执行自定义修改.
	 * @param sc ServletContext实例.
	 * @return 根WebApplicationContext.
	 */
	protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
    
    
		// 获取当前上下文类对象.
		Class<?> contextClass = determineContextClass(sc);
		// 如果获取的上下文类 不是ConfigurableWebApplicationContext类或其子类.抛出异常.
		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);
	}

	/**
	 * 返回要使用的WebApplicationContext实现类,可以是默认的XmlWebApplicationContext,也可以是自定义上下文类.
	 * @param servletContext 实例.
	 * @return 上下文类.
	 */
	protected Class<?> determineContextClass(ServletContext servletContext) {
    
    
		// 获取contextClass参数值.
		String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
		// 若contextClass不为空,则以contextClass为准.
		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 {
    
    
			// contextClass为空时,使用默认策略.
			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);
			}
		}
	}

	/**
	 * @deprecated 
	 * Spring 3.1 起已弃用.
	 */
	@Deprecated
	protected WebApplicationContext createWebApplicationContext(ServletContext sc,
			ApplicationContext parent) {
    
    
		return createWebApplicationContext(sc);
	}

	/**
	 * 配置且刷新WebApplicationContext.
	 * @param wac ConfigurableWebApplicationContext.
	 * @param sc ServletContext.
	 */
	protected void configureAndRefreshWebApplicationContext(
			ConfigurableWebApplicationContext wac, ServletContext sc) {
    
    
		// 若WebApplicationContext的id仍是默认值,则分配一个更有意义的值.
		if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
    
    
			// 初始化参数contextId作为WebApplicationContext的id.
			String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
			if (idParam != null) {
    
    
				wac.setId(idParam);
			}
			else {
    
    
				// 生成默认的id.
				wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX
						+ ObjectUtils.getDisplayString(sc.getContextPath()));
			}
		}
		// 将ServletContext设置到WebApplicationContext.
		wac.setServletContext(sc);
		// 获得初始化参数contextConfigLocation.
		String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
		// 若contextConfigLocation不为空,设置到WebApplicationContext.
		if (configLocationParam != null) {
    
    
			wac.setConfigLocation(configLocationParam);
		}
		// 当前ServletContext设置到WebApplicationContext中.
		ConfigurableEnvironment env = wac.getEnvironment();
		if (env instanceof ConfigurableWebEnvironment) {
    
    
			((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
		}
		// 定制
		customizeContext(sc, wac);
		// WebApplication刷新.
		wac.refresh();
	}

	/**
	 * 在配置位置已提供给上下文之后,但在上下文refresh之前,定制ConfigurableWebApplicationContext.
	 * 
	 * determineContextInitializerClasses确定实现.
	 *  由contextInitializerClasses和ApplicationContextInitializer的initialize方法完成.
	 * 
	 * 实现了org.springframework.core.Ordered的ApplicationContextInitializer
	 *  或被org.springframework.core.annotation.Order标记的ApplicationContextInitializer
	 *  都会被适当的排序.
	 * @param sc 当前ServletContext.
	 * @param wac WebApplicationContext.
	 */
	protected void customizeContext(ServletContext sc,
			ConfigurableWebApplicationContext wac) {
    
    
		// 获取ApplicationContextInitializer列表.
		List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses = determineContextInitializerClasses(
				sc);
		// ApplicationContextInitializer未定义时.
		if (initializerClasses.isEmpty()) {
    
    
			return;
		}
		
		ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerInstances = new ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>>();
		// 整理ApplicationContextInitializer类.
		for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) {
    
    
			Class<?> initializerContextClass = GenericTypeResolver.resolveTypeArgument(
					initializerClass, ApplicationContextInitializer.class);
			if (initializerContextClass != null) {
    
    
				Assert.isAssignable(initializerContextClass, wac.getClass(),
						String.format(
								"Could not add 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()));
			}
			initializerInstances.add(BeanUtils.instantiateClass(initializerClass));
		}
		// 排序.
		AnnotationAwareOrderComparator.sort(initializerInstances);
		// 调用每个ApplicationContextInitializer的initialize方法.
		for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : initializerInstances) {
    
    
			initializer.initialize(wac);
		}
	}

	/**
	 * 如果contextInitializerClasses指定了任何实现类,则返回要使用的ApplicationContextInitializer实现类.
	 * @param servletContext Servlet实例.
	 * @return 返回ContextInitializer列表.
	 */
	protected List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> determineContextInitializerClasses(
			ServletContext servletContext) {
    
    

		List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> classes = new ArrayList<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>();
		// 获取globalInitializerClasses初始化参数.
		String globalClassNames = servletContext.getInitParameter(
				GLOBAL_INITIALIZER_CLASSES_PARAM);
		// 若已配置globalInitializerClasses,则进行解析.
		if (globalClassNames != null) {
    
    
			// globalInitializerClasses以,; \t\n作为分隔符.
			for (String className : StringUtils.tokenizeToStringArray(globalClassNames,
					INIT_PARAM_DELIMITERS)) {
    
    
				classes.add(loadInitializerClass(className));
			}
		}

		// 获取contextInitializerClasses初始化参数.
		String localClassNames = servletContext.getInitParameter(
				CONTEXT_INITIALIZER_CLASSES_PARAM);
		// 若已配置contextInitializerClasses,则进行解析.
		if (localClassNames != null) {
    
    
			// contextInitializerClasses以,; \t\n作为分隔符.
			for (String className : StringUtils.tokenizeToStringArray(localClassNames,
					INIT_PARAM_DELIMITERS)) {
    
    
				classes.add(loadInitializerClass(className));
			}
		}

		return classes;
	}

	@SuppressWarnings("unchecked")
	private Class<ApplicationContextInitializer<ConfigurableApplicationContext>> loadInitializerClass(
			String className) {
    
    
		try {
    
    
			Class<?> clazz = ClassUtils.forName(className,
					ClassUtils.getDefaultClassLoader());
			Assert.isAssignable(ApplicationContextInitializer.class, clazz);
			return (Class<ApplicationContextInitializer<ConfigurableApplicationContext>>) clazz;
		}
		catch (ClassNotFoundException ex) {
    
    
			throw new ApplicationContextException(
					"Failed to load context initializer class [" + className + "]", ex);
		}
	}

	/**
	 * 具有默认实现(可能被子类重写)的模板方法,以加载或获取将用作根WebApplicationContext的父上下文的ApplicationContext实例.
	 * 如果方法的返回值为null,则不设置父上下文.
	 * 
	 * 在这里加载父上下文的主要原因是允许多个根web应用程序上下文都是共享EAR上下文的子上下文,或者也可以共享同一个对ejb可见的父上下文.
	 * 对于纯web应用程序,通常不需要担心根web应用程序上下文的父上下文.
	 * 
	 * 默认实现使用org.springframework.context.access.ContextSingletonBeanFactoryLocator,
	 * 通过locatorFactorySelector和parentContextKey配置,加载将由ContextsingletonBeanFactoryLocator
	 * 的所有其他用户共享的父上下文,该上下文也使用相同的配置参数.
	 * @param servletContext ServletContext实例.
	 * @return 父ApplicationContext,若没有,则返回null.
	 */
	protected ApplicationContext loadParentContext(ServletContext servletContext) {
    
    
		ApplicationContext parentContext = null;
		// 获取初始化参数locatorFactorySelector.
		String locatorFactorySelector = servletContext.getInitParameter(
				LOCATOR_FACTORY_SELECTOR_PARAM);
		// 获取初始化参数parentContextKey.
		String parentContextKey = servletContext.getInitParameter(
				LOCATOR_FACTORY_KEY_PARAM);
		// 配置parentContextKey的情况下.
		if (parentContextKey != null) {
    
    
			// locatorFactorySelector可能为null,表示默认的“类路径*: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;
	}

	/**
	 * 根据给定的ServletContext关闭Spring的WebApplicationContext.
	 * 如果使用loadParentContext(使用ContextSingletonBeanFactoryLocator的),则释放所有共享路径,释放所有信息.
	 * @param ServletContext实例.
	 */
	public void closeWebApplicationContext(ServletContext servletContext) {
    
    
		servletContext.log("Closing Spring root WebApplicationContext");
		try {
    
    
			if (this.context instanceof ConfigurableWebApplicationContext) {
    
    
				((ConfigurableWebApplicationContext) this.context).close();
			}
		}
		finally {
    
    
			ClassLoader ccl = Thread.currentThread().getContextClassLoader();
			if (ccl == ContextLoader.class.getClassLoader()) {
    
    
				currentContext = null;
			}
			else if (ccl != null) {
    
    
				currentContextPerThread.remove(ccl);
			}
			servletContext.removeAttribute(
					WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
			if (this.parentContextRef != null) {
    
    
				this.parentContextRef.release();
			}
		}
	}

	/**
	 * 返回当前线程对应的WebApplicationContext,若无则返回null.
	 * 
	 * @return WebApplicationContext 实例.
	 */
	public static WebApplicationContext getCurrentWebApplicationContext() {
    
    
		ClassLoader ccl = Thread.currentThread().getContextClassLoader();
		if (ccl != null) {
    
    
			WebApplicationContext ccpt = currentContextPerThread.get(ccl);
			if (ccpt != null) {
    
    
				return ccpt;
			}
		}
		return currentContext;
	}

}

  ContextLoader 重点内容

  1) web.xml中<context-param>配置参数名为contextId,其值作为WebApplicationContext的id值。

  2) web.xml中<context-param>配置参数名为contextClass,其值是ConfigurableWebApplicationContext的子类,用于创建WebApplicationContext。

  3) web.xml中<context-param>配置参数名为contextConfigLocation,其值作为Spring配置文件通配路径,可以存在多个路径,使用’,; \t\n’中的一个作为分隔符。

  4) web.xml中<context-param>配置参数名为contextInitializerClasses,其值是ApplicationContextInitializer的实现类,用于创建Web应用中当前WebApplicationContext的回调,此回调会在ConfigurableApplicationContext的refresh方法调用前被调用。可以配置多个类,使用’,; \t\n’中的一个作为分隔符。

  5) web.xml中<context-param>配置参数名为globalInitializerClasses,其值是ApplicationContextInitializer的实现类,用于创建Web应用中所有WebApplicationContext的回调,此回调会在ConfigurableApplicationContext的refresh方法调用前被调用。可以配置多个类,使用’,; \t\n’中的一个作为分隔符。

  globalInitializerClasses与contextInitializerClasses的差异仅仅在于,contextInitializerClasses针对的是当前的ServletContext以及WebApplicationContext,globalInitializerClasses针对的是Web应用下所有的ServletContext以及WebApplicationContext。

  6) web.xml中<context-param>配置参数名为locatorFactorySelector,其值为定义父容器的Xml配置文件地址。

  7) web.xml中<context-param>配置参数名为parentContextKey,其值为定义父容器名称的值。

  8) ContextLoader类路径下存在一个ContextLoader.properties文件,内容如下。当没有在web.xml中配置参数名contextClass时,会使用ContextLoader.properties中的org.springframework.web.context.support.XmlWebApplicationContext作为WebApplicationContextc初始化类。

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

  9) WebApplicationContext初始化过程中,会将ServletContext设置到WebApplicationContext实例中,在使用WebApplicationContext的地方,都可以获得ServletContext的实例。

  10) WebApplicationContext初始化之后,会使用WebApplicationContext.class.getName() + ".ROOT"作为键,存储在ServletContext中,这样只要Servlet中,都可以得到Spring的WebApplicationContext。若在初始化过程中出现异常,那么WebApplicationContext.class.getName() + ".ROOT"作为键,存储在ServletContext中的是异常堆栈。

  11) ContextLoader类内执行过程中(只在ContextLoader实现的部分),WebApplicationContext并没有获得很多属性,其设置的属性包括:parent、id、servletContext、configLocation、environment。

  12) WebApplicationContext初始化过程中,在调用ConfigurableWebApplicationContext的refresh方法前,存在一个扩展点ApplicationContextInitializer,可以对WebApplicationContext进行一些初始化操作。

  13) ServletContext销毁时,ContextLoader对应的closeWebApplicationContext方法负责清理数据,主要包括currentContext、currentContextPerThread、servletContext和parentContextRef。

  总结

  ContextLoaderListener作为监听,ContextLoader作为业务实现,这是Spring出现WebApplicationContext容器的入口,是配置的关键,在搭建Spring项目时是必不可少的。WebApplicationContext作为Ioc容器,负责着Spring的管理工作,是框架最重要的部分。

  若文中存在错误和不足,欢迎指正!

本博微信公众号“超哥说码”,欢迎大家订阅,公众号正在完善中,会及时将更优质的博文推送于您!

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/securitit/article/details/108112637
今日推荐