1. 程式人生 > >IoC容器在Web容器中的啟動

IoC容器在Web容器中的啟動

簡單的說,在web容器中,通過ServletContext為Spring的IOC容器提供宿主環境,對應的建立起一個IOC容器的體系。其中,首先需要建立的是根上下文,這個上下文持有的物件可以有業務物件,資料存取物件,資源,事物管理器等各種中間層物件。在這個上下文的基礎上,和web MVC相關還會有一個上下文來儲存控制器之類的MVC物件,這樣就構成了一個層次化的上下文結構。在web容器中啟動Spring應用程式就是一個建立這個上下文體系的過程。Spring為web應用提供了上下文的擴充套件介面 
WebApplicationContext: 
Java程式碼  收藏程式碼
  1. public interface
     WebApplicationContext extends ApplicationContext {  
  2.     //這裡定義的常量用於在ServletContext中存取根上下文  
  3.     String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";  
  4.     ......  
  5.     //對WebApplicationContext來說,需要得到Web容器的ServletContext  
  6.     ServletContext getServletContext();  
  7. }  

而一般的啟動過程,Spring會使用一個預設的實現,XmlWebApplicationContext - 這個上下文實現作為在web容器中的根上下文容器被建立起來,具體的建立過程在下面我們會詳細分析。 
Java程式碼  收藏程式碼
  1. public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {  
  2.     /** 這是和web部署相關的位置資訊,用來作為預設的根上下文bean定義資訊的存放位置*/  
  3.     public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml"
    ;  
  4.     public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";  
  5.     public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";  
  6.     //我們又看到了熟悉的loadBeanDefinition,就像我們前面對IOC容器的分析中一樣,這個載入工程在容器的refresh()的時候啟動。  
  7.     protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException {  
  8.         //對於XmlWebApplicationContext,當然使用的是XmlBeanDefinitionReader來對bean定義資訊來進行解析  
  9.         XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);  
  10.         beanDefinitionReader.setResourceLoader(this);  
  11.         beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));  
  12.         initBeanDefinitionReader(beanDefinitionReader);  
  13.         loadBeanDefinitions(beanDefinitionReader);  
  14.     }  
  15.     protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {  
  16.     }  
  17.     //使用XmlBeanDefinitionReader來讀入bean定義資訊  
  18.     protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {  
  19.         String[] configLocations = getConfigLocations();  
  20.         if (configLocations != null) {  
  21.             for (int i = 0; i < configLocations.length; i++) {  
  22.                 reader.loadBeanDefinitions(configLocations[i]);  
  23.             }  
  24.         }  
  25.     }  
  26.     //這裡取得bean定義資訊位置,預設的地方是/WEB-INF/applicationContext.xml  
  27.     protected String[] getDefaultConfigLocations() {  
  28.         if (getNamespace() != null) {  
  29.             return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};  
  30.         }  
  31.         else {  
  32.             return new String[] {DEFAULT_CONFIG_LOCATION};  
  33.         }  
  34.     }  
  35. }  

對於一個Spring啟用的web應用程式,可以通過使用Spring程式碼宣告式的指定在web應用程式啟動時載入應用程式上下文(WebApplicationContext),Spring的ContextLoader是提供這樣效能的類,我們可以使用 ContextLoaderServlet或者ContextLoaderListener的啟動時載入的Servlet來例項化Spring IOC容器 - 為什麼會有兩個不同的類來裝載它呢,這是因為它們的使用需要區別不同的Servlet容器支援的Serlvet版本。但不管是 ContextLoaderSevlet還是 ContextLoaderListener都使用ContextLoader來完成實際的WebApplicationContext的初始化工作。這個ContextLoder就像是Spring Web應用程式在Web容器中的載入器booter。當然這些Servlet的具體使用我們都要藉助web容器中的部署描述符來進行相關的定義。 
下面我們使用ContextLoaderListener作為載入器作一個詳細的分析,這個Servlet的監聽器是根上下文被載入的地方,也是整個 Spring web應用載入上下文的第一個地方;從載入過程我們可以看到,首先從Servlet事件中得到ServletContext,然後可以讀到配置好的在web.xml的中的各個屬性值,然後ContextLoder例項化WebApplicationContext並完成其載入和初始化作為根上下文。當這個根上下文被載入後,它被繫結到web應用程式的ServletContext上。任何需要訪問該ApplicationContext的應用程式程式碼都可以從WebApplicationContextUtils類的靜態方法來得到: 
Java程式碼  收藏程式碼
  1. WebApplicationContext getWebApplicationContext(ServletContext sc)  

