淺談spring為什么推薦使用構(gòu)造器注入

一喝峦、前言

? Spring框架對Java開發(fā)的重要性不言而喻,其核心特性就是IOC(Inversion of Control呜达, 控制反轉(zhuǎn))和AOP谣蠢,平時使用最多的就是其中的IOC,我們通過將組件交由Spring的IOC容器管理,將對象的依賴關(guān)系由Spring控制眉踱,避免硬編碼所造成的過度程序耦合挤忙。前幾天的時候,筆者的同事問我為什么要使用構(gòu)造器的注入方式谈喳,我回答說因為Spring文檔推薦這種册烈,而說不出為什么 T^T,后面抽時間了解了一下婿禽,下面就是筆者要討論的就是其注入方式赏僧。

二、常見的三種注入方式

? 筆者為了方便起見就只是用注解的方式注入(現(xiàn)在也很少使用xml了吧扭倾,(~ ̄▽ ̄)~)

2.1 field注入

@Controller
public class FooController {
  @Autowired
  //@Inject
  private FooService fooService;

  //簡單的使用例子淀零,下同
  public List<Foo> listFoo() {
      return fooService.list();
  }
}

這種注入方式應(yīng)該是筆者目前為止開發(fā)中見到的最常見的注入方式。原因很簡單:

  1. 注入方式非常簡單:加入要注入的字段膛壹,附上@Autowired驾中,即可完成。
  2. 使得整體代碼簡潔明了模聋,看起來美觀大方哀卫。

2.2 構(gòu)造器注入

@Controller
public class FooController {

  private final FooService fooService;

  @Autowired
  public FooController(FooService fooService) {
      this.fooService = fooService;
  }

  //使用方式上同,略
}

