詳解Spring依賴注入的三種方式以及優(yōu)缺點(diǎn)
0.概述
在 Spring 中實(shí)現(xiàn)依賴注入的常見方式有以下 3 種:
- 屬性注入(Field Injection);
- Setter 注入(Setter Injection);
- 構(gòu)造方法注入(Constructor Injection)。
它們的具體使用和優(yōu)缺點(diǎn)分析如下。
1.屬性注入
屬性注入是我們最熟悉,也是日常開發(fā)中使用最多的一種注入方式,它的實(shí)現(xiàn)代碼如下:
@RestController public class UserController { // 屬性對象 @Autowired private UserService userService; @RequestMapping("/add") public UserInfo add(String username, String password) { return userService.add(username, password); } }
1.1 優(yōu)點(diǎn)分析
屬性注入最大的優(yōu)點(diǎn)就是實(shí)現(xiàn)簡單、使用簡單,只需要給變量上添加一個(gè)注解(@Autowired),就可以在不 new 對象的情況下,直接獲得注入的對象了(這就是 DI 的功能和魅力所在),所以它的優(yōu)點(diǎn)就是使用簡單。
1.2 缺點(diǎn)分析
然而,屬性注入雖然使用簡單,但也存在著很多問題,甚至編譯器 Idea 都會提醒你“不建議使用此注入方式”,Idea 的提示信息如下:
屬性注入的缺點(diǎn)主要包含以下 3 個(gè):
- 功能性問題:無法注入一個(gè)不可變的對象(final 修飾的對象);
- 通用性問題:只能適應(yīng)于 IoC 容器;
- 設(shè)計(jì)原則問題:更容易違背單一設(shè)計(jì)原則。
接下來我們一一來看。
缺點(diǎn)1:功能性問題
使用屬性注入無法注入一個(gè)不可變的對象(final 修飾的對象),如下圖所示:
原因也很簡單:在 Java 中 final 對象(不可變)要么直接賦值,要么在構(gòu)造方法中賦值,所以當(dāng)使用屬性注入 final 對象時(shí),它不符合 Java 中 final 的使用規(guī)范,所以就不能注入成功了。
PS:如果要注入一個(gè)不可變的對象,要怎么實(shí)現(xiàn)呢?使用下面的構(gòu)造方法注入即可。
缺點(diǎn)2:通用性問題
使用屬性注入的方式只適用于 IoC 框架(容器),如果將屬性注入的代碼移植到其他非 IoC 的框架中,那么代碼就無效了,所以屬性注入的通用性不是很好。
缺點(diǎn)3:設(shè)計(jì)原則問題
使用屬性注入的方式,因?yàn)槭褂闷饋砗芎唵?,所以開發(fā)者很容易在一個(gè)類中同時(shí)注入多個(gè)對象,而這些對象的注入是否有必要?是否符合程序設(shè)計(jì)中的單一職責(zé)原則?就變成了一個(gè)問題。
但可以肯定的是,注入實(shí)現(xiàn)越簡單,那么濫用它的概率也越大,所以出現(xiàn)違背單一職責(zé)原則的概率也越大。
注意:這里強(qiáng)調(diào)的是違背設(shè)計(jì)原則(單一職責(zé))的可能性,而不是一定會違背設(shè)計(jì)原則,二者有著本質(zhì)的區(qū)別。
2.Setter 注入
Setter 注入的實(shí)現(xiàn)代碼如下:
@RestController public class UserController { // Setter 注入 private UserService userService; @Autowired public void setUserService(UserService userService) { this.userService = userService; } @RequestMapping("/add") public UserInfo add(String username, String password) { return userService.add(username, password); } }
優(yōu)缺點(diǎn)分析
從上面代碼可以看出,Setter 注入比屬性注入要麻煩很多。
要說 Setter 注入有什么優(yōu)點(diǎn)的話,那么首當(dāng)其沖的就是它完全符合單一職責(zé)的設(shè)計(jì)原則,因?yàn)槊恳粋€(gè) Setter 只針對一個(gè)對象。
但它的缺點(diǎn)也很明顯,它的缺點(diǎn)主要體現(xiàn)在以下 2 點(diǎn):
- 不能注入不可變對象(final 修飾的對象);
- 注入的對象可被修改。
接下來我們一一來看。
缺點(diǎn)1:不能注入不可變對象
使用 Setter 注入依然不能注入不可變對象,比如以下注入會報(bào)錯(cuò):
缺點(diǎn)2:注入對象可被修改
Setter 注入提供了 setXXX 的方法,意味著你可以在任何時(shí)候、在任何地方,通過調(diào)用 setXXX 的方法來改變注入對象,所以 Setter 注入的問題是,被注入的對象可能隨時(shí)被修改。
3.構(gòu)造方法注入
構(gòu)造方法注入是 Spring 官方從 4.x 之后推薦的注入方式,它的實(shí)現(xiàn)代碼如下:
@RestController public class UserController { // 構(gòu)造方法注入 private UserService userService; @Autowired public UserController(UserService userService) { this.userService = userService; } @RequestMapping("/add") public UserInfo add(String username, String password) { return userService.add(username, password); } }
當(dāng)然,如果當(dāng)前的類中只有一個(gè)構(gòu)造方法,那么 @Autowired 也可以省略,所以以上代碼還可以這樣寫:
@RestController public class UserController { // 構(gòu)造方法注入 private UserService userService; public UserController(UserService userService) { this.userService = userService; } @RequestMapping("/add") public UserInfo add(String username, String password) { return userService.add(username, password); } }
優(yōu)點(diǎn)分析
構(gòu)造方法注入相比于前兩種注入方法,它可以注入不可變對象,并且它只會執(zhí)行一次,也不存在像 Setter 注入那樣,被注入的對象隨時(shí)被修改的情況,它的優(yōu)點(diǎn)有以下 4 個(gè):
- 可注入不可變對象;
- 注入對象不會被修改;
- 注入對象會被完全初始化;
- 通用性更好。
接下來我們一一來看。
優(yōu)點(diǎn)1:注入不可變對象
使用構(gòu)造方法注入可以注入不可變對象,如下代碼所示:
優(yōu)點(diǎn)2:注入對象不會被修改
構(gòu)造方法注入不會像 Setter 注入那樣,構(gòu)造方法在對象創(chuàng)建時(shí)只會執(zhí)行一次,因此它不存在注入對象被隨時(shí)(調(diào)用)修改的情況。
優(yōu)點(diǎn)3:完全初始化
因?yàn)橐蕾噷ο笫窃跇?gòu)造方法中執(zhí)行的,而構(gòu)造方法是在對象創(chuàng)建之初執(zhí)行的,因此被注入的對象在使用之前,會被完全初始化,這也是構(gòu)造方法注入的優(yōu)點(diǎn)之一。
優(yōu)點(diǎn)4:通用性更好
構(gòu)造方法和屬性注入不同,構(gòu)造方法注入可適用于任何環(huán)境,無論是 IoC 框架還是非 IoC 框架,構(gòu)造方法注入的代碼都是通用的,所以它的通用性更好。
總結(jié)
依賴注入的常見實(shí)現(xiàn)方式有 3 種:屬性注入、Setter 注入和構(gòu)造方法注入。其中屬性注入的寫法最簡單,所以日常項(xiàng)目中使用的頻率最高,但它的通用性不好;而 Spring 官方推薦的是構(gòu)造方法注入,它可以注入不可變對象,其通用性也更好,如果是注入可變對象,那么可以考慮使用 Setter 注入。
以上就是詳解Spring依賴注入的三種方式以及優(yōu)缺點(diǎn)的詳細(xì)內(nèi)容,更多關(guān)于Spring依賴注入的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringBoot2零基礎(chǔ)到精通之映射與常用注解請求處理
SpringBoot是一種整合Spring技術(shù)棧的方式(或者說是框架),同時(shí)也是簡化Spring的一種快速開發(fā)的腳手架,本篇讓我們一起學(xué)習(xí)映射、常用注解和方法參數(shù)的小技巧2022-03-03SpringSecurity實(shí)現(xiàn)前后端分離登錄token認(rèn)證詳解
目前市面上比較流行的權(quán)限框架主要實(shí)Shiro和Spring Security,這兩個(gè)框架各自側(cè)重點(diǎn)不同,各有各的優(yōu)劣,本文將給大家詳細(xì)介紹SpringSecurity如何實(shí)現(xiàn)前后端分離登錄token認(rèn)證2023-06-06Java使用Thread創(chuàng)建多線程并啟動操作示例
這篇文章主要介紹了Java使用Thread創(chuàng)建多線程并啟動操作,結(jié)合實(shí)例形式分析了Java基于Thread類的多線程定義與啟動簡單操作技巧,需要的朋友可以參考下2018-06-06Java實(shí)現(xiàn)將漢字轉(zhuǎn)化為漢語拼音的方法
這篇文章主要介紹了Java實(shí)現(xiàn)將漢字轉(zhuǎn)化為漢語拼音的方法,實(shí)例演示了Java引用pinyin4j庫實(shí)現(xiàn)漢子轉(zhuǎn)化成拼音的使用技巧,需要的朋友可以參考下2015-12-12java實(shí)現(xiàn)文件變化監(jiān)控的方法(推薦)
下面小編就為大家?guī)硪黄猨ava實(shí)現(xiàn)文件變化監(jiān)控的方法(推薦)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-08-08Java使用EasyExcel進(jìn)行單元格合并的問題詳解
項(xiàng)目中需要導(dǎo)出并合并指定的單元格,下面這篇文章主要給大家介紹了關(guān)于java評論、回復(fù)功能設(shè)計(jì)與實(shí)現(xiàn)的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-06-06