以Tomcat作為Servlet容器為例,下面是具體的步驟: 
1.Tomcat 啟動時需要從web.xml中讀取啟動引數,在web.xml中我們需要對ContextLoaderListener進行配置,對於在web應用啟動入口是在ContextLoaderListener中的初始化部分;從Spring MVC上看,實際上在web容器中維護了一系列的IOC容器,其中在ContextLoader中載入的IOC容器作為根上下文而存在於 ServletContext中。 
Java程式碼  收藏程式碼
  1. //這裡對根上下文進行初始化。  
  2. public void contextInitialized(ServletContextEvent event) {  
  3.     //這裡建立需要的ContextLoader  
  4.     this.contextLoader = createContextLoader();  
  5.     //這裡使用ContextLoader對根上下文進行載入和初始化  
  6.     this.contextLoader.initWebApplicationContext(event.getServletContext());  
  7. }  

通過ContextLoader建立起根上下文的過程,我們可以在ContextLoader中看到: 
Java程式碼  收藏程式碼
  1. public WebApplicationContext initWebApplicationContext(ServletContext servletContext)  
  2.         throws IllegalStateException, BeansException {  
  3.     //這裡先看看是不是已經在ServletContext中存在上下文,如果有說明前面已經被載入過,或者是配置檔案有錯誤。  
  4.     if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {  
  5.     //直接丟擲異常  
  6.     .........  
  7.     }  
  8.     ...............  
  9.     try {  
  10.         // 這裡載入根上下文的父上下文  
  11.         ApplicationContext parent = loadParentContext(servletContext);  
  12.         //這裡建立根上下文作為整個應用的上下文同時把它存到ServletContext中去,注意這裡使用的ServletContext的屬性值是  
  13.         //ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,以後的應用都是根據這個屬性值來取得根上下文的 - 往往作為自己上下文的父上下文  
  14.         this.context = createWebApplicationContext(servletContext, parent);  
  15.         servletContext.setAttribute(  
  16.                 WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);  
  17.         ..........  
  18.         return this.context;  
  19.     }  
  20.        ............  
  21. }  

建立根上下文的父上下文使用的是下面的程式碼,取決於在web.xml中定義的引數:locatorFactorySelector,這是一個可選引數: 
Java程式碼  收藏程式碼
  1. protected ApplicationContext loadParentContext(ServletContext servletContext)  
  2.         throws BeansException {  
  3.     ApplicationContext parentContext = null;  
  4.     String locatorFactorySelector = servletContext.getInitParameter(LOCATOR_FACTORY_SELECTOR_PARAM);  
  5.     String parentContextKey = servletContext.getInitParameter(LOCATOR_FACTORY_KEY_PARAM);  
  6.     if (locatorFactorySelector != null) {  
  7.         BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);  
  8.         ........  
  9.         //得到根上下文的父上下文的引用  
  10.         this.parentContextRef = locator.useBeanFactory(parentContextKey);  
  11.         //這裡建立得到根上下文的父上下文  
  12.         parentContext = (ApplicationContext) this.parentContextRef.getFactory();  
  13.     }  
  14.     return parentContext;  
  15. }  

