Spring 官方為什么建議構(gòu)造器注入?
前言
本章的內(nèi)容主要是想探討我們?cè)谶M(jìn)行 Spring 開(kāi)發(fā)過(guò)程當(dāng)中,關(guān)于依賴(lài)注入的幾個(gè)知識(shí)點(diǎn)。感興趣的讀者可以先看下以下問(wèn)題:
@Autowired,@Resource,@Inject三個(gè)注解的區(qū)別當(dāng)你在使用 @Autowired時(shí),是否有出現(xiàn)過(guò)Field injection is not recommended的警告?你知道這是為什么嗎?Spring 依賴(lài)注入有哪幾種方式?官方是怎么建議使用的呢?
@Autowired,@Resource,@Inject 三個(gè)注解的區(qū)別
@Autowired, @Resource, @Inject 三個(gè)注解進(jìn)行依賴(lài)注入。下面來(lái)介紹一下這三個(gè)注解有什么區(qū)別。@Autowired
@Autowired為Spring 框架提供的注解,需要導(dǎo)入包org.springframework.beans.factory.annotation.Autowired。public interface Svc {
void sayHello();
}
@Service
public class SvcA implements Svc {
@Override
public void sayHello() {
System.out.println("hello, this is service A");
}
}
@Service
public class SvcB implements Svc {
@Override
public void sayHello() {
System.out.println("hello, this is service B");
}
}
@Service
public class SvcC implements Svc {
@Override
public void sayHello() {
System.out.println("hello, this is service C");
}
}
@SpringBootTest
public class SimpleTest {
@Autowired
// @Qualifier("svcA")
Svc svc;
@Test
void rc() {
Assertions.assertNotNull(svc);
svc.sayHello();
}
}
type在上下文中查找匹配的bean,查找type為Svc的beanname進(jìn)行匹配@Qualifier注解,則按照@Qualifier指定的name進(jìn)行匹配,查找name為svcA的bean@Autowired(required=false),如果設(shè)置required為false(默認(rèn)為true),則注入失敗時(shí)不會(huì)拋出異常)@Inject和@Autowired 是相同的,因?yàn)樗鼈兊囊蕾?lài)注入都是使用AutowiredAnnotationBeanPostProcessor來(lái)處理的。
@Inject是 JSR-330 定義的規(guī)范,如果使用這種方式,切換到Guice也是可以的。Guice 是 google 開(kāi)源的輕量級(jí) DI 框架
@Inject是 Java EE 包里的,在 SE 環(huán)境需要單獨(dú)引入。另一個(gè)區(qū)別在于@Autowired可以設(shè)置required=false而@Inject并沒(méi)有這個(gè)屬性。@Resource
@Resource是 JSR-250 定義的注解。Spring 在 CommonAnnotationBeanPostProcessor實(shí)現(xiàn)了對(duì)JSR-250的注解的處理,其中就包括@Resource。
@Resource有兩個(gè)重要的屬性:name和type,而Spring 將@Resource注解的name屬性解析為bean的名字,而type屬性則解析為bean的類(lèi)型。name和type,則從 Spring 上下文中找到唯一匹配的 bean 進(jìn)行裝配,找不到則拋出異常。name,則從上下文中查找名稱(chēng)(id)匹配的 bean 進(jìn)行裝配,找不到則拋出異常。type,則從上下文中找到類(lèi)型匹配的唯一 bean 進(jìn)行裝配,找不到或是找到多個(gè),都會(huì)拋出異常。name,又沒(méi)有指定type,則默認(rèn)按照byName方式進(jìn)行裝配;如果沒(méi)有匹配,按照byType進(jìn)行裝配。@Autowired注解的時(shí)候,你會(huì)發(fā)現(xiàn) IDEA 會(huì)有警告提示:Field injection is not recommended Inspection info: Spring Team Recommends: "Always use constructor based dependency injection in your beans. Always use assertions for mandatory dependencies

