1. 程式人生 > >Spring原始碼淺析 -- XML配置檔案的載入與解析

Spring原始碼淺析 -- XML配置檔案的載入與解析

最近在看Spring原始碼,對配置檔案資訊的載入是使用Spring的第一步 ,而這第一步就是一個非常複雜的過程.... 
Spring通過定義BeanDefination來管理Ioc中的各種物件以及它們之間的依賴關係,所以載入的過程其實就是將XML檔案讀取並解析成BeanDefination資料的過程。
我們以最常使用的ClassPathXmlApplicationContext為切入點 

1. 建立一個ClassPathXmlApplicationContext物件,傳入檔案路徑

Java程式碼  收藏程式碼
  1. ClassPathXmlApplicationContext re = new
     ClassPathXmlApplicationContext("applicationContext.xml");  

這個構造方法會過載到

Java程式碼  收藏程式碼
  1. public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)  
  2.         throws BeansException {  
  3.     super(parent);  
  4.     setConfigLocations(configLocations);  
  5.     if (refresh) {  
  6.         refresh();  
  7.     }  
  8. }  

其中首先設定配置路徑 setConfigLocations(configLocations)  ,而後進行重新整理 refresh(), 而這個refresh()方法是Ioc容器初始化的入口

2.refresh方法的結構

 refresh方法由AbstractApplicationContext實現

Java程式碼  收藏程式碼
  1. public void refresh() throws BeansException, IllegalStateException {  
  2.         synchronized (this.startupShutdownMonitor) {  
  3.             // Prepare this context for refreshing.  
  4.             prepareRefresh();  
  5.             // Tell the subclass to refresh the internal bean factory.  
  6.             ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();  
  7.             // Prepare the bean factory for use in this context.  
  8.             prepareBeanFactory(beanFactory);  
  9.             try {  
  10.                 // Allows post-processing of the bean factory in context subclasses.  
  11.                 postProcessBeanFactory(beanFactory);  
  12.                 // Invoke factory processors registered as beans in the context.  
  13.                 invokeBeanFactoryPostProcessors(beanFactory);  
  14.                 // Register bean processors that intercept bean creation.  
  15.                 registerBeanPostProcessors(beanFactory);  
  16.                 // Initialize message source for this context.  
  17.                 initMessageSource();  
  18.                 // Initialize event multicaster for this context.  
  19.                 initApplicationEventMulticaster();  
  20.                 // Initialize other special beans in specific context subclasses.  
  21.                 onRefresh();  
  22.                 // Check for listener beans and register them.  
  23.                 registerListeners();  
  24.                 // Instantiate all remaining (non-lazy-init) singletons.  
  25.                 finishBeanFactoryInitialization(beanFactory);  
  26.                 // Last step: publish corresponding event.  
  27.                 finishRefresh();  
  28.             }  
  29.             catch (BeansException ex) {  
  30.                 // Destroy already created singletons to avoid dangling resources.  
  31.                 destroyBeans();  
  32.                 // Reset 'active' flag.  
  33.                 cancelRefresh(ex);  
  34.                 // Propagate exception to caller.  
  35.                 throw ex;  
  36.             }  
  37.         }  
  38.     }  

 這個方法中描述了ApplicationContext的整個初始化過程,包括BeanFactory的更新,還有messagesource以及一些生命週期有關屬性的註冊,而我們關心的是BeanFactory的更新,即obtainFreshBeanFactory()方法

3.啟動對BeanDefination的載入

還是在ApplicationContext類中

Java程式碼  收藏程式碼
  1. protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {  
  2.     refreshBeanFactory();  
  3.     ConfigurableListableBeanFactory beanFactory = getBeanFactory();  
  4.     if (logger.isDebugEnabled()) {  
  5.         logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);  
  6.     }  
  7.     return beanFactory;  
  8. }  

 它的第一步交給一個抽象方法refreshBeanFactory(), 具體的實現在AbstractRefreshableApplicationContext類中

Java程式碼  收藏程式碼
  1. @Override  
  2. protected final void refreshBeanFactory() throws BeansException {  
  3.     if (hasBeanFactory()) {  
  4.         destroyBeans();  
  5.         closeBeanFactory();  
  6.     }  
  7.     try {  
  8.         DefaultListableBeanFactory beanFactory = createBeanFactory();  
  9.         beanFactory.setSerializationId(getId());  
  10.         customizeBeanFactory(beanFactory);  
  11.         loadBeanDefinitions(beanFactory);  
  12.         synchronized (this.beanFactoryMonitor) {  
  13.             this.beanFactory = beanFactory;  
  14.         }  
  15.     }  
  16.     catch (IOException ex) {  
  17.         throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);  
  18.     }  
  19. }  

第一步是判斷是否已經建立過BeanFactory,如果是,將它銷燬,重新建立
第二步就是建立各種ApplicationContext持有的真正容器實現類DefaultListableBeanFactory,建立Ioc容器

最後啟動BeanDefination的載入  loadBeanDefinitions(beanFactory)方法

