當咱們在使用依賴注入的時候,一般有三種方式:函數
1.經過構造器來注入;單元測試
2.經過setter方法來注入;測試
3.經過filed變量來注入;this
那麼他們有什麼區別嗎?應該選擇哪一種方式更好?spa
代碼示例:翻譯
1 private DependencyA dependencyA; 2 private DependencyB dependencyB; 3 private DependencyC dependencyC; 4 5 @Autowired 6 public DI(DependencyA dependencyA, DependencyB dependencyB, DependencyC dependencyC) { 7 this.dependencyA = dependencyA; 8 this.dependencyB = dependencyB; 9 this.dependencyC = dependencyC; 10 }
1 private DependencyA dependencyA; 2 private DependencyB dependencyB; 3 private DependencyC dependencyC; 4 5 @Autowired 6 public void setDependencyA(DependencyA dependencyA) { 7 this.dependencyA = dependencyA; 8 } 9 10 @Autowired 11 public void setDependencyB(DependencyB dependencyB) { 12 this.dependencyB = dependencyB; 13 } 14 15 @Autowired 16 public void setDependencyC(DependencyC dependencyC) { 17 this.dependencyC = dependencyC; 18 }
1 @Autowired 2 private DependencyA dependencyA; 3 4 @Autowired 5 private DependencyB dependencyB; 6 7 @Autowired 8 private DependencyC dependencyC;
三種方式的區別小結:3d
1.基於constructor的注入,會固定依賴注入的順序;該方式不容許咱們建立bean對象之間的循環依賴關係,這種限制實際上是一種利用構造器來注入的益處 - 當你甚至沒有注意到使用setter注入的時候,Spring能解決循環依賴的問題;代理
2.基於setter的注入,只有當對象是須要被注入的時候它纔會幫助咱們注入依賴,而不是在初始化的時候就注入;另外一方面若是你使用基於constructor注入,CGLIB不能建立一個代理,迫使你使用基於接口的代理或虛擬的無參數構造函數。指針
3.相信不少同窗都選擇使用直接在成員變量上寫上註解來注入,正如咱們所見,這種方式看起來很是好,精短,可讀性高,不須要多餘的代碼,也方便維護;code
缺點:
1.當咱們利用constructor來注入的時候,比較明顯的一個缺點就是:假如咱們須要注入的對象特別多的時候,咱們的構造器就會顯得很是的冗餘、很差看,很是影響美觀和可讀性,維護起來也較爲困難;
2.當咱們選擇setter方法來注入的時候,咱們不能將對象設爲final的;
3.當咱們在field變量上來實現注入的時候
a.這樣不符合JavaBean的規範,並且頗有可能引發空指針;
b.同時也不能將對象標爲final的;
c.類與DI容器高度耦合,咱們不能在外部使用它;
d.類不經過反射不能被實例化(例如單元測試中),你須要用DI容器去實例化它,這更像集成測試;
... etc.
來自Spring官方文檔的建議:
在Spring 3.x 中,Spring團隊建議咱們使用setter來注入:
大體是說大量的構造器參數會顯得很是笨重,尤爲是當屬性是可選的時候。setter方法可使類的對象在後來從新配置或者從新注入。提供全部的依賴意味着對象老是返回一個徹底初始化狀態的client客戶端(調用)。缺點是對象變得不那麼適合從新配置和從新注入。
而在Spring 4.x 中,Spring團隊再也不建議咱們使用setter來注入,改成了constructor:
Spring團隊一般建議使用構造器來注入,由於它容許一個應用程序組件實現爲不可變對象,並確保所需的依賴項不是空。此外構造器注入組件老是返回一個徹底初始化狀態的client客戶端(調用)。附註,大量的構造函數參數是一個糟糕的代碼習慣,看起來也很壞,這意味着類可能有太多的責任,應該被重構,以更好地解決適當的關注點分離。
setter方法只應該主要的用在能夠在類中指定合理的默認值的可選的依賴關係。不然,用到依賴的全部地方都應該進行非空檢查。setter注入的一個好處是,setter方法使類的對象能夠在以後從新配置或者從新注入。
(以上是本人的渣渣英語翻譯結合有道得來。。大佬看到請輕噴)
接下來插播一條Spring 4.3 的新特徵:
在Spring 4.3 之後,若是咱們的類中只有單個構造函數,那麼Spring就會實現一個隱式的自動注入,上代碼:
以前:
1 @Service 2 public class FooService { 3 4 private final FooRepository repository; 5 6 @Autowired 7 public FooService(FooRepository repository) { 8 this.repository = repository 9 } 10 }
在Spring 4.3 以後:
1 @Service 2 public class FooService { 3 4 private final FooRepository repository; 5 6 public FooService(FooRepository repository) { 7 this.repository = repository 8 } 9 }
如咱們所見,我去掉了構造器上的@Autowired註解,經測試後發現,程序能正常運行,repository的依賴也被成功注入了,當時感受就很amazing。。有興趣的同窗能夠試試~
總結:
1.強制性的依賴性或者當目標不可變時,使用構造函數注入(應該說盡可能都使用構造器來注入)
2.可選或多變的依賴使用setter注入(建議可使用構造器結合setter的方式來注入)
3.在大多數的狀況下避免field域注入(感受大多數同窗可能會有異議,畢竟這個方式寫起來很是簡便,可是它的弊端確實遠大於這些優勢)
4.Spring 4.3+ 的同窗能夠試一試構造器的隱式注入,採用此方式注入後,使得咱們的代碼更優雅,更獨立,減小了對Spring的依賴性。
ps: 轉載請標註出處謝謝。