得到根上下文的父上下文以後,就是根上下文的建立過程: 
Java程式碼  收藏程式碼
  1. protected WebApplicationContext createWebApplicationContext(  
  2.         ServletContext servletContext, ApplicationContext parent) throws BeansException {  
  3.     //這裡需要確定我們載入的根WebApplication的型別,由在web.xml中配置的contextClass中配置的引數可以決定我們需要載入什麼樣的ApplicationContext,  
  4.     //如果沒有使用預設的。  
  5.     Class contextClass = determineContextClass(servletContext);  
  6.     .........  
  7.     //這裡就是上下文的建立過程  
  8.     ConfigurableWebApplicationContext wac =  
  9.             (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);  
  10.     //這裡保持對父上下文和ServletContext的引用到根上下文中  
  11.     wac.setParent(parent);  
  12.     wac.setServletContext(servletContext);  
  13.     //這裡從web.xml中取得相關的初始化引數  
  14.     String configLocation = servletContext.getInitParameter(CONFIG_LOCATION_PARAM);  
  15.     if (configLocation != null) {  
  16.         wac.setConfigLocations(StringUtils.tokenizeToStringArray(configLocation,  
  17.                 ConfigurableWebApplicationContext.CONFIG_LOCATION_DELIMITERS));  
  18.     }  
  19.    //這裡對WebApplicationContext進行初始化,我們又看到了熟悉的refresh呼叫。  
  20.     wac.refresh();  
  21.     return wac;  
  22. }  

初始化根ApplicationContext後將其儲存到SevletContext中去以後,這樣就建立了一個全域性的關於整個應用的上下文。這個根上下文會被以後的DispatcherServlet初始化自己的時候作為自己ApplicationContext的父上下文。這個在對 DispatcherServlet做分析的時候我們可以看看到。 

3.完成對ContextLoaderListener的初始化以後, Tomcat開始初始化DispatchServlet,- 還記得我們在web.xml中隊載入次序進行了定義。DispatcherServlet會建立自己的ApplicationContext,同時建立這個自己的上下文的時候會從ServletContext中得到根上下文作為父上下文,然後再對自己的上下文進行初始化,並最後存到 ServletContext中去供以後檢索和使用。 
可以從DispatchServlet的父類FrameworkServlet的程式碼中看到大致的初始化過程,整個ApplicationContext的建立過程和ContextLoder建立的過程相類似: 
Java程式碼  收藏程式碼
  1. protected final void initServletBean() throws ServletException, BeansException {  
  2.     .........  
  3.     try {  
  4.         //這裡是對上下文的初始化過程。  
  5.         this.webApplicationContext = initWebApplicationContext();  
  6.         //在完成對上下文的初始化過程結束後,根據bean配置資訊建立MVC框架的各個主要元素  
  7.         initFrameworkServlet();  
  8.     }  
  9.    ........  
  10. }  

對initWebApplicationContext()呼叫的程式碼如下: 
Java程式碼  收藏程式碼
  1. protected WebApplicationContext initWebApplicationContext() throws BeansException {  
  2.     //這裡呼叫WebApplicationContextUtils靜態類來得到根上下文  
  3.     WebApplicationContext parent = WebApplicationContextUtils.getWebApplicationContext(getServletContext());  
  4.     //建立當前DispatcherServlet的上下文,其上下文種類使用預設的在FrameworkServlet定義好的:DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;  
  5.     WebApplicationContext wac = createWebApplicationContext(parent);  
  6.     ........  
  7.     if (isPublishContext()) {  
  8.         //把當前建立的上下文存到ServletContext中去,注意使用的屬性名是和當前Servlet名相關的。  
  9.         String attrName = getServletContextAttributeName();  
  10.         getServletContext().setAttribute(attrName, wac);  
  11.     }  
  12.     return wac;  
  13. }  