? 在Spring4.x版本中推薦的注入方式就是這種撬槽,相較于上面的field注入方式而言此改,就顯得有點難看,特別是當(dāng)注入的依賴很多(5個以上)的時候侄柔,就會明顯的發(fā)現(xiàn)代碼顯得很臃腫共啃。對于從field注入轉(zhuǎn)過來+有強迫癥的園友 來說,簡直可以說是石樂志 (`Д′*)9暂题。對于這一點我們后面再來討論移剪,別急。

2.3 setter注入

@Controller
public class FooController {

  private FooService fooService;

  //使用方式上同薪者,略
  @Autowired
  public void setFooService(FooService fooService) {
      this.fooService = fooService;
  }
}

? 在Spring3.x剛推出的時候纵苛,推薦使用注入的就是這種,筆者現(xiàn)在也基本沒看到過這種注解方式言津,寫起來麻煩攻人,當(dāng)初推薦Spring自然也有他的道理,這里我們引用一下Spring當(dāng)時的原話:

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.

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.

? 咳咳悬槽,簡單的翻譯一下就是:構(gòu)造器注入?yún)?shù)太多了怀吻,顯得很笨重,另外setter的方式能用讓類在之后重新配置或者重新注入初婆。

? 那么后面為什么又換成構(gòu)造器注入了呢蓬坡?(喂喂喂猿棉,Spring你前一大版本還貶低構(gòu)造器注入,后面就立刻捧人家了不好吧屑咳,不過能用于承認(rèn)自己的錯誤萨赁,才是真正令人稱贊的地方吧 (??????)??)

三、構(gòu)造器注入的好處

? 先來看看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.

? 咳咳兆龙,再來簡單的翻譯一下:這個構(gòu)造器注入的方式啊杖爽,能夠保證注入的組件不可變,并且確保需要的依賴不為空详瑞。此外掂林,構(gòu)造器注入的依賴總是能夠在返回客戶端(組件)代碼的時候保證完全初始化的狀態(tài)

下面來簡單的解釋一下:

  • 依賴不可變:其實說的就是final關(guān)鍵字坝橡,這里不再多解釋了泻帮。不明白的園友可以回去看看Java語法。
  • 依賴不為空(省去了我們對其檢查):當(dāng)要實例化FooController的時候计寇,由于自己實現(xiàn)了有參數(shù)的構(gòu)造函數(shù)锣杂,所以不會調(diào)用默認(rèn)構(gòu)造函數(shù),那么就需要Spring容器傳入所需要的參數(shù)番宁,所以就兩種情況:1元莫、有該類型的參數(shù)->傳入,OK 蝶押。2:無該類型的參數(shù)->報錯踱蠢。所以保證不會為空,Spring總不至于傳一個null進去吧 :-(
  • 完全初始化的狀態(tài):這個可以跟上面的依賴不為空結(jié)合起來棋电,向構(gòu)造器傳參之前茎截,要確保注入的內(nèi)容不為空,那么肯定要調(diào)用依賴組件的構(gòu)造方法完成實例化赶盔。而在Java類加載實例化的過程中企锌,構(gòu)造方法是最后一步(之前如果有父類先初始化父類,然后自己的成員變量于未,最后才是構(gòu)造方法撕攒,這里不詳細(xì)展開。)烘浦。所以返回來的都是初始化之后的狀態(tài)抖坪。

等等,比較完了setter注入與構(gòu)造器注入的優(yōu)缺點谎倔,你還沒用說使用field注入與構(gòu)造器的比較呢柳击!那么我們再回頭看一看使用最多的field注入方式:

//承接上面field注入的代碼,假如客戶端代碼使用下面的調(diào)用(或者再Junit測試中使用)
//這里只是模擬一下片习,正常來說我們只會暴露接口給客戶端捌肴,不會暴露實現(xiàn)。
FooController fooController = new FooController();
fooController.listFoo(); // -> NullPointerException

如果使用field注入藕咏,缺點顯而易見状知,對于IOC容器以外的環(huán)境,除了使用反射來提供它需要的依賴之外孽查,無法復(fù)用該實現(xiàn)類饥悴。而且將一直是個潛在的隱患,因為你不調(diào)用將一直無法發(fā)現(xiàn)NPE的存在盲再。

還值得一提另外一點是:使用field注入可能會導(dǎo)致循環(huán)依賴西设,即A里面注入B,B里面又注入A:

public class A {
    @Autowired
    private B b;
}

public class B {
    @Autowired
    private A a;
}

如果使用構(gòu)造器注入答朋,在spring項目啟動的時候贷揽,就會拋出:BeanCurrentlyInCreationException:Requested bean is currently in creation: Is there an unresolvable circular reference?從而提醒你避免循環(huán)依賴梦碗,如果是field注入的話禽绪,啟動的時候不會報錯,在使用那個bean的時候才會報錯洪规。

四印屁、答疑

? 好了,相信已經(jīng)園友們知道了構(gòu)造器注入的好處斩例,那么回到了在前面提到的問題:

Q1:跟3.x里說的一樣雄人,我要是有大量的依賴要注入,構(gòu)造方法不會顯得很臃腫嗎念赶?

對于這個問題础钠,說明你的類當(dāng)中有太多的責(zé)任,那么你要好好想一想是不是自己違反了類的單一性職責(zé)原則晶乔,從而導(dǎo)致有這么多的依賴要注入珍坊。

Q2:是不是其他的注入方式都不適合用了呢?

當(dāng)然不是正罢,存在即是合理阵漏!setter的方式既然一開始被Spring推薦肯定是有它的道理,像之前提到的setter的方式能用讓類在之后重新配置或者重新注入翻具,就是其優(yōu)點之一履怯。除此之外,如果一個依賴有多種實現(xiàn)方式裆泳,我們可以使用@Qualifier叹洲,在構(gòu)造方法里選擇對應(yīng)的名字注入,也可以使用field或者setter的方式來手動配置要注入的實現(xiàn)工禾。

五运提、總結(jié)

? 使用構(gòu)造器注入的好處:

  1. 保證依賴不可變(final關(guān)鍵字)
  2. 保證依賴不為空(省去了我們對其檢查)
  3. 保證返回客戶端(調(diào)用)的代碼的時候是完全初始化的狀態(tài)
  4. 避免了循環(huán)依賴
  5. 提升了代碼的可復(fù)用性

另外蝗柔,當(dāng)有一個依賴有多個實現(xiàn)的使用,推薦使用field注入或者setter注入的方式來指定注入的類型民泵。這是spring官方博客對setter注入方式和構(gòu)造器注入的比較癣丧。謝謝各位園友觀看,如果有描述不對的地方歡迎指正栈妆,與大家共同進步胁编!

參考鏈接:

https://spring.io/blog/2007/07/11/setter-injection-versus-constructor-injection-and-the-use-of-required/

http://vojtechruzicka.com/field-dependency-injection-considered-harmful/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市鳞尔,隨后出現(xiàn)的幾起案子嬉橙,更是在濱河造成了極大的恐慌,老刑警劉巖寥假,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件市框,死亡現(xiàn)場離奇詭異,居然都是意外死亡昧旨,警方通過查閱死者的電腦和手機拾给,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來兔沃,“玉大人蒋得,你說我怎么就攤上這事∑故瑁” “怎么了额衙?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長怕吴。 經(jīng)常有香客問我窍侧,道長,這世上最難降的妖魔是什么转绷? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任伟件,我火速辦了婚禮,結(jié)果婚禮上议经,老公的妹妹穿的比我還像新娘斧账。我一直安慰自己,他們只是感情好煞肾,可當(dāng)我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布咧织。 她就那樣靜靜地躺著,像睡著了一般籍救。 火紅的嫁衣襯著肌膚如雪习绢。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天蝙昙,我揣著相機與錄音闪萄,去河邊找鬼梧却。 笑死,一個胖子當(dāng)著我的面吹牛桃煎,可吹牛的內(nèi)容都是我干的篮幢。 我是一名探鬼主播大刊,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼为迈,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了缺菌?” 一聲冷哼從身側(cè)響起葫辐,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎伴郁,沒想到半個月后耿战,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡焊傅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年剂陡,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片狐胎。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡鸭栖,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出握巢,到底是詐尸還是另有隱情晕鹊,我是刑警寧澤,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布暴浦,位于F島的核電站溅话,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏歌焦。R本人自食惡果不足惜飞几,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望独撇。 院中可真熱鬧屑墨,春花似錦、人聲如沸券勺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽关炼。三九已至程腹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間儒拂,已是汗流浹背寸潦。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工色鸳, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人见转。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓命雀,卻偏偏與公主長得像,于是被迫代替她去往敵國和親斩箫。 傳聞我的和親對象是個殘疾皇子吏砂,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,047評論 2 355

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