不建議使用基于 field 的注入方式。Spring 開(kāi)發(fā)團(tuán)隊(duì)建議:在你的Spring Bean 永遠(yuǎn)使用基于constructor 的方式進(jìn)行依賴(lài)注入。對(duì)于必須的依賴(lài),永遠(yuǎn)使用斷言來(lái)確認(rèn)。
@Service
public class HelpService {
@Autowired
@Qualifier("svcB")
private Svc svc;
public void sayHello() {
svc.sayHello();
}
}
public interface Svc {
void sayHello();
}
@Service
public class SvcB implements Svc {
@Override
public void sayHello() {
System.out.println("hello, this is service B");
}
}
@Autowired處,使用Alt + Enter 快捷進(jìn)行修改之后,代碼就會(huì)變成基于 Constructor 的注入方式,修改之后:@Service
public class HelpService {
private final Svc svc;
@Autowired
public HelpService(@Qualifier("svcB") Svc svc) {
// Assert.notNull(svc, "svc must not be null");
this.svc = svc;
}
public void sayHello() {
svc.sayHello();
}
}
svc是必須的依賴(lài),應(yīng)該使用Assert.notNull(svc, "svc must not be null")來(lái)確認(rèn)。基于 field 注入(屬性注入) 基于 setter 注入 基于 constructor 注入(構(gòu)造器注入)
1. 基于 field 注入
@Autowired
private Svc svc;
2. 基于 setter 方法注入
通過(guò)對(duì)應(yīng)變量的setXXX()方法以及在方法上面使用注解,來(lái)完成依賴(lài)注入。比如:
private Helper helper;
@Autowired
public void setHelper(Helper helper) {
this.helper = helper;
}注:在 Spring 4.3 及以后的版本中,setter 上面的 @Autowired 注解是可以不寫(xiě)的。
3. 基于 constructor 注入
將各個(gè)必需的依賴(lài)全部放在帶有注解構(gòu)造方法的參數(shù)中,并在構(gòu)造方法中完成對(duì)應(yīng)變量的初始化,這種方式,就是基于構(gòu)造方法的注入。比如:
private final Svc svc;
@Autowired
public HelpService(@Qualifier("svcB") Svc svc) {
this.svc = svc;
}在 Spring 4.3 及以后的版本中,如果這個(gè)類(lèi)只有一個(gè)構(gòu)造方法,那么這個(gè)構(gòu)造方法上面也可以不寫(xiě) @Autowired 注解。
基于 field 注入的好處
@Autowired扔到變量之上就好了,不需要特殊的構(gòu)造器或者set方法,依賴(lài)注入容器會(huì)提供你所需的依賴(lài)。基于 field 注入的壞處
成也蕭何敗也蕭何
容易違背了單一職責(zé)原則 使用這種基于 field 注入的方式,添加依賴(lài)是很簡(jiǎn)單的,就算你的類(lèi)中有十幾個(gè)依賴(lài)你可能都覺(jué)得沒(méi)有什么問(wèn)題,普通的開(kāi)發(fā)者很可能會(huì)無(wú)意識(shí)地給一個(gè)類(lèi)添加很多的依賴(lài)。但是當(dāng)使用構(gòu)造器方式注入,到了某個(gè)特定的點(diǎn),構(gòu)造器中的參數(shù)變得太多以至于很明顯地發(fā)現(xiàn) something is wrong。擁有太多的依賴(lài)通常意味著你的類(lèi)要承擔(dān)更多的責(zé)任,明顯違背了單一職責(zé)原則(SRP:Single responsibility principle)。 依賴(lài)注入與容器本身耦合 依賴(lài)注入框架的核心思想之一就是受容器管理的類(lèi)不應(yīng)該去依賴(lài)容器所使用的依賴(lài)。換句話說(shuō),這個(gè)類(lèi)應(yīng)該是一個(gè)簡(jiǎn)單的 POJO(Plain Ordinary Java Object)能夠被單獨(dú)實(shí)例化并且你也能為它提供它所需的依賴(lài)。 這個(gè)問(wèn)題具體可以表現(xiàn)在: 你的類(lèi)不能繞過(guò)反射(例如單元測(cè)試的時(shí)候)進(jìn)行實(shí)例化,必須通過(guò)依賴(lài)容器才能實(shí)例化,這更像是集成測(cè)試 你的類(lèi)和依賴(lài)容器強(qiáng)耦合,不能在容器外使用 不能使用屬性注入的方式構(gòu)建不可變對(duì)象( final修飾的變量)
Spring 開(kāi)發(fā)團(tuán)隊(duì)的建議
Since you can mix constructor-based and setter-based DI, it is a good rule of thumb to use constructors for mandatory dependencies and setter methods or configuration methods for optional dependencies.
強(qiáng)制依賴(lài)就用構(gòu)造器方式 可選、可變的依賴(lài)就用 setter 注入 當(dāng)然你可以在同一個(gè)類(lèi)中使用這兩種方法。構(gòu)造器注入更適合強(qiáng)制性的注入旨在不變性,Setter 注入更適合可變性的注入。
讓我們看看 Spring 這樣推薦的理由,首先是基于構(gòu)造方法注入,
final 修飾的變量),另一方面也可以保證這些變量的值不會(huì)是 null。此外,經(jīng)過(guò)構(gòu)造方法完成依賴(lài)注入的組件 (注:比如各個(gè) service),在被調(diào)用時(shí)可以保證它們都完全準(zhǔn)備好了。與此同時(shí),從代碼質(zhì)量的角度來(lái)看,一個(gè)巨大的構(gòu)造方法通常代表著出現(xiàn)了代碼異味,這個(gè)類(lèi)可能承擔(dān)了過(guò)多的責(zé)任。而對(duì)于基于 setter 的注入,他們是這么說(shuō)的:
基于 setter 的注入,則只應(yīng)該被用于注入非必需的依賴(lài),同時(shí)在類(lèi)中應(yīng)該對(duì)這個(gè)依賴(lài)提供一個(gè)合理的默認(rèn)值。如果使用 setter 注入必需的依賴(lài),那么將會(huì)有過(guò)多的 null 檢查充斥在代碼中。使用 setter 注入的一個(gè)優(yōu)點(diǎn)是,這個(gè)依賴(lài)可以很方便的被改變或者重新注入。
小結(jié)
參考
轉(zhuǎn)自:Richard_Yi 來(lái)源:https://juejin.cn/post/6844904056230690824
PS:如果覺(jué)得我的分享不錯(cuò),歡迎大家隨手點(diǎn)贊、在看。
(完) 加我"微信" 獲取一份 最新Java面試題資料 請(qǐng)備注:666,不然不通過(guò)~
最近好文
1、Spring Boot 實(shí)現(xiàn)掃碼登錄,這種方式太香了!!
2、SpringSecurity + JWT 實(shí)現(xiàn)單點(diǎn)登錄
3、基于 Vue+Spring 前后端分離管理系統(tǒng)ELAdmin
最近面試BAT,整理一份面試資料《Java面試BAT通關(guān)手冊(cè)》,覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫(kù)、數(shù)據(jù)結(jié)構(gòu)等等。 獲取方式:關(guān)注公眾號(hào)并回復(fù) java 領(lǐng)取,更多內(nèi)容陸續(xù)奉上。 明天見(jiàn)(??ω??)??
評(píng)論
圖片
表情
