作者:xlj3 原文:https://blog.csdn.net/luman1991/article/details/52229105
<listener>
<description>spring监听器</description>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:spring-servlet.xml,classpath:spring-application.xml
</param-value>
</context-param>
<context-param>
<param-name>charset</param-name>
<param-value>UTF-8</param-value>
</context-param>
由于ContextLoderListener实现了ServletContextListener,所以最先执行初始化方法contextInitianized进行初始化
initWebApplicationContext
通过对ContextLoaderListener的源码分析,我们看到ContextLoaderListener继承ContextLoader,所以ContextLoaderListener本身也是Spring的上下文加载器。
ContextLoaderListener实现了ServletContextListener接口,当Web应用在Web服务器中被被启动和停止时,Web服务器启动和停止事件会分别触发ContextLoaderListener的contextInitialized和contextDestroyed方法来初始化和销毁Spring上下文。
我们通过上述对ContextLoaderListener的源码分析看到真正实现Spring上下文的初始化和销毁功能的是ContextLoader类
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
private ContextLoader contextLoader;
/**
* Initialize the root web application context.
*/
public void contextInitialized(ServletContextEvent event) {
this.contextLoader = createContextLoader();
if (this.contextLoader == null) {
this.contextLoader = this;
}
this.contextLoader.initWebApplicationContext(event.getServletContext());
}
public void contextDestroyed(ServletContextEvent event) {
if (this.contextLoader != null) {
this.contextLoader.closeWebApplicationContext(event.getServletContext());
}
ContextCleanupListener.cleanupAttributes(event.getServletContext());
}
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 {
// Determine parent for root web application context, if any.
ApplicationContext parent = loadParentContext(servletContext);
// Store context in local instance variable, to guarantee that
// it is available on ServletContext shutdown.
this.context = createWebApplicationContext(servletContext, parent);
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;
}
}
ContextLoader初始化Spring Web上下文的主要源码分析,我们看到当Web应用在Web服务器启动时。
Servlet上下文通过web.xml文件获取所配置的contextConfigLocation、contextClass等参数,定位Spring配置文件资源,调用Spring Web容器的刷新的refresh()方法启动Web环境中Spring Ioc容器的初始化过程
protected WebApplicationContext createWebApplicationContext(ServletContext sc, ApplicationContext parent) {
Class<?> contextClass = determineContextClass(sc);
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
}
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
// Assign the best possible id value.
if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {
// Servlet <= 2.4: resort to name specified in web.xml, if any.
String servletContextName = sc.getServletContextName();
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(servletContextName));
}
else {
// Servlet 2.5's getContextPath available!
try {
String contextPath = (String) ServletContext.class.getMethod("getContextPath").invoke(sc);
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(contextPath));
}
catch (Exception ex) {
throw new IllegalStateException("Failed to invoke Servlet 2.5 getContextPath method", ex);
}
}
wac.setParent(parent);
wac.setServletContext(sc);
wac.setConfigLocation(sc.getInitParameter(CONFIG_LOCATION_PARAM));
customizeContext(sc, wac);
wac.refresh();
return wac;
}
//关闭指定Servlet上下文中的Spring Web应用上下文
public void closeWebApplicationContext(ServletContext servletContext) {
servletContext.log("Closing Spring root WebApplicationContext");
try {
//Spring Web应用上下文的类型都是ConfigurableWebApplicationContext
if (this.context instanceof ConfigurableWebApplicationContext) {
//关闭Spring Web应用上下文,释放资源,销毁所有缓存的单态模式Bean
((ConfigurableWebApplicationContext) this.context).close();
}
}
finally {
//获取当前线程的上下文类加载器
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
//将当前上下文对象设置为null
if (ccl == ContextLoader.class.getClassLoader()) {
currentContext = null;
}
//移除容器类加载器—>Web应用上下文Map集合中中key为指定类加载器的项
else if (ccl != null) {
currentContextPerThread.remove(ccl);
}
//移除Servlet上下文中Spring Web根上下文属性 servletContext.removeAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
//释放父容器引用
if (this.parentContextRef != null) {
this.parentContextRef.release();
}
}
}
public interface WebApplicationContext extends ApplicationContext {
//用于定义在Servlet上下文中存储Spring Web根上下文
String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
//请求范围
String SCOPE_REQUEST = "request";
//会话范围
String SCOPE_SESSION = "session";
//全局会话范围
String SCOPE_GLOBAL_SESSION = "globalSession";
//应用程序范围
String SCOPE_APPLICATION = "application";
//容器中存储的Servlet上下文环境的Bean名称
String SERVLET_CONTEXT_BEAN_NAME = "servletContext";
//web.xml文件中的配置Spring初始化参数的属性
String CONTEXT_PARAMETERS_BEAN_NAME = "contextParameters";
//Servlet上下文属性
String CONTEXT_ATTRIBUTES_BEAN_NAME = "contextAttributes";
//获取当前应用程序所以Web容器的标准Servlet上下文
ServletContext getServletContext();
}