在方法 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
![](http://static.javashuo.com/static/loading.gif)
- 父 <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 是在配置文件裏面配置的
- 該方法能夠用於設計一些可插拔的功能上,解除程序依賴
舉個栗子:
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
replace-method 子元素
- replaced-method :能夠在運行時調用新的方法替換現有的方法,還能動態的更新原有方法的邏輯
- 該標籤使用方法和 lookup-method 標籤差很少,只不過替代方法的類須要實現 MethodReplacer 接口
舉個栗子:
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
constructor-arg 子元素
![](http://static.javashuo.com/static/loading.gif)
- 首先獲取 index、type、name 三個屬性值,而後根據是否存在 index 來區分
- 其實二者邏輯都差很少,總共分爲以下幾個步驟(以有 index 爲例)
- 構造 ConstructorArgumentEntry 對象並將其加入到 ParseState 隊列中。ConstructorArgumentEntry 表示構造函數的參數
- 調用 parsePropertyValue() 解析 constructor-arg 子元素,返回結果值
- 根據解析的結果值構造 ConstructorArgumentValues.ValueHolder 實例對象
- 將 type、name 封裝到 ConstructorArgumentValues.ValueHolder 中,而後將 ValueHolder 實例對象添加到 indexedArgumentValues 中
parsePropertyValue()
對子元素進一步解析
- 提取 constructor-arg 子元素的 ref 和 value 的屬性值,對其進行判斷,如下兩種狀況是不容許存在的
- ref 和 value 屬性同時存在
- 存在 ref 或者 value 且又有子元素
- 若存在 ref 屬性,則獲取其值並將其封裝進 RuntimeBeanReference 實例對象中
- 若存在 value 屬性,則獲取其值並將其封裝進 TypedStringValue 實例對象中
- 若是子元素不爲空,則調用 parsePropertySubElement() 進行子元素進一步處理
須要調用 parsePropertySubElement()
進一步處理
property 子元素
![](http://static.javashuo.com/static/loading.gif)
Spring 調用 parsePropertyElements()