Spring IOC 之解析 bean 標籤:開啓解析進程,BeanDefinition

在方法 parseDefaultElement() 中,若是遇到標籤 爲 bean 則調用 processBeanDefinition() 方法進行 bean 標籤解析java

整個過程分爲四個步驟spring

  • 調用 BeanDefinitionParserDelegate.parseBeanDefinitionElement() 進行元素解析,解析過程當中若是失敗,返回 null,錯誤由 ProblemReporter 處理。若是解析成功則返回 BeanDefinitionHolder 實例 bdHolder。BeanDefinitionHolder 爲持有 name 和 alias 的 BeanDefinition。
  • 若實例 bdHolder 不爲空,則調用 BeanDefinitionParserDelegate.decorateBeanDefinitionIfRequired() 進行自定義標籤處理
  • 解析完成後,則調用 BeanDefinitionReaderUtils.registerBeanDefinition() 對 bdHolder 進行註冊
  • 發出響應事件,通知相關的監聽器,完成 Bean 標籤解析

parseBeanDefinitionElement()架構

沒有對 Bean 標籤進行解析,只是在解析動做以前作了一些功能架構,主要的工做有:ide

  • 解析 id、name 屬性,肯定 alias 集合,檢測 beanName 是否惟一
  • 調用方法 parseBeanDefinitionElement() 對屬性進行解析並封裝成 GenericBeanDefinition 實例 beanDefinition
  • 根據所獲取的信息(beanName、aliases、beanDefinition)構造 BeanDefinitionHolder 實例對象並返回

這裏有必要說下 beanName 的命名規則:函數

  • 若是 id 不爲空,則 beanName = id;
  • 若是 id 爲空,可是 alias 不空,則 beanName 爲 alias 的第一個元素,若是二者都爲空,則根據默認規則來設置 beanName

BeanDefinition

  • 解析 bean 標籤的過程其實就是構造一個 BeanDefinition 對象的過程
  • <bean> 元素標籤擁有的配置屬性,BeanDefinition 均提供了相應的屬性,與之一一對應
  • 因此咱們有必要對 BeanDefinition 有一個總體的認識

BeanDefinition 是一個接口,它描述了一個 Bean 實例ui

  • 包括屬性值、構造方法值和繼承自它的類的更多信息
  • 它繼承 AttributeAccessor 和 BeanMetadataElement 接口
    • AttributeAccessor :定義了與其它對象的(元數據)進行鏈接和訪問的約定,即對屬性的修改,包括獲取、設置、刪除
    • BeanMetadataElement:Bean 元對象持有的配置元素能夠經過getSource() 方法來獲取

BeanDefinition 整個結構以下圖:this

    

  • 父 <bean> 用 RootBeanDefinition表示
  • 子 <bean> 用 ChildBeanDefinition 表示
  • 而沒有父 <bean> 的就使用RootBeanDefinition 表示。GenericBeanDefinition 爲一站式服務類
  • AbstractBeanDefinition對三個子類共同的類信息進行抽象。

解析 Bean 標籤

  • 在 BeanDefinitionParserDelegate.parseBeanDefinitionElement() 中完成 Bean 的解析
    • 返回的是一個已經完成對 <bean> 標籤解析的 BeanDefinition 實例
    • 在該方法內部,首先調用 createBeanDefinition() 方法建立一個用於承載屬性的 GenericBeanDefinition 實例
    • 委託 BeanDefinitionReaderUtils 建立

 

protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName)
			throws ClassNotFoundException {
		return BeanDefinitionReaderUtils.createBeanDefinition(
				parentName, className, this.readerContext.getBeanClassLoader());
	}
  • 建立完 GenericBeanDefinition 實例後,再調用 parseBeanDefinitionAttributes() 
  • 該方法將建立好的 GenericBeanDefinition 實例當作參數,對 Bean 標籤的全部屬性進行解析

完成 Bean 標籤基本屬性解析後spa

  • 會依次調用 parseMetaElements()、parseLookupOverrideSubElements()、parseReplacedMethodSubElements() 對子元素 meta、lookup-method、replace-method 完成解析

三個子元素的做用以下:設計

  • meta:元數據。
  • lookup-method:Spring 動態改變 bean 裏方法的實現。方法執行返回的對象,使用 Spring 內原有的這類對象替換,經過改變方法返回值來動態改變方法。內部實現爲使用 cglib 方法,從新生成子類,重寫配置的方法和返回對象,達到動態改變的效果。
  • replace-method:Spring 動態改變 bean 裏方法的實現。須要改變的方法,使用 Spring 內原有其餘類(須要繼承接口org.springframework.beans.factory.support.MethodReplacer)的邏輯,替換這個方法。經過改變方法執行邏輯來動態改變方法。

meta 子元素

  • meta :元數據。當須要使用裏面的信息時能夠經過key獲取
  • meta 所聲明的 key 並不會在 Bean 中體現,只是一個額外的聲明,當咱們須要使用裏面的信息時,經過 BeanDefinition 的 getAttribute()
  • 解析過程較爲簡單,獲取相應的 key - value 構建 BeanMetadataAttribute 對象,而後經過 addMetadataAttribute() 加入到 AbstractBeanDefinition

委託 AttributeAccessorSupport 實現code

  • AttributeAccessorSupport 是接口 AttributeAccessor 的實現者
  • AttributeAccessor 接口定義了與其餘對象的元數據進行鏈接和訪問的約定,能夠經過該接口對屬性進行獲取、設置、刪除操做

lookup-method 子元素

  • lookup-method :獲取器注入,是把一個方法聲明爲返回某種類型的 bean 但實際要返回的 bean 是在配置文件裏面配置的
  • 該方法能夠用於設計一些可插拔的功能上,解除程序依賴

舉個栗子:

  • 配置以下

replace-method 子元素

  • replaced-method :能夠在運行時調用新的方法替換現有的方法,還能動態的更新原有方法的邏輯
  • 該標籤使用方法和 lookup-method 標籤差很少,只不過替代方法的類須要實現 MethodReplacer 接口

舉個栗子:

  • 配置以下(執行原始方法):

  • 配置以下(執行替換方法):

constructor-arg 子元素

  • 首先獲取 index、type、name 三個屬性值,而後根據是否存在 index 來區分
  • 其實二者邏輯都差很少,總共分爲以下幾個步驟(以有 index 爲例)
    • 構造 ConstructorArgumentEntry 對象並將其加入到 ParseState 隊列中。ConstructorArgumentEntry 表示構造函數的參數
    • 調用 parsePropertyValue() 解析 constructor-arg 子元素,返回結果值
    • 根據解析的結果值構造 ConstructorArgumentValues.ValueHolder 實例對象
    • 將 type、name 封裝到 ConstructorArgumentValues.ValueHolder 中,而後將 ValueHolder 實例對象添加到 indexedArgumentValues 中

parsePropertyValue() 對子元素進一步解析

  1. 提取 constructor-arg 子元素的 ref 和 value 的屬性值,對其進行判斷,如下兩種狀況是不容許存在的
    • ref 和 value 屬性同時存在
    • 存在 ref 或者 value 且又有子元素
  2. 若存在 ref 屬性,則獲取其值並將其封裝進 RuntimeBeanReference 實例對象中
  3. 若存在 value 屬性,則獲取其值並將其封裝進 TypedStringValue 實例對象中
  4. 若是子元素不爲空,則調用 parsePropertySubElement() 進行子元素進一步處理

須要調用 parsePropertySubElement() 進一步處理

property 子元素

Spring 調用 parsePropertyElements() 

相關文章
相關標籤/搜索