4.BeanFactory將載入工作交給BeanDefinationReader

loadBeanDefinitions(beanFactory)方法是抽象的,又因為我們的配置檔案是XML格式的,所以具體實現實在AbstractXmlApplicationConext中

Java程式碼  收藏程式碼
  1. @Override  
  2. protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {  
  3.     // Create a new XmlBeanDefinitionReader for the given BeanFactory.  
  4.     XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);  
  5.     // Configure the bean definition reader with this context's  
  6.     // resource loading environment.  
  7.     beanDefinitionReader.setResourceLoader(this);  
  8.     beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));  
  9.     // Allow a subclass to provide custom initialization of the reader,  
  10.     // then proceed with actually loading the bean definitions.  
  11.     initBeanDefinitionReader(beanDefinitionReader);  
  12.     loadBeanDefinitions(beanDefinitionReader);  
  13. }  

 
這裡建立了一個XmlBeanDefinitionReader 物件,它專門用來讀取基於XML檔案格式的BeanDefinition配置,接下來過載到loadBeanDefinitions(beanDefinitionReader);

Java程式碼  收藏程式碼
  1. protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {  
  2.     Resource[] configResources = getConfigResources();  
  3.     if (configResources != null) {  
  4.         reader.loadBeanDefinitions(configResources);  
  5.     }  
  6.     String[] configLocations = getConfigLocations();  
  7.     if (configLocations != null) {  
  8.         reader.loadBeanDefinitions(configLocations);  
  9.     }  
  10. }  

 首先載入Resource物件用來定位資源,Resource物件的生成在ClassPathXmlApplicationContext 中setConfigLocations(configLocations)方法實現

然後呼叫XmlBeanDefinitionReader基類AbstractBeanDefinitionReader的loadBeanDefinitions方法

Java程式碼  收藏程式碼
  1. public int loadBeanDefinitions(Resource[] resources) throws BeanDefinitionStoreException {  
  2.     Assert.notNull(resources, "Resource array must not be null");  
  3.     int counter = 0;  
  4.     for (Resource resource : resources) {  
  5.         counter += loadBeanDefinitions(resource);  
  6.     }  
  7.     return counter;  
  8. }  

然後呼叫loadBeanDefinitions(resource)方法,此方法的具體實現在XmlBeanDefinitionReader 中

Java程式碼  收藏程式碼
  1. public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {  
  2.     return loadBeanDefinitions(new EncodedResource(resource));  
  3. }  

過載到

