Spring启动<一>——ContextLoaderListener

 org.springframework.web.context.ContextLoaderListener继承自org.springframework.web.context.ContextLoader,同时实现了javax.servlet.ServletContextListener接口,因此在系统启动的时候,首先会调用ContextLoaderListener方法中的public void contextInitialized(ServletContextEvent event)方法,该方法源代码如下所示:

Java代码   收藏代码
  1. private ContextLoader contextLoader;  
  2.   
  3.   
  4.     /** 
  5.      * Initialize the root web application context. 
  6.      */  
  7.     public void contextInitialized(ServletContextEvent event) {  
  8.         this.contextLoader = createContextLoader();  
  9.         if (this.contextLoader == null) {  
  10.             this.contextLoader = this;  
  11.         }  
  12.         this.contextLoader.initWebApplicationContext(event.getServletContext());  
  13.     }  

      ContextLoaderListener基本上是对ContextLoader的代理。在该方法的第一步是创建一个ContextLoader,通过查看源代码知道,该方法createContextLoader()是个Deprecated的方法,这一步返回值为null。最重要的是这最后一步:

Java代码   收藏代码
  1. this.contextLoader.initWebApplicationContext(event.getServletContext());  

      这一步直接调用了其父类中的public WebApplicationContext initWebApplicationContext(ServletContext servletContext)方法。该方法通过web.xml配置文件中contextClass参数和contextConfigLocation的值来初始化给定的ServletContext。我们来看看这个方法做了哪些工作。下面代码中刨除了日志相关代码,只保留了最核心的操作:

Java代码   收藏代码
  1. public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {  
  2.     if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {  
  3.         throw new IllegalStateException(  
  4.                 "Cannot initialize context because there is already a root application context present - " +  
  5.                 "check whether you have multiple ContextLoader* definitions in your web.xml!");  
  6.     }  
  7.   
  8.     try {  
  9.         // 第一步:Determine parent for root web application context, if any.  
  10.         ApplicationContext parent = loadParentContext(servletContext);  
  11.   
  12.         // 第二步:Store context in local instance variable, to guarantee that  
  13.         // it is available on ServletContext shutdown.  
  14.         this.context = createWebApplicationContext(servletContext, parent);  
  15.   
  16.                 //第三步  
  17.         servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);  
  18.                  
  19.         ClassLoader ccl = Thread.currentThread().getContextClassLoader();  
  20.         if (ccl == ContextLoader.class.getClassLoader()) {  
  21.             currentContext = this.context;  
  22.         }  
  23.         else if (ccl != null) {  
  24.             currentContextPerThread.put(ccl, this.context);  
  25.         }  
  26.   
  27.         return this.context;  
  28.     }  
  29.     catch (RuntimeException ex) {  
  30.         servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);  
  31.         throw ex;  
  32.     }  
  33.     catch (Error err) {  
  34.         servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);  
  35.         throw err;  
  36.     }  
  37.     }  

     跳过前面的检测部分,我们直接看第一步,这一步调用了ContextLoader类中另外一个方法以判断是否存在root context的parent context,查看该方法的注释,有这么一句话:“For pure web applications, there is usually no need to worry about
having a parent context to the root web application context.”通过注释我们知道,一般情况下我们是用不到parent context,因此也不用考虑parent context,因此这一步我们得到的返回值会是null。(对于使用parent context的情况日后再去研究)。

    且看第二步和第三步,分别是创建context和保存context到本地实例变量。跳过第二步,我们先看第三步。

 

Java代码   收藏代码
  1. servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);  

    上面的代码将context的引用保存到了ServletContext中,从而保证其他对象都消亡而ServletContext还存在的时候context仍然是可见的。

    下面来看关键的第二步。第二步中调用了另外一个方法,其核心源代码如下:

Java代码   收藏代码
  1. protected WebApplicationContext createWebApplicationContext(ServletContext sc, ApplicationContext parent) {  
  2.         //第一步  
  3.                 Class<?> contextClass = determineContextClass(sc);  
  4.   
  5.                 //第二步   
  6.         if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {  
  7.             throw new ApplicationContextException("Custom context class [" + contextClass.getName() +  
  8.                     "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");  
  9.         }  
  10.   
  11.                 //第三步  
  12.         ConfigurableWebApplicationContext wac =  
  13.                 (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);  
  14.   
  15.         //Assign the best possible id value.  
  16.         if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {  
  17.             // Servlet <= 2.4: resort to name specified in web.xml, if any.  
  18.             String servletContextName = sc.getServletContextName();  
  19.             wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +  
  20.                     ObjectUtils.getDisplayString(servletContextName));  
  21.         }  
  22.         else {  
  23.             // Servlet 2.5's getContextPath available!  
  24.             try {  
  25.                 String contextPath = (String) ServletContext.class.getMethod("getContextPath").invoke(sc);  
  26.                 wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +  
  27.                         ObjectUtils.getDisplayString(contextPath));  
  28.             }  
  29.             catch (Exception ex) {  
  30.                 throw new IllegalStateException("Failed to invoke Servlet 2.5 getContextPath method", ex);  
  31.             }  
  32.         }  
  33.   
  34.         wac.setParent(parent);  
  35.         wac.setServletContext(sc);  
  36.         wac.setConfigLocation(sc.getInitParameter(CONFIG_LOCATION_PARAM));  
  37.         customizeContext(sc, wac);  
  38.         wac.refresh();  
  39.         return wac;  
  40.     }  

    我们且看第一步,这一步调用了determineContextClass方法,查看该方法的注释和源代码我们知道,这一步的意义在于返回系统设定的WebApplicationContext接口实现类,如果开发者没有指定该类,这默认使用org.springframework.web.context.support.XmlWebApplicationContext。context的实现类由web.xml中的contextClass参数指定。这一步我们假定返回的是系统的默认值。

    第二步是个判断语句,我们可以看出这一步用来判断第一步返回的Class是否实现了org.springframework.web.context.ConfigurableWebApplicationContext接口,该接口继承自org.springframework.web.context.WebApplicationContext和org.springframework.context.ConfigurableApplicationContext接口。

   第三步及之后的语句我们可以到,系统所做的是实例化org.springframework.web.context.support.XmlWebApplicationContext,并进行一些属性的设置。之后,整个启动过程也就完成了,下一篇我们将详细看看启动过程中,XmlWebApplicationContext实例化所做的工作以及设置属性时的工作。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值