1. 程式人生 > >spring——bean的創建過程

spring——bean的創建過程

函數參數 oca 復雜 date 析構 require ams decorate custom

  1. 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模塊。

  2. 通過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. 接口InputStreamSource封裝任何能返回InputStream的類。它只有一個方法定義:getInputStream();該方法返回一個InputStream對象。
      2. Resource接口繼承了InputStreamSource接口
      3. ClassPathResource類是Resource接口的一個實現類
    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信息
        1. BeanDefinitionDocumentReader接口的實現類DefaultBeanDefinitionDocumentReader會獲取到document對象的root節點
        2. 通過root節點獲取所有的bean節點:一種是默認的bean節點:使用parseDefaultElement();方法進行處理;一種是自定義的bean節點:使用delegate.parseCustomElement()方法進行處理,其中delegate是根據自定義節點的命名空間獲取到的自定義的節點處理器;以下只討論對於第一種情況即:對於默認的bean節點的處理。
        3. 對於默認的bean節點有四種標簽:import、alias、bean、beans,我們這裏主要討論最常用也最復雜的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. 解析各種子元素:
                1. 解析元數據:parseMetaElements
                2. 解析lookup-method子元素:parseLookupOverrideSubElements
                3. 解析replaced-method子元素:parseReplacedMethodSubElements
                4. 解析構造函數參數:parseConstructorArgElements
                5. 解析property子元素:parsePropertyElements
                6. 解析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();進行自定義標簽的處理,處理過程如下:

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

spring——bean的創建過程