Java程式碼  收藏程式碼
  1. public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {  
  2.         Assert.notNull(encodedResource, "EncodedResource must not be null");  
  3.         if (logger.isInfoEnabled()) {  
  4.             logger.info("Loading XML bean definitions from " + encodedResource.getResource());  
  5.         }  
  6.         Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();  
  7.         if (currentResources == null) {  
  8.             currentResources = new HashSet<EncodedResource>(4);  
  9.             this.resourcesCurrentlyBeingLoaded.set(currentResources);  
  10.         }  
  11.         if (!currentResources.add(encodedResource)) {  
  12.             throw new BeanDefinitionStoreException(  
  13.                     "Detected recursive loading of " + encodedResource + " - check your import definitions!");  
  14.         }  
  15.         try {  
  16.             InputStream inputStream = encodedResource.getResource().getInputStream();  
  17.             try {  
  18.                 InputSource inputSource = new InputSource(inputStream);  
  19.                 if (encodedResource.getEncoding() != null) {  
  20.                     inputSource.setEncoding(encodedResource.getEncoding());  
  21.                 }  
  22.                 return doLoadBeanDefinitions(inputSource, encodedResource.getResource());  
  23.             }  
  24.             finally {  
  25.                 inputStream.close();  
  26. 相關推薦

    Spring原始碼淺析 -- XML配置檔案載入解析

    最近在看Spring原始碼,對配置檔案資訊的載入是使用Spring的第一步 ,而這第一步就是一個非常複雜的過程....  Spring通過定義BeanDefination來管理Ioc中的各種物件以及它們之間的依賴關係,所以載入的過程其實就是將XML檔案讀取並解析成B

    XmlBeanDefinitionReader-----Spring原始碼解析 配置檔案裝載解析

    以下內容有部分摘自網路 步驟A. 讀取 Resource 檔案形成 Document 模型     類圖: XmlBeanFactory -> XmlBeanDefinitionReader     Spring 使用 XmlBeanDefinitionReade

    Spring MVC web.xml配置檔案

    <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-a

    Spring的applicationContext.xml配置檔案如何查詢

    目錄 一、文章前言 Spring需要載入配置檔案applicationContext.xml,那麼自帶的jar包又沒有,該如何查詢;在網上找又怕找的不對,那麼其實很簡單,當我們在jar包找不

    使用Redis儲存Spring session(基於XML配置檔案)

    Spring session是一種分散式session共享方案,提供了管理使用者session資訊的介面及實現。下文描述如何通過XML配置檔案的方式實現使用Redis儲存Spring session。 前置條件:Redis server已安裝就緒。 (1) pom.xml配

    spring xml配置檔案bean的解析定義

    spring為解析xml建立bean提供了一種非常解藕的方式。 整體架構面向介面程式設計,AbstractApplicationContext的refresh方法被呼叫時,在重新整理BeanFactory的時候會解析配置檔案。框架提供了: 介面 org.springframework.beans.fact

    properties和xml配置檔案載入效率

    本人通過用java.util.Properties類分別載入一個xml配置檔案和一個properties配置檔案,得到的結果大概是properties配置檔案載入的速度比xml快50倍左右(這個數字不是很精確,但可以確定的是應該有兩個數量級)!這是一個驚人的速度,所以配置檔

    Spring中applicationContext.xml配置檔案中資料庫資料來源配置

    <!-- 標頭檔案,主要注意一下編碼 --> <?xmlversion="1.0"encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN""http://www.springframework

    Spring mvc通過xml配置檔案方式實現簡單HelloWorld

    實現Spring MVC有兩種不同的方式:基於XML配置檔案和基於註解。 這裡,我們使用XML檔案的方式來實現。 首先,我們需要在Eclipse或者是MyEclipse中新建一個web專案,並將Spr

    spring boot匯入xml配置檔案

    【來也匆匆,去也匆匆,在此留下您的腳印吧,轉發點贊評論; 您的認可是我最大的動力,感謝您的支援】 SpringBoot理念就是零配置程式設計,但是如果絕對需要使用XML的配置,我們建議您仍舊從一個@Configuration類開始,你可以使用@ImportResouce註解

    三、Spring Boot專案中MyBatis配置檔案載入深入解析

    開發十年,就只剩下這套架構體系了! >>>   

    Tomcat7.0原始碼分析——server.xml檔案載入解析

    前言作為Java程式設計師,對於Tomcat的server.xml想必都不陌生。本文基於Tomcat7.0的Java原始碼,對server.xml檔案是如何載入和解析進行分析。載入過程分析Bootstrap的load方法用於載入Tomcat的server.xml,實際是通過反

    Spring原始碼解析配置檔案載入

    Spring類的繼承結構圖: Spring運用了大量的模板方法模式和策略模式,所以各位看原始碼的時候,務必留意,每一個繼承的層次都有不同的作用,然後將相同的地方抽取出來,依賴抽象將不同的處理按照不同的策略去處理。 步驟A. 讀取 Resource 檔案形成 Documen

    不認識spring xml配置檔案dtd和xsd型別,血淚的教訓啊

    今天在初次使用spring原始碼的時候,在spring4.2中使用了xsd型別的配置檔案,導致一直解析配置檔案出錯: 找不到元素 'beans' 的宣告 後來從原始碼工程的test/sources目錄中copy新的dtd格式,該巴改巴就成功了。真是汗啊(⊙﹏⊙)b 記住了,

    Spring原始碼分析3 — spring XML配置檔案解析流程

    1 介紹 建立並初始化spring容器中,關鍵一步就是讀取並解析spring XML配置檔案。這個過程比較複雜,本文將詳細分析整個流程。先看涉及到的關鍵類。 XmlWebApplicationContext:web應用的預設Spring容器 XmlBean

    Spring載入xml配置檔案的常用的幾種方式

    專案中一個需求就是所有的功能都是外掛的形式裝入系統,這就需要利用Spring去動態載入某一位置下的配置檔案,就總結了下Spring中載入xml配置檔案的方式, xml是最常見的spring 應用系統配置源。Spring中的幾種容器都支援使用xml裝配bean,包括:  X

    Spring 載入xml配置檔案的方式 ApplicationContext

           大家都知道Java讀普通檔案是通過Basic I/O 中的InputStream、OutStream、Reader、Writer 等實現的。在spring 框架中,它是怎樣識別xml這個配置檔案的呢? 這就要靠IoC容器的兩個介面BeanFactory 和Ap

    讓Eclipse中springxml配置檔案出現屬性和類提示

    在spring配置檔案中可以讓配置bean的時候出現提示,這裡需要做一些設定。設定包括安裝springide外掛,spring-beans-version.xsd檔案引入,增加xml編輯提示的字元,預設只有=>:。最後是讓配置檔案可以通過Spring Config Editor的方式開

    轉:ssm spring+springmvc+mybatis中的xml配置檔案詳解

    這幾天一直在整合SSM框架,雖然網上有很多已經整合好的,但是對於裡面的配置檔案並沒有進行過多的說明,很多人知其然不知其所以然,經過幾天的搜尋和整理,今天總算對其中的XML配置檔案有了一定的瞭解,所以拿出來一起分享一下,希望有不足的地方大家批評指正~~~ 首先   這篇文章暫時只對框架中所要用到的配

    Maven之pom.xmlsetting.xml配置檔案詳解

    一.pom.xml詳解     1.概述     pom中節點如下分佈 <project xmlns="http://maven.apache.org/