項目中遇到一個問題:項目啓動完成前,在A類中注入B類,並調用B類的某個方法。html
那麼調用B類的這個方法寫在哪裏呢,我選擇寫到構造器裏,可是構造器先於Spring注入執行,那麼執行構造器時,注入B類確定爲null,因而選擇了構造器注入,解決問題java
執行順序爲:靜態變量或靜態語句塊–>實例變量或初始化語句塊–>構造方法–>Spring注入spring
筆者爲了方便起見就只是用註解的方式注入(如今也不多使用xml了吧)app
@Controller public class FooController { @Autowired private FooService fooService; //簡單的使用例子,下同 public List<Foo> listFoo() { return fooService.list(); } }
這種注入方式應該是筆者目前爲止開發中見到的最多見的注入方式。緣由很簡單:less
@Autowired
,便可完成。@Controller public class FooController { private final FooService fooService; @Autowired public FooController(FooService fooService) { this.fooService = fooService; } //使用方式上同,略 }
在Spring4.x版本中推薦的注入方式就是這種,相較於上面的註解注入方式而言,就顯得有點難看,特別是當注入的依賴不少(5個以上)的時候,就會明顯的發現代碼顯得很臃腫。對於從註解注入轉過來+有強迫症的園友 來講,簡直能夠說是沒法忍受。對於這一點咱們後面再來討論,別急。
函數
@Controller public class FooController { private FooService fooService; //使用方式上同,略 @Autowired public void setFooService(FooService fooService) { this.fooService = fooService; } }
在Spring3.x剛推出的時候,推薦使用注入的就是這種,筆者如今也基本沒看到過這種註解方式,寫起來麻煩,當初推薦Spring天然也有他的道理,這裏咱們引用一下Spring當時的原話:測試
The Spring team generally advocates setter injection, because large numbers of constructor arguments can get unwieldy, especially when properties are optional. Setter methods also make objects of that class amenable to reconfiguration or re-injection later. Management through JMX MBeans is a compelling use case.ui
Some purists favor constructor-based injection. Supplying all object dependencies means that the object is always returned to client (calling) code in a totally initialized state. The disadvantage is that the object becomes less amenable to reconfiguration and re-injection.this
咳咳,簡單的翻譯一下就是:構造器注入參數太多了,顯得很笨重,另外setter的方式能用讓類在以後從新配置或者從新注入。
spa
那麼後面爲何又換成構造器注入了呢?(喂喂喂,Spring你前一大版本還貶低構造器注入,後面就馬上捧人家了很差吧,不過能用於認可本身的錯誤,纔是真正使人稱讚的地方吧 (๑•̀ㅂ•́)و✧)
先來看看Spring在文檔裏怎麼說:
The Spring team generally advocates constructor injection as it enables one to implement application components as immutable objects and to ensure that required dependencies are not
null
. Furthermore constructor-injected components are always returned to client (calling) code in a fully initialized state.
咳咳,再來簡單的翻譯一下:這個構造器注入的方式啊,可以保證注入的組件不可變,而且確保須要的依賴不爲空。此外,構造器注入的依賴老是可以在返回客戶端(組件)代碼的時候保證徹底初始化的狀態。
下面來簡單的解釋一下:
等等,比較完了setter注入與構造器注入的優缺點,你還沒用說使用field注入與構造器的比較呢!那麼咱們再回頭看一看使用最多的field注入方式:
//承接上面field注入的代碼,假如客戶端代碼使用下面的調用(或者再Junit測試中使用) //這裏只是模擬一下,正常來講咱們只會暴露接口給客戶端,不會暴露實現。 FooController fooController = new FooController(); fooController.listFoo(); // -> NullPointerException
若是使用field注入,缺點顯而易見,對於IOC容器之外的環境,除了使用反射來提供它須要的依賴以外,沒法複用該實現類。並且將一直是個潛在的隱患,由於你不調用將一直沒法發現NPE的存在。
還值得一提另一點是:使用field注入可能會致使循環依賴,即A裏面注入B,B裏面又注入A:
public class A { @Autowired private B b; } public class B { @Autowired private A a; }
若是使用構造器注入,在spring項目啓動的時候,就會拋出:BeanCurrentlyInCreationException:Requested bean is currently in creation: Is there an unresolvable circular reference?從而提醒你避免循環依賴,若是是field注入的話,啓動的時候不會報錯,在使用那個bean的時候纔會報錯。
好了,相信已經園友們知道了構造器注入的好處,那麼回到了在前面提到的問題:
Q1:跟3.x裏說的同樣,我要是有大量的依賴要注入,構造方法不會顯得很臃腫嗎?
對於這個問題,說明你的類當中有太多的責任,那麼你要好好想想是否是本身違反了類的單一性職責原則,從而致使有這麼多的依賴要注入。
Q2:是否是其餘的注入方式都不適合用了呢?
固然不是,存在便是合理!setter的方式既然一開始被Spring推薦確定是有它的道理,像以前提到的setter的方式能用讓類在以後從新配置或者從新注入,就是其優勢之一。除此以外,若是一個依賴有多種實現方式,咱們可使用@Qualifier
,在構造方法裏選擇對應的名字注入,也可使用field或者setter的方式來手動配置要注入的實現。
使用構造器注入的好處:
另外,當有一個依賴有多個實現的使用,推薦使用field注入或者setter注入的方式來指定注入的類型。這是spring官方博客對setter注入方式和構造器注入的比較。謝謝各位園友觀看,若是有描述不對的地方歡迎指正,與你們共同進步!