運(yùn)行 IDE 的自動(dòng)檢查工具分析代碼時(shí), 如果用@Autowired 注解的話,會(huì)提示如下的警告:
第一次看到這樣的提示,是很困惑的墩弯,因?yàn)橥ǔG闆r下直接使用@Autowired 不僅讓代碼更加簡(jiǎn)潔易讀吩跋,寫起來也十分的方便。
雖然最新(5.1.9)的Spring 文檔依賴注入的章節(jié)里只介紹了兩種依賴注入的方法渔工,但實(shí)際上有三種依賴注入的方式:
- Constructor-based dependency injection(基于構(gòu)造方法的依賴注入)
- Setter-based dependency injection(基于 setter 的依賴注入)
- Field-based dependency injection(基于 filed 注解的依賴注入)
第三種依賴注入的方式是代碼分析工具不建議的锌钮,但也是使用最多、最常見的依賴注入方式引矩。即使在 Spring 官方的一些手冊(cè)里(比如Accessing data with MySQL)梁丘,也可以看到使用 Field-based dependency injection。
下面分別具體介紹一下這三種依賴注入的方式旺韭。
三種依賴注入的方式
-
Constructor-based dependency injection
@Component public class ConstructorBasedInjection { private final InjectedBean injectedBean; @Autowired public ConstructorBasedInjection(InjectedBean injectedBean) { this.injectedBean = injectedBean; } }
基于構(gòu)造方法的依賴注入的主要優(yōu)點(diǎn)是氛谜,可以注入聲明為 final 的字段。
基于構(gòu)造方法的依賴注入在類實(shí)例化期間啟動(dòng)区端,對(duì)于必要的依賴項(xiàng)來說值漫,使用構(gòu)造方法注入會(huì)更合適。 -
Setter-based dependency injection
@Component public class ConstructorBasedInjection { private InjectedBean injectedBean; @Autowired public void setInjectedBean(InjectedBean injectedBean) { this.injectedBean = injectedBean; } }
如果使用無參數(shù)構(gòu)造方法或無參數(shù)靜態(tài)工廠方法實(shí)例化 Bean织盼,Spring 容器將調(diào)用這些 setter 方法杨何,注入 Bean 的依賴項(xiàng)。
-
Field-based dependency injection
@Component public class ConstructorBasedInjection { @Autowired private InjectedBean injectedBean; }
使用基于字段的依賴注入的話沥邻,只需在需要注入的字段加上@Autowired危虱,Spring 容器就會(huì)在類初始化的時(shí)候設(shè)置這些字段。
可以看到谋国,這種方法確實(shí)是最簡(jiǎn)潔的,不需要任何模版代碼迁沫。但是為什么代碼檢查還是還是不建議這種方法呢芦瘾?因?yàn)樗_實(shí)存在著一些缺點(diǎn)。
Field-based dependency injection 的缺點(diǎn)
-
不允許 Immutable 字段的聲明
基于字段的依賴注入不支持聲明為 final 的字段集畅,聲明為 final 的字段必須在類初始化的時(shí)候初始化該字段近弟。如果聲明了 final 的字段并想注入該依賴,唯一的方式是使用基于構(gòu)造方法的注入
-
可能會(huì)違反單一職責(zé)原則
在面向?qū)ο蟮脑O(shè)計(jì)原則中挺智,我們經(jīng)常會(huì)提到 SOLID祷愉,更好地遵循 SOLID 原則能讓我們的代碼更好理解、維護(hù)赦颇,拓展性更強(qiáng)二鳄。其中 S 指的是單一職責(zé)原則,也就是一個(gè)類應(yīng)該只負(fù)責(zé)整個(gè)工程的單個(gè)功能部分媒怯。
如果使用基于字段依賴注入的話订讼,即使這個(gè)類依賴了很多其它的類,也經(jīng)常覺得沒什么問題扇苞。而如果使用基于構(gòu)造方法的依賴注入的話欺殿,會(huì)很容易發(fā)現(xiàn)構(gòu)造方法傳入了太多的參數(shù)(有的人覺得這是基于構(gòu)造方法依賴注入方法的缺點(diǎn)寄纵,事實(shí)上出現(xiàn)這種情況時(shí),是一個(gè)代碼需要重構(gòu)的提醒)脖苏,這個(gè)時(shí)候我們可以審視我們的代碼程拭,是否需要將該類拆分重構(gòu)。
使用基于字段的依賴注入雖然沒有直接違背單一職責(zé)原則棍潘,但確實(shí)隱藏了發(fā)現(xiàn)違背單一職責(zé)原則的一些信號(hào)恃鞋。
-
與依賴注入的結(jié)合過分緊密
使用基于字段的依賴注入的主要原因是能夠減少代碼,讓代碼簡(jiǎn)潔蜒谤。但這同時(shí)也意味著設(shè)置這些字段的唯一方法是通過 Spring 容器實(shí)例化類并使用反射注入它們山宾,否則字段將不會(huì)初始化,該類也無法使用鳍徽。
如果要在 Spring 容器外部使用這些類资锰,比如單元測(cè)試,則必須使用 Spring 容器來實(shí)例化類阶祭,沒有其他方法(除了反射)來設(shè)置這些字段绷杜。
而如果使用基于構(gòu)造方法或者基于 setter 依賴注入時(shí),在單元測(cè)試時(shí)濒募,我們可以 mock 依賴的對(duì)象鞭盟,并將它們傳到構(gòu)造方法或 setter 方法中,會(huì)讓單元測(cè)試簡(jiǎn)單很多瑰剃。 -
隱藏了依賴關(guān)系
使用基于構(gòu)造方法依賴注入齿诉,或者基于 setter 的依賴注入時(shí),外部可以通過構(gòu)造方法或者 setter 方法知道該類的依賴項(xiàng)晌姚。
而如果使用基于字段的依賴注入時(shí)粤剧,該類的所有依賴對(duì)于外部來說是不可知的。
結(jié)論
基于字段的依賴注入雖然用起來十分方便挥唠,代碼也十分簡(jiǎn)潔抵恋,但確實(shí)存在著一些缺點(diǎn),這也是為什么代碼檢查工具不推薦這種寫法的原因宝磨。
通常情況下弧关,如果有 final 字段,或者有必需的依賴項(xiàng)唤锉,建議使用基于構(gòu)造方法的依賴注入世囊。基于 setter 的依賴注入通常建議用來注入可選的依賴項(xiàng)窿祥。