其中我們看到呼叫了WebApplicationContextUtils的靜態方法得到根ApplicationContext: 
Java程式碼  收藏程式碼
  1.     public static WebApplicationContext getWebApplicationContext(ServletContext sc) {  
  2.         //很簡單,直接從ServletContext中通過屬性名得到根上下文  
  3.         Object attr = sc.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);  
  4.         .......  
  5.         return (WebApplicationContext) attr;  
  6.     }  
  7. 然後建立DispatcherServlet自己的WebApplicationContext:  
  8.     protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent)  
  9.             throws BeansException {  
  10.         .......  
  11.         //這裡使用了BeanUtils直接得到WebApplicationContext,ContextClass是前面定義好的DEFAULT_CONTEXT_CLASS =                             
  12.         //XmlWebApplicationContext.class;  
  13.         ConfigurableWebApplicationContext wac =  
  14.                 (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(getContextClass());  
  15.         //這裡配置父上下文,就是在ContextLoader中建立的根上下文  
  16.         wac.setParent(parent);  
  17.         //保留ServletContext的引用和相關的配置資訊。  
  18.         wac.setServletContext(getServletContext());  
  19.         wac.setServletConfig(getServletConfig());  
  20.         wac.setNamespace(getNamespace());  
  21.         //這裡得到ApplicationContext配置檔案的位置  
  22.         if (getContextConfigLocation() != null) {  
  23. 相關推薦

    各種容器與伺服器的區別與聯絡:Servlet容器 WEB容器 Java EE容器 應用伺服器 WEB伺服器 Java EE伺服器

    各種容器與伺服器的區別與聯絡   Servlet容器 WEB容器 Java EE容器 應用伺服器 WEB伺服器  Java EE伺服器        平時我們經常看到各種容器名稱:Servlet容器、WEB容器、Java WEB容器、Java EE容器等,還有各種伺服

    maven web 專案啟動報錯 Java.lang.ClassNotFoundException: org.springframework.web.servlet.DispatcherServlet

    主要原因是maven專案裡面的jar包嗎,沒有匯入到專案中 maven web 專案中啟動報錯  Java.lang.ClassNotFoundException: org.springframework.web.servlet.DispatcherServlet 錯誤描述: Java.lang.Cl

    web專案啟動一個執行緒,的方法

    JavaWeb 服務啟動時,在後臺啟動載入一個執行緒。 目前,我所掌握的一共有兩種方法,第一種是監聽(Listener),第二種是配置隨專案啟動而啟動的Servlet。 下面對這兩種方法做一簡單的介紹,(Mark一下,防止以後急用又忘記了): 1.監聽(Listener) 首先,我們建立

    容器 web容器 EJB容器 web伺服器 web應用伺服器

    容器: 容器是一種服務呼叫規範框架,J2EE大量運用了容器和元件技術來構建分層的企業級應用。在J2EE規範中,相應的有WEB Container和EJB Container等。容器提供系統級的服務呼叫。 WEB容器: 給處於其中的應用程式元件(

    通過自定義URL協議在Web網頁啟動本地應用程式

        在做web應用的時候,我們經常會遇到在web中呼叫本地應用程式的問題,例如在web中點選一個按鈕,然後開啟自己寫的或者別人的應用程式。上網比較留意的同學應該會發現,想qq客服,淘寶的阿里旺旺客服都會有這樣的效果。     下面我主要介紹一種現在主流的處理方法,這種方

    IoC容器Web容器啟動

    簡單的說,在web容器中,通過ServletContext為Spring的IOC容器提供宿主環境,對應的建立起一個IOC容器的體系。其中,首先需要建立的是根上下文,這個上下文持有的物件可以有業務物件,資料存取物件,資源,事物管理器等各種中間層物件。在這個上下文的基礎上,和w

    Spring MVC原理之Spring應用上下文(IoC容器)在Web容器啟動分析

    Spring IoC是一個獨立的模組,它並不是直接在Web容器中發揮作用的。如果要在Web環境中使用IoC容器,需要Spring為IoC設計一個啟動過程,把IoC容器導人,並在Web容器中建立起來。具體說來,這個啟動過程是和Web容器的啟動過程整合在一起的

    web.xml配置監聽器來控制ioc容器生命週期

    、整合關鍵-在web.xml中配置監聽器來控制ioc容器生命週期 原因: 1、配置的元件太多,需保障單例項 2、專案停止後,ioc容器也需要關掉,降低對記憶體資源的佔用。 專案啟動建立容器,專案停止銷燬容器。 利用ServletContextListener監控專案來控制。 Spring

    Web專案配置Spring的IOC容器

    如果需要在Web專案中使用Spring的IOC容器,可以在Web專案配置檔案web.xml中做出如下配置: <listener> <listener-class>org.springframework.web.context.Contex

    如何在Web專案配置Spring的IOC容器

    如何在Web專案中配置Spring的IOC容器? 如果需要在Web專案中使用Spring的IOC容器,可以在Web專案配置檔案web.xml中做出如下配置: <listener> <listener-class>org.springframework.w

    Spring MVC 根應用上下文在Web容器啟動及其銷燬

    public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { //判斷servletContext是否已有根上下文 if (servletContext.getAttribute(WebApp

    web.xml配置Spring容器隨專案啟動

     <!-- 可以讓spring容器隨專案的啟動而建立,隨專案的關閉而銷燬 -->  <listener>  <listener-class>org.springframework.web.context.ContextLoaderListe

    web專案如何啟動spring容器

    1.在web.xml配置spring配置檔案的資訊 <context-param> <param-name>contextConfigLocation</param-name> <param-value>clas

    docker容器啟動service服務 Failed to get D-Bus connection: Operation not permitted

    docker容器中啟動service服務 failed to get d-bus connection: operation not permitted解決方案:啟動時設置參數 --privileged使用該參數,container內的root擁有真正的root權限。否則,container內的root只是

    docker啟動關閉刪除所有的容器命令

    讀取 star -a 命令 啟動 表示 docke dock ima docker中 啟動所有的容器命令 docker start $(docker ps -a | awk ‘{ print $1}‘ | tail -n +2) docker中 關閉所有的容器命令 d

    web容器向jar尋找資源

    示例程式碼下載地址 https://files.cnblogs.com/files/Mike_Chang/web%E5%AE%B9%E5%99%A8%E5%90%91jar%E4%B8%AD%E5%AF%BB%E6%89%BE%E8%B5%84%E6%BA%90.rar 具體步驟  1、在

    Servlet容器Tomcatweb.xmlurl-pattern的配置詳解[附帶原始碼分析

    轉載 http://www.cnblogs.com/fangjian0423/p/servletContainer-tomcat-urlPattern.html#springmvc Servlet容器Tomcat中web.xml中url-pattern的配置詳解[附帶原始碼分析 目錄 前

    Web容器 與Spring容器 啟動

    spring容器是如何在web應用中得到應用的,web容器又是如何以及何時啟動spring容器的呢?     話不多說,看web.xml中幾段配置:       <!---①從類路徑下載入spring配置

    spring-web:在web應用即使用監聽器建立spring容器物件

    spring-web2:在web應用中,使用spring。spring管理Service,Dao物件,而servlet、JSP、監聽器物件是由Tomcat生成的 web專案中使用spring的問題: 1.容器物件建立多次了, 應該是建立一次 (在應用啟動的時候建立一次) 2.容器物件需要

    Web容器自動對HTTP請求引數進行URLDecode處理

                         如題,在Java中也許很多人都沒有注意到當我們傳送一個http請求時,如果附帶的引數被URLEncode之後,到達web容器之後,開發者獲取到的引數值會自動變成了encode之前的值。這是一個很好的特點,開發者完全可以忽略http的引數是否需要decode這種事,但是