為什么使用構(gòu)造注入而不是Autowired

使用Spring開(kāi)發(fā)時(shí)募胃,我們通常有兩種依賴(lài)注入的方式,基于注解@Autowired的依賴(lài)注入和基于構(gòu)造函數(shù)的依賴(lài)注入畦浓。

用IDEA開(kāi)發(fā)過(guò)程中痹束,如果使用@Autowired注入,通常會(huì)有如下警告


warn.png
Inspection info: Spring Team recommends: "Always use constructor based dependency injection in your beans. Always use assertions for mandatory dependencies".

翻譯成中文:
spring 團(tuán)隊(duì)建議:“在bean中始終使用基于構(gòu)造函數(shù)的依賴(lài)注入讶请,始終使用斷言來(lái)強(qiáng)制依賴(lài)”祷嘶。

為什么spring團(tuán)隊(duì)會(huì)這樣建議呢?

可能會(huì)出現(xiàn)空指針異常

首先我們看一個(gè)使用@Autowired注入的簡(jiǎn)單例子:

public class Car {

    @Autowired
    private Wheel wheel;

    public void run() {
        wheel.roll();
    }
}

假設(shè)測(cè)試代碼如下夺溢,就會(huì)發(fā)生空指針異常:

Car car = new Car();
car.run();// -> NullPointerException

出現(xiàn)這種問(wèn)題的關(guān)鍵在于论巍,Car允許創(chuàng)建無(wú)狀態(tài)的對(duì)象,也就是說(shuō)在構(gòu)建Car時(shí)允許Wheel為空风响。

下面我們使用構(gòu)造注入的方式:

public class Car {

    private final Wheel wheel;

    public Car(Wheel wheel) {
        Assert.notNull(wheel,"Wheel must not be null");
        this.wheel = wheel;
    }

    public void run() {
        wheel.roll();
    }

}

這樣我們就有如下優(yōu)點(diǎn):

  • 在創(chuàng)建Car對(duì)象的時(shí)候嘉汰,強(qiáng)制依賴(lài)Wheel對(duì)象,確保創(chuàng)建Car對(duì)象時(shí)每個(gè)對(duì)象都是有效狀態(tài)状勤。
  • 構(gòu)造器中可以添加對(duì)象初始化的校驗(yàn)邏輯
  • 可以清楚的區(qū)分對(duì)象是通過(guò)setter方法注入的(非final對(duì)象)還是通過(guò)強(qiáng)制依賴(lài)注入的(final對(duì)象)

構(gòu)造注入代碼變得臃腫鞋怀?

或許有的讀者可能會(huì)說(shuō),構(gòu)造注入的話(huà)持搜,如果依賴(lài)的對(duì)象很多密似,構(gòu)造器參數(shù)就會(huì)很多,顯得代碼很臃腫葫盼。這種情況的話(huà)残腌,就要考慮這個(gè)類(lèi)是符合足單一職責(zé)原則了,將這個(gè)類(lèi)拆分為多個(gè)類(lèi)贫导。

而且使用@Autowired的自動(dòng)裝配會(huì)讓依賴(lài)對(duì)象變得很容易抛猫,隨著項(xiàng)目的迭代,自動(dòng)注入的對(duì)象可能會(huì)變得很多脱盲,但是使用構(gòu)造注入邑滨,構(gòu)造器就會(huì)變得很臃腫,提醒你代碼里有bad smell了钱反,需要拆分或重構(gòu)代碼了掖看。

還有一個(gè)問(wèn)題是@Autowired注入的對(duì)象無(wú)法使用final關(guān)鍵字匣距,因?yàn)閒inal對(duì)象必須在構(gòu)造器中初始化。

@Autowired測(cè)試不友好

使用注解的自動(dòng)裝配哎壳,我們的業(yè)務(wù)代碼確實(shí)會(huì)變得比較少毅待,但是單元測(cè)試該如何寫(xiě)呢?

        Wheel wheel = Mock(Wheel);
        Car car = new Car();
        //通過(guò)反射來(lái)將wheel對(duì)象注入到Car對(duì)象里
        car.run();

通過(guò)反射注入到Car對(duì)象里归榕,我們的單元測(cè)試代碼就會(huì)顯得很繁瑣尸红,或者在Car對(duì)象里提供一個(gè)Wheel的setter方法?這樣代碼不是很優(yōu)雅刹泄。

如果是構(gòu)造注入外里,單元測(cè)試就會(huì)變成如下:

        Wheel wheel = Mock(Wheel);
        Car car = new Car(wheel);
        car.run();

單元測(cè)試代碼就會(huì)變得很優(yōu)雅,而且在后續(xù)的開(kāi)發(fā)中特石,如果Car對(duì)象添加了強(qiáng)制依賴(lài)的Tank對(duì)象盅蝗,單元測(cè)試也不會(huì)出現(xiàn)沒(méi)有設(shè)置的強(qiáng)制依賴(lài)項(xiàng)。

