深入分析ContextLoaderListener

来源:互联网 发布:软件的缺陷等级 编辑:程序博客网 时间:2024/06/05 03:57
本篇参考了【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的方法
/*** 应用容器启动时执行*/@Overridepublic void contextInitialized(ServletContextEvent event) {     /**     * spring环境启动方法     */     initWebApplicationContext(event.getServletContext());}/*** 应用容器销毁时执行*/@Overridepublic 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


1 0
原创粉丝点击