本篇参考了【spring源码解析】,在此基础加ru个人理解。适合刚开始学习spring(mvc)的开发人员。
本人头一次写博客,如有不对或者不好的地方欢迎大家指正,让我们一起进步,为了money!!!
1. ContextLoaderListener简介
它的作用是启动web应用服务器时,自动装配ApplicationContext的配置信息。它实现了ServletContextListener
public class ContextLoaderListener extends ContextLoader implements ServletContextListener
在web.xml中配置监听器,启动容器时就会执行实现的方法。使用ServletContextListener接口,开发者能够在请求前向ServletContext中添加任意的对象。ServletContext在服务启动时初始化,在整个运行期间都是可见的,生命周期与应用的周期一致。
2. ContextLoaderListener源码分析
ContextLoaderListener实现了ServletContextListener接口
public interface ServletContextListener extends EventListener {
void contextInitialized(ServletContextEvent var1);
void contextDestroyed(ServletContextEvent var1);
}
ServletContextListener有2个方法,contextInitialized是在应用服务器启动时执行,contextDestroyed是在应用服务器销毁时执行。
ContextLoaderListener即重写了ServletContextListener的方法
/**
* 应用容器启动时执行
*/
@Override
public void contextInitialized(ServletContextEvent event) {
/**
* spring环境启动方法
*/
initWebApplicationContext(event.getServletContext());
}
/**
* 应用容器销毁时执行
*/
@Override
public void contextDestroyed(ServletContextEvent event) { closeWebApplicationContext(event.getServletContext()); ContextCleanupListener.cleanupAttributes(event.getServletContext());
}
initWebApplicationContext方法完成了spring的初始化,其中event.getServletContext()即上文提到的ServletContext对象,ServletContext其中有2个方法
Object getAttribute(String var1);
void setAttribute(String var1, Object var2);
我们可以在整个应用生命周期内通过ServletContext添加或获取自定义的对象。
我们继续看initWebApplicationContext方法做了什么工作,首先看源码
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) {
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;
}
}
initWebApplicationContext方法主要是创建了WebApplicationContext实例的一个功能框架,大致分为以下步骤
2.1 WebApplicationContext存在性验证。
在web.xml中只允许声明一次ContextLoaderListener。在spring中创建的WebApplicationContext实例会保存在ServletContext中以方便全局调用,key为 WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE。
2.2 WebApplicationContext实例创建
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);
}
首先获取实现WebApplicationContext的context class类,该contextClass可以在web.xml指定,也可以默认为ContextLoader.properties中的XmlWebApplicationContext
通过BeanUtils实例化该类
2.3 刷新上下文环境,进行配置文件的加载 configureAndRefreshWebApplicationContext 请参考ContextLoader源码
2.4 将实例保存到ServletContext中
2.5 映射当前的类加载器与webApplicationContext实例到全局变量currentContextPerThread
3.ContextLoaderListener在应用容器中使用
3.1 在web.xml中增加全局参数,指定spring 加载配置文件的路径
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/classes/applicationContext-*.xml
</param-value>
</context-param>
如果不指定,默认的路径是/WEB-INF/applicationContext.xml,参考XmlWebApplicationContext.DEFAULT_CONFIG_LOCATION
3.2 web.xml中增加ContextLoaderListener过滤器
<listener>
<listener-class> org.springframework.web.context.ContextLoaderListener </listener-class>
</listener>
这样我们就完成了spring环境的启动
下一篇我们将分析下DispatcherServlet