Spring 常見(jiàn)的三種注入方式

Spring框架對(duì)Java開(kāi)發(fā)的重要性不言而喻扇调,其核心特性就是IOC(Inversion of Control蹦掐, 控制反轉(zhuǎn))和AOP丐怯,平時(shí)使用最多的就是其中的IOC蟀伸,我們通過(guò)將組件交由Spring的IOC容器管理蚀同,將對(duì)象的依賴關(guān)系由Spring控制,避免硬編碼所造成的過(guò)度程序耦合啊掏。前幾天的時(shí)候蠢络,筆者的同事問(wèn)我為什么要使用構(gòu)造器的注入方式,我回答說(shuō)因?yàn)镾pring文檔推薦這種迟蜜,而說(shuō)不出為什么 T^T刹孔,后面抽時(shí)間了解了一下,下面就是筆者要討論的就是其注入方式娜睛。

二髓霞、常見(jiàn)的三種注入方式

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

2.1 field注入

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

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

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

  1. 注入方式非常簡(jiǎn)單:加入要注入的字段结序,附上@Autowired,即可完成纵潦。
  2. 使得整體代碼簡(jiǎn)潔明了徐鹤,看起來(lái)美觀大方。

2.2 構(gòu)造器注入

@Controller
public class FooController {

  private final FooService fooService;

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

  //使用方式上同邀层,略
}

