spring启动入口

一、Spring与WEB容器整合   

web项目中,Spring启动是在web.xml配置监听器,如下所示: 

Xml代码   收藏代码
  1. <!-- 配置Spring上下文监听器 -->  
  2. <listener>  
  3.     <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
  4. </listener>  

 
  可以看看ContextLoaderListener类,它实现了Tomcat容器的ServletContextListener接口,所以它与普通的Servlet监听是一样的。同样是重写到两个方法:contextInitialized()方法在web容器初始化时执行,contextDestroyed()方法在容器销毁时执行。

    WEB容器启动时会触发初始化事件,ContextLoaderListener监听到这个事件,其contextInitialized()方法会被调用,在这个方法中Spring会初始化一个根上下文,即WebApplicationContext。这是一个接口,其实际默认实现类是XmlWebApplicationContext。这个就是Spring IOC的容器,其对应bean定义的配置信息由web.xml中的context-param来指定

Xml代码   收藏代码
  1. <!-- 配置Spring配置文件路径 -->  
  2. <context-param>  
  3.     <param-name>contextConfigLocation</param-name>  
  4.     <param-value>classpath*:applicationContext.xmlclasspath*:applicationContext-shiro.xml  
  5.         </param-value>  
  6. </context-param>  

 在Spring IOC 容器启动初始化完毕之后,会将其储存到ServletContext中。形式如下:servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, WebApplicationContext context);



 
注:以前我们写Servlet程序的时候,如果在Tomcat容器启动的时候需要绑定一些参数或者做一些全局处理,那么就需要实现ServletContextListener接口,在contextInitialized方法中编写必要的代码。然后在web.xml添加配置listener

 在ContextLoaderListener类中,只是实现了ServletContextListener提供的到两个方法,Spring启动主要的逻辑在父类ContextLoader的方法initWebApplicationContext实现。ContextLoaderListener的作用就是启动web容器时自动装配ApplicationContext的配置信息。更细化一点讲,Spring的启动过程其实就是Spring IOC容器的启动过程。

 

Java代码   收藏代码
  1. public class ContextLoaderListener extends ContextLoader implements ServletContextListener {  
  2.   
  3.     /** 
  4.      * Initialize the root web application context. 
  5.      */  
  6.     @Override  
  7.     public void contextInitialized(ServletContextEvent event) {  
  8.         initWebApplicationContext(event.getServletContext());  
  9.     }  
  10.   
  11.     /** 
  12.      * Close the root web application context. 
  13.      */  
  14.     @Override  
  15.     public void contextDestroyed(ServletContextEvent event) {  
  16.         closeWebApplicationContext(event.getServletContext());  
  17.         ContextCleanupListener.cleanupAttributes(event.getServletContext());  
  18.     }  
  19. }  

 

 二、ContextLoader剖析

  从上一部分ContextLoaderListener类可以得知,ContextLoader实际执行Spring容器的初始化,Spring整个的配置工作都是在ContextLoader完成的,这里参数ServletContextEvent由web容器提供,不做说明。ContextLoaderListener很好理解,所以我们主要看ContextLoader类。用Maven引入Spring的源码,打开ContextLoader类,类注释的第一行就是

Xml代码   收藏代码
  1. /**  
  2.  * Performs the actual initialization work for the root application context.  
  3.   ......  
  4. **/  

 用google翻译:实际执行根应用上下文的初始化工作。这里的根应用上下文就是上文所写的WebApplicationContext。我们先看看ContextLoader的时序图

 ContextLoader类initWebApplicationContext()方法

