在IoC容器中裝配Bean之-----------基於XML的配置(依賴注入)
一.Spring裝配Bean的簡要概述
1.要使應用程式中的Spring容器成功啟動, 需要同時具備以下三個方面的條件:
(1)Spring框架的類包都已經被Maven專案依賴成功.
(2)應用程式為Spring提供了完整的Bean配置資訊.
(3)Bean的類都已經放在應用程式的類路徑
2.Spring是如何在容器中儲存Bean的資訊?
Spring啟動時讀取應用程式提供的Bean的配置資訊, 並在Spring容器中生成一份相關的Bean配置登錄檔, 然後根據這張登錄檔例項化Bean. 裝配好Bean之間的依賴關係, 為上層應用提供準備就緒的執行環境.
Bean配置資訊是Bean的元資料資訊, 由四個方面組成:
(1)Bean的實現類.
(2)Bean的屬性資訊, 如資料來源的連線數, 使用者名稱, 密碼等.
(3)Bean的依賴關係, Spring根據Bean的依賴關係完成Bean之間的裝配.
(4)Bean的行為配置, 如生命週期範圍及生命週期各過程的回撥函式等.
Bean的元資料資訊在Spring容器中的內部對應物是由一個個BeanDefinition形成的Bean登錄檔.
Spring支援多種Bean配置方式, Spring1.0僅支援基於XML的配置, Spring2.0新增基於註解的配置, Spring3.0新增基於Java類的配置, Spring4.0新增基於Groovy動態語言的配置.
Bean配置資訊首先定義了Bean的實現及依賴關係, Spring容器根據各種形式的Bean配置資訊在容器內部建立起Bean定義登錄檔; 然後根據登錄檔載入, 例項化Bean, 並建立Bean和Bean之間的依賴關係, 最後將這些準備就緒的Bean放在Bean快取池中, 以供外層的應用程式進行呼叫.
二.基於XML的配置
1.Bean的命名
id屬性: 一般情況下, 在配置一個Bean時, 需要為其指定一個id屬性作為Bean的名稱. id在IoC容器中必須是唯一的, 而且id的命名必須滿足XML對id的命名規範: 必須以字母開始, 後面可以是字母,數字,連字元,下劃線,句號,冒號等完整結束的符號. 逗號和空格這些非完整結束符是非法的. Spring不允許出現兩個id相同的<bean>.
name屬性: name屬性沒有字元上的限制, 幾乎可以使用任何符號. name可以指定多個名字, 名字之間可用逗號,分號或者空格進行分割. 使用者可以使用這多個名字來獲取IoC容器中的Bean. Spring容器允許出現兩個相同name的<bean>, 如果有多個name相同的<bean>, 那麼通過getBean(beanName)獲取Bean時, 將返回後面的那個Bean. 原因是後面的Bean覆蓋了前面同名的Bean.
如果id和name兩個屬性都未指定, Spring自動將全限定類名作為Bean的名稱. 如下圖所示:
第一個Bean可以通過 getBean("com.bean.Car")獲得.
第二個Bean可以通過 getBean("com.bean.Car#1")獲得
第三個Bean可以通過 getBean("com.bean.Car#2")獲得
2.依賴注入
Spring支援兩種依賴注入的方式, 分別是屬性注入和建構函式注入, 除此之外, Spring還支援工廠方法注入方式.
(1)屬性注入
屬性注入指通過setXxx()方法注入Bean的屬性值或依賴物件.
1.屬性注入例項
屬性注入要求Bean提供一個預設的構造方法, 併為需要注入的屬性提供setter方法. Spring先呼叫Bean的預設構造方法例項化Bean物件, 然後通過反射的方法呼叫Setter方法注入屬性值.
配置檔案配置了一個Man Bean, 為該Bean的兩個屬性提供了屬性值, Bean的每一個屬性對應一個<property>標籤, name為屬性的名稱, 在Bean的實現類中有與其對應的Setter方法. 注意:###Spring只會檢查Bean中是否有對應的Setter方法, 對Bean中是否有對應的屬性成員變數則不做要求.
如上圖, 在xml配置檔案中通過屬性注入方式, 不需要在Man 類中新增屬性des, 只需提供其對應的setDes()方法就可以了.
(2)建構函式注入
建構函式注入確保一些屬性在Bean例項化時就得到設定, 確保Bean在例項化後就可以使用.
(1)聯合使用型別和索引匹配入參
(2)通過自身型別反射匹配入參
如果Bean建構函式入參的型別是可辨別的, 由於Java反射機制可以獲取到建構函式的入參型別, 即使建構函式注入的配置不提供型別和索引的資訊, Spring依舊可以完成建構函式的注入工作.
(3)迴圈依賴問題
Spring容器能對建構函式配置的Bean進行例項化有一個前提, 即Bean建構函式入參引用的物件必須已經準備就緒, 由於這個機制的限制, 如果兩個Bean都採用建構函式注入, 而且都通過建構函式入參引用對方, 就會發生類似於執行緒死鎖的迴圈依賴問題.
上述情況就會造成執行緒死鎖問題, 解決方法是將建構函式注入方式調整為屬性注入即可.
將配置檔案做如下更改, 則可以測試成功.
(3)工廠方法注入
1.非靜態工廠方法
有些工廠方法是非靜態的, 即必須例項化工廠類後才能呼叫工廠方法.
輸出結果 Dog Bean:
由於工廠方法不是靜態的, 所以首先需要定義一個工廠類的Bean, 通過factory-bean引入工廠類例項, 通過factory-method指定對應的工廠方法.
2.靜態工廠方法
靜態工廠方法意味著使用者無需在建立工廠類例項的情況下就可以呼叫工廠類方法.
直接在<bean>中通過 class 屬性指定工廠類, 再通過 factory-method指定對應的工廠方法.
3.選擇注入方式的考量
建構函式注入的好處:
(1)保證一些重要的屬性在Bean例項化時就設定好, 避免因一些重要屬性沒有提供而導致一個無用Bean例項的情況.
(2)不需要為每個屬性提供Setter方法, 減少了類的方法個數.
(3)更好的封裝類變數, 避免外部錯誤呼叫
建構函式注入的缺點:
(1)如果一個類屬性眾多, 那麼使用建構函式注入, 使程式可讀性變差.
(2)靈活性不強.
(3)不利於類的繼承和擴充套件.
(4)建構函式注入可能會造成迴圈依賴問題.