深入分析ContextLoaderListener

本篇参考了【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


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值