Spring beans架構--set注入

Java Beans

Spring管理對象是以bean爲顆粒度,在最初設計時其實是特指Java beans,因此之前的注入也幾乎是清一色的set注入,直到聰明的大腦們引入了Annotation後兩者纔有了明顯差異,慢慢進化出Spring特有的bean規範。

本篇先從設計者的初衷Java Beans開始,理清楚set的注入原理,然後再(如)往(果)下(有)探(時)尋(間)Annotation注入。

Java beans規範主要有三點:
- 有一個公有的無參構造器
- 屬性可以通過get、set、is(可以替代get,用在布爾型屬性上)方法或遵循特定命名規範的其他方法訪問
- 可序列化

Sun之所以指定beans規範,很大程度上是爲IDE準備的——IDE可以用可視化的方式設置bean的屬性。

java beans原型

Java bean規範通過java.beans.PropertyEditor設置bean屬性,通過BeanInfo描述了JavaBean哪些屬性是可定製以及可定製屬性與PropertyEditor的對應關係(propertyName->editor)。
  
BeanInfo與JavaBean之間通過兩者之間規範的命名確立,對應JavaBean的BeanInfo採用如下的命名規範:BeanInfo,如Car對應的BeanInfo爲CarBeanInfo。

JDK提供內省(Introspector)暴露BeanInfo,訪問者通過內省間接依賴PropertyDescriptor從而獲得bean屬性描述,該描述包括該屬性是否開放以及使用的屬性編輯器等,最後再通過屬性編輯器編輯修改屬性。此外JDK提供一個默認屬性編輯器的管理器–PropertyEditorManager,它定義常見類型的屬性編輯器(class->editor),如果某個JavaBean的常見類型屬性沒有通過BeanInfo指定屬性編輯器,IDE將自動使用PropertyEditorManager中該類型的默認屬性編輯器。

Spring Set注入

對Java beans的使用透着Spring整個團隊的靈性,Rod Johnson和其團隊在對JDK組件以及第三方開源中間件的使用上,不僅知其然更知其所以然,並在此基礎上進行了合理的精簡和擴展。

舉一個例子,Spring中使用了AspectJ,AspectJ能提供了強大的靜態織入能力,很多人也想當然的認爲AspectJ是Aop的一種織入方式,事實上Spring做了取捨,只集成了後者的語法,保留了自身的動態織入,利用後者解析AspectJ風格的表達式並生成Advisor,最終對target的織入只有JDK dynamic和Cglib兩種方式。

Spring beans也幾乎是對Java beans的顛覆和升級,在後者的基礎上,增加了更加強大的嵌套屬性支持並有自己獨特的表達式。

Bean表達式

Bean表達式和Spring表達式(Spring Expression)是兩碼事,前者只是bean模塊裏內置簡易功能,後者則是一個單獨模塊,兩者既不是正交關係也不是平行關係。

Bean表達式主要用於對象和集合的注入,語法很簡單,主要有兩種:
- 以bean.propertyName代表bean的內部屬性,並支持多級嵌套。每個點代表是一級路徑,路徑和路徑之間是嵌套和包含關係。
- 以propertyName[index/key]代表集合類屬性的內部元素,同樣也支持多級嵌套。
- 支持兩者混合使用,舉例路徑A.c.b[1][name]。這代表A的屬性c下的屬性b是個數組或者集合(假設其爲aa),則這個表達式是用來對aa的第2個元素map的name key綁定值。

值綁定

實體關係

spring bean邏輯視圖

PropertyAccessor

Spring使用統一的屬性訪問外觀–PropertyAccessor,主要包括屬性可讀和可寫判斷以及屬性值的set和get。

PropertyAccessor類似BeanFactory,也是多層次結構,同樣也是使用parent屬性指向父層級。對於複合屬性,會遞歸構建相應path的PropertyAccessor,依然以A.c.b[1][name]爲例,其屬性訪問實體結構會是:
PropertyAccessor層級結構

它依賴兩個實體,分別是PropertyValue和PropertyTokenHolder:
- PropertyValue是屬性名和值的鍵值對,屬性名可以是一個簡單屬性,也可以是複合屬性即表達式。
- PropertyTokenHolder是對屬性path解析的結果,解析後有actualName、canonicalName和keys三個屬性,分別指實際名稱、規範名稱和屬性下標。在上面例子中,三者分別是:
多級path

當前層級的PropertyAccessor通過actualName(實際就是對象屬性名)就可以找到相應的屬性描述,並據此展開一系列處理,例如綁定值到集合的某個元素等等。

主要實體關係

  • Introspector, BeanInfo, PropertyEditor以及PropertyDescriptor四架馬車在Spring beans體系中依然存在, 但是後兩者扮演的角色卻大幅下降:
    • Descriptor被封裝在PropertyHandler外觀中,後者基於前者感知property類型、名稱以及setter和getter等信息。
    • Editor被放入專門的registry管理去除對descriptor和默認屬性編輯器管理器的依賴。registry同時維護了默認和客戶化兩類editor緩存,客戶化緩存有兩類–類型映射(type->editor)和屬性名映射(name->editor),而默認緩存則只有類型映射。
  • TypeConvertDelegator,屬性值類型轉換器,依賴TypeDescriptor和PropertyEditor,前者描述屬性類型信息,包括屬性本身以及泛化參數(數組、集合以及map等的元素)類型信息;後者則set以及get屬性值。
  • CachedIntrospectionResults是個多級緩存,結構如(類型->(屬性名->屬性描述實體))。在構造時傳入類型,它通過Introspector獲取BeanInfo,再使用後者獲取所有PropertyDescriptor,然後將屬性名和描述的關係緩存到該類型層級的緩存之下。
  • BeanWrapper同時繼承PropertyAccessor和PropertyEditorRegistry,類似DefaultlListableBeanFactory也是個適配器,既管理editor的註冊及發現又可以編輯處理相應屬性。因此BeanWrapper可以方便獲取內置管理的屬性編輯器編輯相應屬性:
    • 關聯類型轉換器,轉換器由它構造且在構造時將自身作爲轉換器所需的registry傳入,這也幾乎與DefaultlListableBeanFactory和BeanDefinitionReader的關係一致。轉換器用於注入屬性的類型轉換,它依賴類型描述實體(TypeDescriptor),該實體依賴PropertyHandler,handler有屬性相關的各類信息,可用於構造類型描述。TypeDescriptor和PropertyHandler的區別在於一個聚焦於屬性的多個角度描述,另一個則聚焦於對類型的描述,可能是屬性類型還可能是屬性的泛化參數類型。
    • PropertyHandler持有屬性getter和bean對象,通過反射即可獲得屬性值。屬性訪問實體通過handler獲取屬性當前值,並將PropertyValue注入。

實體交互

值綁定過程
1. 首先從PropertyValue獲取屬性路徑;
2. 接着通過路徑構造PropertyAccessor,基於不同路徑可能會存在多層級情況;
3. 使用actualName獲取屬性相應的PropertyHandler(PropertyDescriptor),並取得屬性對象;
1. 如果屬性對象是集合類型,則循環keys,直到倒數第二層級。即如果是b[1][name],返回的屬性對象是b[1]這個map對象,這個map對象作爲被綁定對象返回。
4. 用綁定值綁定該屬性對象,
1. 如果綁定值和對象或集合元素類型不相符,則通過TypeConverterDelegator進行轉換。
值綁定

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章