【Spring】@Autowired註解Field injection is not recommended
參考文章:http://vojtechruzicka.com/field-dependency-injection-considered-harmful/
首先對於還不熟悉@Autowired的同學當然也包括我先去看看它到底有什麼作用。—自動裝配
大體意思就是就是說用了它就可以省去類裡面的set/get方法,也不用在xml檔案中要注入的類中設定屬性,它會根據@Autowired註釋自動完成。
言歸正傳,那為什麼spring team不推薦使用Filed injection呢?
-
注入型別
有三種主要的方式注入依賴到你的類中。構造器注入、Setter(方法)和Field injection。下面讓我們來比較一下這三種方式。構造器注入
private DependencyA dependencyA; private DependencyB dependencyB; private DependencyC dependencyC; @Autowired public DI(DependencyA dependencyA, DependencyB dependencyB, DependencyC dependencyC) { this.dependencyA = dependencyA; this.dependencyB = dependencyB; this.dependencyC = dependencyC; }
Setter注入
private DependencyA dependencyA; private DependencyB dependencyB; private DependencyC dependencyC; @Autowired public void setDependencyA(DependencyA dependencyA) { this.dependencyA = dependencyA; } @Autowired public void setDependencyB(DependencyB dependencyB) { this.dependencyB = dependencyB; } @Autowired public void setDependencyC(DependencyC dependencyC) { this.dependencyC = dependencyC; }
Field 注入
@Autowired
private DependencyA dependencyA;
@Autowired
private DependencyB dependencyB;
@Autowired
private DependencyC dependencyC;
問題是什麼
正如你所見,Field注入看起來非常好,夠簡潔,並且也沒有樣板式的程式碼(為了實現通用和簡單的任務而重複的程式碼)。程式碼通俗易懂。你的類可以專注於業務而不被依賴注入所汙染。你只需要把@Autowired扔到變數之上就好了,不需要特殊的構造器或者set方法,依賴注入容器會提供你所需的依賴。Java本身就非常繁重,所以要儘可能的讓你的程式碼簡潔,對嗎?
單一職責侵入原則
新增依賴是很簡單的,可能過於簡單了。新增六個、十個甚至一堆依賴一點也沒有困難。當你使用構造器方式注入,到了某個特定的點,構造器中的引數變得太多以至於很明顯地發現something is wrong。擁有太多的依賴通常意味著你的類要承擔更多的責任。明顯違背了單一職責原則(SRP:Single responsibility principle)。
依賴隱藏
使用依賴注入容器意味著類不再對依賴負責,獲取依賴的職責從類中抽離出來,依賴容器會幫你裝配。當類不再為依賴負責,它應該更明確的使用公有的介面方法或構造器,使用這種方式能很清晰的瞭解類需要什麼,也能明確它是可選的(setter注入)還是強制的(構造器注入)。
依賴注入容器耦合
依賴注入框架的核心思想之一就是受容器管理的類不應該去依賴容器所使用的依賴。換句話說,這個類應該是一個簡單的POJO(Plain Ordinary Java Object)能夠被單獨例項化並且你也能為它提供它所需的依賴。只有這樣,你才能在單元測試中例項化這個類而不必去啟動依賴注入容器,實現測試分離(啟動容器更多是整合測試)。
然而,當使用變數直接注入時,沒有一種方式能直接地例項化這個類並且滿足其所有的依賴。這就意味著:
1.有一種方式(呼叫預設構造器)來建立物件就是使用new關鍵字,但是當這個物件缺少一些必要的依賴,呼叫的時候就會出現空指標異常。
舉個栗子:
public class DependencyA {
public void a(){
System.out.println("dependencyA");
}
}
public class POJO {
@Autowired
private DependencyA dependencyA;
public void execute(){
dependencyA.a();
}
}
public class Test {
public static void main(String[] args) {
POJO pojo = new POJO();
pojo.execute();
}
}
當你執行execute()方法時就會報空指標異常,是因為DependencyA沒有被建立,使用這種方式(@Autowired)也不會強制你去建立類所需的依賴,所以當使用者呼叫的方法的時候就可能會出現空指標異常。
2.像這樣的類沒辦法在容器之外被重用,也不能期望反射提供其所需的依賴。
不變性
不像構造器注入,Field注入不能有效地用來指定依賴
構造器注入 VS Setter注入
Setter
Setter應該被用來注入可變的依賴。當沒有提供依賴時,這個類也應該能夠運轉。當例項化物件後,這些依賴也能隨時改變。其實也視情況而變,有時,一個不變的物件是理想狀態。有時,最好是能在執行期間改變物件的屬性。
構造器
構造器對注入強制性的的依賴是好的。物件需要這些依賴才能正常運轉,通過構造器提供這些依賴就能保證物件初始化後就能被使用。使用構造器注入的一個可能的影響就是迴圈依賴。
- 結論
Field注入應該儘可能地去避免使用。作為替代,你應該使用構構造器注入或Setter注入。他們都有利有弊,需要視情況而定。當然你可以在同一個類中使用這兩種方法。構造器注入更適合強制性的注入旨在不變性,Setter注入更適合可變性的注入。