欢迎大家关注本博,同时欢迎大家评论交流,可以给个赞哦!!!
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/**/*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的管理工作,是框架最重要的部分。
若文中存在错误和不足,欢迎指正!
本博微信公众号“超哥说码”,欢迎大家订阅,公众号正在完善中,会及时将更优质的博文推送于您!