在Spring4.x版本中推薦的注入方式就是這種返敬,相較于上面的field注入方式而言,就顯得有點(diǎn)難看寥院,特別是當(dāng)注入的依賴很多(5個(gè)以上)的時(shí)候劲赠,就會(huì)明顯的發(fā)現(xiàn)代碼顯得很臃腫。對(duì)于從field注入轉(zhuǎn)過(guò)來(lái)+有強(qiáng)迫癥的園友 來(lái)說(shuō)只磷,簡(jiǎn)直可以說(shuō)是石樂(lè)志 (`Д′*)9经磅。對(duì)于這一點(diǎn)我們后面再來(lái)討論,別急钮追。

2.3 setter注入

@Controller
public class FooController {

  private FooService fooService;

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

在Spring3.x剛推出的時(shí)候,推薦使用注入的就是這種元媚,筆者現(xiàn)在也基本沒(méi)看到過(guò)這種注解方式轧叽,寫(xiě)起來(lái)麻煩,當(dāng)初推薦Spring自然也有他的道理刊棕,這里我們引用一下Spring當(dāng)時(shí)的原話:

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.

咳咳炭晒,簡(jiǎn)單的翻譯一下就是:構(gòu)造器注入?yún)?shù)太多了,顯得很笨重甥角,另外setter的方式能用讓類在之后重新配置或者重新注入网严。

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

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

先來(lái)看看Spring在文檔里怎么說(shuō):

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.

咳咳,再來(lái)簡(jiǎn)單的翻譯一下:這個(gè)構(gòu)造器注入的方式啊嚎卫,能夠保證注入的組件不可變嘉栓,并且確保需要的依賴不為空。此外,構(gòu)造器注入的依賴總是能夠在返回客戶端(組件)代碼的時(shí)候保證完全初始化的狀態(tài)侵佃。

下面來(lái)簡(jiǎn)單的解釋一下:

  • 依賴不可變:其實(shí)說(shuō)的就是final關(guān)鍵字麻昼,這里不再多解釋了。不明白的園友可以回去看看Java語(yǔ)法趣钱。
  • 依賴不為空(省去了我們對(duì)其檢查):當(dāng)要實(shí)例化FooController的時(shí)候涌献,由于自己實(shí)現(xiàn)了有參數(shù)的構(gòu)造函數(shù),所以不會(huì)調(diào)用默認(rèn)構(gòu)造函數(shù)首有,那么就需要Spring容器傳入所需要的參數(shù)燕垃,所以就兩種情況:1、有該類型的參數(shù)->傳入井联,OK 卜壕。2:無(wú)該類型的參數(shù)->報(bào)錯(cuò)。所以保證不會(huì)為空烙常,Spring總不至于傳一個(gè)null進(jìn)去吧 ??
  • 完全初始化的狀態(tài):這個(gè)可以跟上面的依賴不為空結(jié)合起來(lái)轴捎,向構(gòu)造器傳參之前,要確保注入的內(nèi)容不為空蚕脏,那么肯定要調(diào)用依賴組件的構(gòu)造方法完成實(shí)例化侦副。而在Java類加載實(shí)例化的過(guò)程中,構(gòu)造方法是最后一步(之前如果有父類先初始化父類驼鞭,然后自己的成員變量秦驯,最后才是構(gòu)造方法,這里不詳細(xì)展開(kāi)挣棕。)译隘。所以返回來(lái)的都是初始化之后的狀態(tài)。

等等洛心,比較完了setter注入與構(gòu)造器注入的優(yōu)缺點(diǎn)固耘,你還沒(méi)用說(shuō)使用field注入與構(gòu)造器的比較呢!那么我們?cè)倩仡^看一看使用最多的field注入方式:

//承接上面field注入的代碼词身,假如客戶端代碼使用下面的調(diào)用(或者再Junit測(cè)試中使用)
//這里只是模擬一下厅目,正常來(lái)說(shuō)我們只會(huì)暴露接口給客戶端,不會(huì)暴露實(shí)現(xiàn)法严。
FooController fooController = new FooController();
fooController.listFoo(); // -> NullPointerException

如果使用field注入璧瞬,缺點(diǎn)顯而易見(jiàn),對(duì)于IOC容器以外的環(huán)境渐夸,除了使用反射來(lái)提供它需要的依賴之外,無(wú)法復(fù)用該實(shí)現(xiàn)類渔欢。而且將一直是個(gè)潛在的隱患墓塌,因?yàn)槟悴徽{(diào)用將一直無(wú)法發(fā)現(xiàn)NPE的存在。

還值得一提另外一點(diǎn)是:使用field注入可能會(huì)導(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項(xiàng)目啟動(dòng)的時(shí)候,就會(huì)拋出:BeanCurrentlyInCreationException:Requested bean is currently in creation: Is there an unresolvable circular reference韩肝?從而提醒你避免循環(huán)依賴触菜,如果是field注入的話,啟動(dòng)的時(shí)候不會(huì)報(bào)錯(cuò)哀峻,在使用那個(gè)bean的時(shí)候才會(huì)報(bào)錯(cuò)涡相。

四、答疑

好了剩蟀,相信已經(jīng)園友們知道了構(gòu)造器注入的好處催蝗,那么回到了在前面提到的問(wèn)題:

Q1:跟3.x里說(shuō)的一樣,我要是有大量的依賴要注入育特,構(gòu)造方法不會(huì)顯得很臃腫嗎丙号?

對(duì)于這個(gè)問(wèn)題,說(shuō)明你的類當(dāng)中有太多的責(zé)任缰冤,那么你要好好想一想是不是自己違反了類的單一性職責(zé)原則犬缨,從而導(dǎo)致有這么多的依賴要注入。

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

當(dāng)然不是怀薛,存在即是合理!setter的方式既然一開(kāi)始被Spring推薦肯定是有它的道理涮拗,像之前提到的setter的方式能用讓類在之后重新配置或者重新注入乾戏,就是其優(yōu)點(diǎn)之一。除此之外三热,如果一個(gè)依賴有多種實(shí)現(xiàn)方式鼓择,我們可以使用@Qualifier,在構(gòu)造方法里選擇對(duì)應(yīng)的名字注入就漾,也可以使用field或者setter的方式來(lái)手動(dòng)配置要注入的實(shí)現(xiàn)呐能。

五、總結(jié)

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

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

另外抑堡,當(dāng)有一個(gè)依賴有多個(gè)實(shí)現(xiàn)的使用摆出,推薦使用field注入或者setter注入的方式來(lái)指定注入的類型。這是spring官方博客對(duì)setter注入方式和構(gòu)造器注入的比較首妖。謝謝各位園友觀看偎漫,如果有描述不對(duì)的地方歡迎指正,與大家共同進(jìn)步有缆!

參考:
https://www.cnblogs.com/joemsu/p/7688307.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末象踊,一起剝皮案震驚了整個(gè)濱河市温亲,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌杯矩,老刑警劉巖栈虚,帶你破解...
    沈念sama閱讀 211,817評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異史隆,居然都是意外死亡魂务,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)泌射,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)粘姜,“玉大人,你說(shuō)我怎么就攤上這事魄幕∠嗤В” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,354評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵纯陨,是天一觀的道長(zhǎng)坛芽。 經(jīng)常有香客問(wèn)我,道長(zhǎng)翼抠,這世上最難降的妖魔是什么咙轩? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,498評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮阴颖,結(jié)果婚禮上活喊,老公的妹妹穿的比我還像新娘。我一直安慰自己量愧,他們只是感情好钾菊,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著偎肃,像睡著了一般煞烫。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上累颂,一...
    開(kāi)封第一講書(shū)人閱讀 49,829評(píng)論 1 290
  • 那天滞详,我揣著相機(jī)與錄音,去河邊找鬼紊馏。 笑死料饥,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的朱监。 我是一名探鬼主播岸啡,決...
    沈念sama閱讀 38,979評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼赫编!你這毒婦竟也來(lái)了凰狞?” 一聲冷哼從身側(cè)響起篇裁,我...
    開(kāi)封第一講書(shū)人閱讀 37,722評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎赡若,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體团甲,經(jīng)...
    沈念sama閱讀 44,189評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡逾冬,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了躺苦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片身腻。...
    茶點(diǎn)故事閱讀 38,654評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖匹厘,靈堂內(nèi)的尸體忽然破棺而出嘀趟,到底是詐尸還是另有隱情,我是刑警寧澤愈诚,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布她按,位于F島的核電站,受9級(jí)特大地震影響炕柔,放射性物質(zhì)發(fā)生泄漏酌泰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評(píng)論 3 313
  • 文/蒙蒙 一匕累、第九天 我趴在偏房一處隱蔽的房頂上張望陵刹。 院中可真熱鬧,春花似錦欢嘿、人聲如沸衰琐。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,762評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)羡宙。三九已至,卻和暖如春框弛,著一層夾襖步出監(jiān)牢的瞬間辛辨,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,993評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工瑟枫, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留斗搞,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,382評(píng)論 2 360
  • 正文 我出身青樓慷妙,卻偏偏與公主長(zhǎng)得像僻焚,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子膝擂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評(píng)論 2 349

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