Spring 的DI設(shè)計(jì)模式姆蘸,是將依賴(lài)關(guān)系的創(chuàng)建和類(lèi)本身分離墩莫,將依賴(lài)關(guān)系創(chuàng)建的職責(zé)交給了類(lèi)注入器做,允許程序設(shè)計(jì)的松耦合逞敷,并遵循單一職責(zé)原則和依賴(lài)反轉(zhuǎn)原則狂秦。因此使用@Autowired自動(dòng)裝配的字段在Spring容器之外無(wú)法使用(不包含通過(guò)反射設(shè)置對(duì)象的方式)。

構(gòu)造注入可以在受影響的類(lèi)中輕松表明對(duì)象的依賴(lài)關(guān)系推捐,但是@Autowired的自動(dòng)裝配其實(shí)對(duì)外隱藏了這些依賴(lài)關(guān)系裂问,需要到對(duì)應(yīng)的類(lèi)中查看代碼才能明確依賴(lài)。

使用Lombok來(lái)解決構(gòu)造注入樣板代碼的問(wèn)題

Lombok是一個(gè)強(qiáng)大的java樣板代碼解決方案玖姑,這里來(lái)介紹下使用Lombok簡(jiǎn)化構(gòu)造注入的代碼:

@RequiredArgsConstructor
public class Car {

    private final @NonNull Wheel wheel;

    public void run() {
        wheel.roll();
    }

}

@RequiredArgsConstructor注解會(huì)在編譯過(guò)程中愕秫,將所有的final對(duì)象作為參數(shù)添加到構(gòu)造器中。

小結(jié)

下面我們來(lái)總結(jié)下注解注入和構(gòu)造注入的優(yōu)缺點(diǎn):

注解注入

++ 寫(xiě)更少的代碼
-- 代碼變得不安全
-- 單元測(cè)試會(huì)比較復(fù)雜
-- 無(wú)法使用fianl對(duì)象
-- 違反單一職責(zé)原則變得很容易
-- 對(duì)受影響的類(lèi)隱藏自己的依賴(lài)關(guān)系

構(gòu)造注入

++ 更安全的代碼
++ 測(cè)試友好
++ 依賴(lài)添加代價(jià)較高焰络,顯式的表明代碼的bad smell
++ 在受影響的類(lèi)中顯式的表明依賴(lài)關(guān)系
-- 需要寫(xiě)更多的業(yè)務(wù)代碼(可以通過(guò)Lombok解決)

參考文章

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市符喝,隨后出現(xiàn)的幾起案子闪彼,更是在濱河造成了極大的恐慌,老刑警劉巖协饲,帶你破解...
    沈念sama閱讀 219,110評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件畏腕,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡茉稠,警方通過(guò)查閱死者的電腦和手機(jī)描馅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)而线,“玉大人铭污,你說(shuō)我怎么就攤上這事恋日。” “怎么了嘹狞?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,474評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵岂膳,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我磅网,道長(zhǎng)谈截,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,881評(píng)論 1 295
  • 正文 為了忘掉前任涧偷,我火速辦了婚禮簸喂,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘燎潮。我一直安慰自己喻鳄,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,902評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布跟啤。 她就那樣靜靜地躺著诽表,像睡著了一般。 火紅的嫁衣襯著肌膚如雪隅肥。 梳的紋絲不亂的頭發(fā)上竿奏,一...
    開(kāi)封第一講書(shū)人閱讀 51,698評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音腥放,去河邊找鬼泛啸。 笑死,一個(gè)胖子當(dāng)著我的面吹牛秃症,可吹牛的內(nèi)容都是我干的候址。 我是一名探鬼主播,決...
    沈念sama閱讀 40,418評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼种柑,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼岗仑!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起聚请,我...
    開(kāi)封第一講書(shū)人閱讀 39,332評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤荠雕,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后驶赏,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體炸卑,經(jīng)...
    沈念sama閱讀 45,796評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,968評(píng)論 3 337
  • 正文 我和宋清朗相戀三年煤傍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了盖文。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,110評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蚯姆,死狀恐怖五续,靈堂內(nèi)的尸體忽然破棺而出洒敏,到底是詐尸還是另有隱情,我是刑警寧澤返帕,帶...
    沈念sama閱讀 35,792評(píng)論 5 346
  • 正文 年R本政府宣布桐玻,位于F島的核電站,受9級(jí)特大地震影響荆萤,放射性物質(zhì)發(fā)生泄漏镊靴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,455評(píng)論 3 331
  • 文/蒙蒙 一链韭、第九天 我趴在偏房一處隱蔽的房頂上張望偏竟。 院中可真熱鬧,春花似錦敞峭、人聲如沸踊谋。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,003評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)殖蚕。三九已至,卻和暖如春沉迹,著一層夾襖步出監(jiān)牢的瞬間睦疫,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,130評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工鞭呕, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蛤育,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,348評(píng)論 3 373
  • 正文 我出身青樓葫松,卻偏偏與公主長(zhǎng)得像瓦糕,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子腋么,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,047評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容