Java代码   收藏代码
  1. /** 
  2.  * Initialize Spring's web application context for the given servlet context, 
  3.  * using the application context provided at construction time, or creating a new one 
  4.  * according to the "{@link #CONTEXT_CLASS_PARAM contextClass}" and 
  5.  * "{@link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params. 
  6.  * @param servletContext current servlet context 
  7.  * @return the new WebApplicationContext 
  8.  * @see #ContextLoader(WebApplicationContext) 
  9.  * @see #CONTEXT_CLASS_PARAM 
  10.  * @see #CONFIG_LOCATION_PARAM 
  11.  */  
  12. public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {  
  13.     //判断ServletContext是否已经存在WebApplication,如果存在则抛出异常  
  14.     if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {  
  15.         throw new IllegalStateException(  
  16.                 "Cannot initialize context because there is already a root application context present - " +  
  17.                 "check whether you have multiple ContextLoader* definitions in your web.xml!");  
  18.     }  
  19.   
  20.     Log logger = LogFactory.getLog(ContextLoader.class);  
  21.     servletContext.log("Initializing Spring root WebApplicationContext");  
  22.     if (logger.isInfoEnabled()) {  
  23.         logger.info("Root WebApplicationContext: initialization started");  
  24.     }  
  25.     long startTime = System.currentTimeMillis();  
  26.   
  27.     try {  
  28.         // Store context in local instance variable, to guarantee that  
  29.         // it is available on ServletContext shutdown.  
  30.         if (this.context == null) {  
  31.             //创建WebApplicationContext(下文有说明)  
  32.             this.context = createWebApplicationContext(servletContext);  
  33.         }  
  34.         if (this.context instanceof ConfigurableWebApplicationContext) {  
  35.             ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;  
  36.             if (!cwac.isActive()) {  
  37.                 // The context has not yet been refreshed -> provide services such as  
  38.                 // setting the parent context, setting the application context id, etc  
  39.                 if (cwac.getParent() == null) {  
  40.                     // 得到根上下文的父上下文,然后设置到根上下文 。一般的web项目parent为空  
  41.                     ApplicationContext parent = loadParentContext(servletContext);  
  42.                     cwac.setParent(parent);  
  43.                 }  
  44.                 //从web.xml加载参数,初始化跟上下文WebApplicationContext,创建bean工厂和bean对象。  
  45.                 //这个过程比较麻烦,下一篇文章专门分析  
  46.                 configureAndRefreshWebApplicationContext(cwac, servletContext);  
  47.             }  
  48.         }  
  49.         servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);  
  50.   
  51.         ClassLoader ccl = Thread.currentThread().getContextClassLoader();  
  52.         if (ccl == ContextLoader.class.getClassLoader()) {  
  53.             currentContext = this.context;  
  54.         }  
  55.         else if (ccl != null) {  
  56.             currentContextPerThread.put(ccl, this.context);  
  57.         }  
  58.   
  59.         if (logger.isDebugEnabled()) {  
  60.             logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +  
  61.                     WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");  
  62.         }  
  63.         if (logger.isInfoEnabled()) {  
  64.             long elapsedTime = System.currentTimeMillis() - startTime;  
  65.             logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");  
  66.         }  
  67.   
  68.         return this.context;  
  69.     }  
  70.     catch (RuntimeException ex) {  
  71.         logger.error("Context initialization failed", ex);  
  72.         servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);  
  73.         throw ex;  
  74.     }  
  75.     catch (Error err) {  
  76.         logger.error("Context initialization failed", err);  
  77.         servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);  
  78.         throw err;  
  79.     }  
  80. }  

     

    在这个方法中ServletContext是由web容器监听器(ContextLoaderListener)提供。首先判断servlectContext中是否已经存在根上下文,如果存在,则抛出异常;否则通过createWebApplicationContext方法创建新的根上下文。然后通过loadParentContext()方法为其设置父上下文。再通过configureAndRefreshWebApplicationContext为根上下文构建bean工厂和bean对象。 最后把上下文存入servletContext,并且存入currentContextPerThread。至此初始化过程完毕,接下来可以获取WebApplicationContext,进而用getBean("bean name")得到bean。

 

createWebApplicationContext()方法用来创建根上下文:

Java代码   收藏代码
  1. protected WebApplicationContext createWebApplicationContext(ServletContext sc) {  
  2.     //从web.xml配置的contextClass参数中获取上下文类名,如果contextClass为空,则使用默认的。  
  3.         //下文有说明  
  4.     Class<?> contextClass = determineContextClass(sc);  
  5.         //根上下文必须是ConfigurableWebApplicationContext的子类,否则抛出异常  
  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.     //BeanUtils.instantiateClass工具方法,根据类名创建类  
  11.     return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);  
  12. }  
 

determineContextClass()方法返回根上下文的类名

Java代码   收藏代码
  1. protected Class<?> determineContextClass(ServletContext servletContext) {  
  2.     //从web.xml获得参数contextClass,在一般的web项目中,此参数为null  
  3.     String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);  
  4.     if (contextClassName != null) {  
  5.         try {  
  6.             return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());  
  7.         }  
  8.         catch (ClassNotFoundException ex) {  
  9.             throw new ApplicationContextException(  
  10.                     "Failed to load custom context class [" + contextClassName + "]", ex);  
  11.         }  
  12.     }  
  13.     else {  
  14.         //获得根上下文WebApplicationContext的默认实现类的类名,defaultStrategies是Properties类型,  
  15.         //在CotnextLoader类开头static语句块中初始化  
  16.         contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());  
  17.         try {  
  18.             return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());  
  19.         }  
  20.         catch (ClassNotFoundException ex) {  
  21.             throw new ApplicationContextException(  
  22.                     "Failed to load default context class [" + contextClassName + "]", ex);  
  23.         }  
  24.     }  
  25. }  
 

WebApplicationContext默认实现类的类名获取

Java代码   收藏代码
  1. static {  
  2.     try {  
  3.         //获取当前包下面的ContextLoader.properties文件,文件内容是:  
  4.         //org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext  
  5.         ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);  
  6.         defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);  
  7.     }  
  8.     catch (IOException ex) {  
  9.         throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());  
  10.     }  
  11. }  
  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值