spring bean的創建過程


spring的核心容器包括:core、beans、context、express language四個模塊。所以對於一個簡單的spring工程,最基本的就是依賴以下三個jar包即可:

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>4.3.8.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>4.3.8.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>4.3.8.RELEASE</version>
    </dependency>

暫時先不考慮express language模塊。

 

通過xml文件創建一個spring bean的大概過程:

  1. 主要考慮以下情況:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="myTestBean" class="chapter02.MyTestBean"/>
    
    </beans>
    BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"));

    注:beanFactoryTest.xml指上述xml文件;chapter02.MyTestBean是一個簡單的bean(包含一個私有屬性,get,set方法)

  2. 關於new ClassPathResource("beanFactoryTest.xml")
    1. ClassPathResource類是Resource接口的一個實現類
    2. Resource接口繼承了InputStreamSource接口
    3. InputStreamSource接口只有一個方法定義:InputStream getInputStream() throws IOException;
  3. 關於new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"))
    1. 首先對傳入的resource參數做封裝
    2. 其次通過SAX讀取XML文件的方式來准備InputSource對象
    3. 最后將准備好的數據通過參數傳入真正的核心處理部分:doLoadBeanDefinitions
  4. 對於真正的核心處理部分:doLoadBeanDefinitions的處理過程,如下:
    1. 獲取對XML文件的驗證模式:DTD、XSD
    2. 加載XML文件,並得到對應的Document。通過SAX解析XML文檔的步驟如下:
      1. 首先創建DocumentBuilderFactory
      2. 通過DocumentBuilderFactory創建DocumentBuilder
      3. 使用DocumentBuilder解析InputSource來返回Document對象
    3. 根據返回的Document注冊Bean信息
  5. 對於根據返回的Document注冊Bean信息的處理過程,如下:
    1. BeanDefinitionDocumentReader接口的實現類DefaultBeanDefinitionDocumentReader會獲取到document對象的root節點
    2. 通過root節點獲取所有的bean節點:一種是默認的bean節點:使用parseDefaultElement()方法進行處理;一種是自定義的bean節點:使用delegate.parseCustomElement()方法進行處理,其中delegate是根據自定義節點的命名空間獲取到的自定義的節點處理器;以下只討論對於第一種情況即:對於默認的bean節點的處理。
    3. 對於默認的bean節點有四種標簽:import、alias、bean、beans,我們這里主要討論最常用也最復雜的bean標簽:
  6. 對於最常用也最復雜的默認節點中的bean標簽的處理過程,如下:
    1. 首先委托BeanDefinitionDelegate類的parseBeanDefinitionElement()方法進行元素解析,返回BeanDefinitionHolder類型的實例bdHolder
      1. 提取元素中的id以及name屬性
      2. 進一步解析其他所有屬性以及子元素,並統一封裝至GenericBeanDefinition類型的實例中;注:BeanDefinition存在三個實現類:RootBeanDefinition(父節點)、ChildBeanDefinition(子節點)、GenericBeanDefinition(Xml屬性對應的java容器)
        1. 解析各種屬性:scope屬性、singleton屬性、abstract屬性、lazy-init屬性、autowire屬性、dependency-check屬性、depends-on屬性、autowire-candidate屬性、primary屬性、init-method屬性、destory-method屬性、factory-method屬性、factory-bean屬性
        2. 解析各種子元素:
          • 解析元數據:parseMetaElements
          • 解析lookup-method子元素:parseLookupOverrideSubElements
          • 解析replaced-method子元素:parseReplacedMethodSubElements
          • 解析構造函數參數:parseConstructorArgElements
          • 解析property子元素:parsePropertyElements
          • 解析qualifier子元素:parseQualifierElements
      3. 如果檢測到bean沒有指定beanName,那么使用默認規則為此bean生產beanName
      4. 將獲取到的信息封裝到BeanDefinitionHolder的實例中
    2. 當返回的bdHolder下存在自定義屬性時,還需要對自定義標簽進行解析。例如以下包含mybean:user自定義屬性時:
      <bean id="test" class="test.Myclass">
              <mybean:user username="aaa"/>
      </bean>

      需要用到delegate.decorateBeanDefinitionIfRequired()進行自定義標簽的處理,處理過程如下:

      • 遍歷所有的屬性以及所有的子元素
      • 根據節點獲取標簽的命名空間
      • 根據命名空間判斷是否是默認標簽。(這里只對非默認標簽進行處理,因為對於默認標簽在之前已經處理過了)
      • 根據命名空間找到對應的處理器
      • 使用相應的處理器進行修飾處理
    3. 對解析完成后的bdHolder進行注冊:包括通過beanName的注冊以及alias(別名)的注冊
      1. 通過beanName注冊BeanDefinition,關鍵方法:this.beanDefinitionMap.put(beanName, beanDefinition),主要步驟如下:
        • 對AbstractBeanDefinition的methodOvirrides屬性進行校驗
        • 如果beanName已經注冊過:如果用戶設置了不允許重復注冊,則拋異常,否則直接覆蓋,重新注冊
        • 加入beanDfinitionMap
        • 清除解析之前留下的對應beanName的緩存
      2. 通過alias(別名)注冊BeanDefinition,關鍵方法:this.aliasMap.put(alias, beanName),主要步驟如下:
        • alias與beanName相同,則不需要處理,並刪除掉原有alias
        • 若alias存在,根據用戶是否設置允許別名覆蓋來進行相應的處理
        • alias循環檢查。例如:當存在A(key)-->B(value)時,如果還存在B(key)-->A(value)或者B(key)-->C(value)、C(key)-->A(value)時,稱為出現了循環。此時則會拋出異常
        • 注冊alias:this.aliasMap.put(alias, beanName);
    4. 最后發出bean已注冊完成的響應事件,通知相關的監聽器  


免責聲明!

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



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