spring啟動入口


spring啟動入口

(44條消息) spring啟動入口_lieyanhaipo的專欄-CSDN博客_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. }  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM