Spring官方為什么建議構(gòu)造器注入?
往期熱門文章: 2、14 個經(jīng)典的 Linux 終端命令行,這些工具堪稱神器!
3、Java 8 失寵!開發(fā)人員向 Java 11 轉(zhuǎn)移...
轉(zhuǎn)自:Richard_Yi
來源:https://juejin.cn/post/6844904056230690824
前言
@Autowired,@Resource,@Inject三個注解的區(qū)別當你在使用
@Autowired時,是否有出現(xiàn)過Field injection is not recommended的警告?你知道這是為什么嗎?Spring 依賴注入有哪幾種方式?官方是怎么建議使用的呢?
@Autowired,@Resource,@Inject 三個注解的區(qū)別
@Autowired, @Resource, @Inject 三個注解進行依賴注入。下面來介紹一下這三個注解有什么區(qū)別。@Autowired
@Autowired為Spring 框架提供的注解,需要導入包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的bean如果有多個bean,則按照
name進行匹配如果有
@Qualifier注解,則按照@Qualifier指定的name進行匹配查找name為svcA的bean如果沒有,則按照變量名進行匹配
查找name為svc的bean匹配不到,則報錯。(
@Autowired(required=false),如果設置required為false(默認為true),則注入失敗時不會拋出異常)
@Inject
@Inject和@Autowired 是相同的,因為它們的依賴注入都是使用AutowiredAnnotationBeanPostProcessor來處理的。
@Inject是 JSR-330 定義的規(guī)范,如果使用這種方式,切換到Guice也是可以的。Guice 是 google 開源的輕量級 DI 框架
@Inject是 Java EE 包里的,在 SE 環(huán)境需要單獨引入。另一個區(qū)別在于@Autowired可以設置required=false而@Inject并沒有這個屬性。@Resource
@Resource是 JSR-250 定義的注解。Spring 在 CommonAnnotationBeanPostProcessor實現(xiàn)了對JSR-250的注解的處理,其中就包括@Resource。
@Resource有兩個重要的屬性:name和type,而Spring 將@Resource注解的name屬性解析為bean的名字,而type屬性則解析為bean的類型。如果同時指定了
name和type,則從 Spring 上下文中找到唯一匹配的 bean 進行裝配,找不到則拋出異常。如果指定了
name,則從上下文中查找名稱(id)匹配的 bean 進行裝配,找不到則拋出異常。如果指定了
type,則從上下文中找到類型匹配的唯一 bean 進行裝配,找不到或是找到多個,都會拋出異常。如果既沒有指定
name,又沒有指定type,則默認按照byName方式進行裝配;如果沒有匹配,按照byType進行裝配。
@Autowired注解的時候,你會發(fā)現(xiàn) IDEA 會有警告提示: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 開發(fā)團隊建議:在你的Spring Bean 永遠使用基于constructor 的方式進行依賴注入。對于必須的依賴,永遠使用斷言來確認。
@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 快捷進行修改之后,代碼就會變成基于 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是必須的依賴,應該使用Assert.notNull(svc, "svc must not be null")來確認。基于 field 注入(屬性注入)
基于 setter 注入
基于 constructor 注入(構(gòu)造器注入)
1. 基于 field 注入
@Autowired
private Svc svc;
2. 基于 setter 方法注入
setXXX()方法以及在方法上面使用注解,來完成依賴注入。比如:private Helper helper;
@Autowired
public void setHelper(Helper helper) {
this.helper = helper;
}
注:在 Spring 4.3 及以后的版本中,setter 上面的 @Autowired 注解是可以不寫的。
3. 基于 constructor 注入
private final Svc svc;
@Autowired
public HelpService(@Qualifier("svcB") Svc svc) {
this.svc = svc;
}
在 Spring 4.3 及以后的版本中,如果這個類只有一個構(gòu)造方法,那么這個構(gòu)造方法上面也可以不寫 @Autowired 注解。
@Autowired扔到變量之上就好了,不需要特殊的構(gòu)造器或者set方法,依賴注入容器會提供你所需的依賴。基于 field 注入的壞處
成也蕭何敗也蕭何
容易違背了單一職責原則 使用這種基于 field 注入的方式,添加依賴是很簡單的,就算你的類中有十幾個依賴你可能都覺得沒有什么問題,普通的開發(fā)者很可能會無意識地給一個類添加很多的依賴。但是當使用構(gòu)造器方式注入,到了某個特定的點,構(gòu)造器中的參數(shù)變得太多以至于很明顯地發(fā)現(xiàn) something is wrong。擁有太多的依賴通常意味著你的類要承擔更多的責任,明顯違背了單一職責原則(SRP:Single responsibility principle)。 依賴注入與容器本身耦合 依賴注入框架的核心思想之一就是受容器管理的類不應該去依賴容器所使用的依賴。換句話說,這個類應該是一個簡單的 POJO(Plain Ordinary Java Object)能夠被單獨實例化并且你也能為它提供它所需的依賴。 這個問題具體可以表現(xiàn)在: 你的類不能繞過反射(例如單元測試的時候)進行實例化,必須通過依賴容器才能實例化,這更像是集成測試 你的類和依賴容器強耦合,不能在容器外使用 不能使用屬性注入的方式構(gòu)建不可變對象( final修飾的變量)
Spring 開發(fā)團隊的建議
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.
強制依賴就用構(gòu)造器方式 可選、可變的依賴就用 setter 注入 當然你可以在同一個類中使用這兩種方法。構(gòu)造器注入更適合強制性的注入旨在不變性,Setter 注入更適合可變性的注入。
final 修飾的變量),另一方面也可以保證這些變量的值不會是 null。此外,經(jīng)過構(gòu)造方法完成依賴注入的組件 (注:比如各個 service),在被調(diào)用時可以保證它們都完全準備好了。與此同時,從代碼質(zhì)量的角度來看,一個巨大的構(gòu)造方法通常代表著出現(xiàn)了代碼異味,這個類可能承擔了過多的責任。小結(jié)
參考
Setter-based dependency injection Field Dependency Injection Considered Harmful IDEA 警告 Field injection is not recommended
往期熱門文章:
1、IntelliJ idea 高效使用教程,一勞永逸!
2、微軟再出手,這次要干翻 IDEA 了。。 3、用好 Java 中的枚舉,讓你的工作效率飛起來! 4、還在用分頁?太Low !試試 MyBatis 流式查詢,真心強大! 5、告別 swagger-ui ,我選擇了這款神器! 6、JDK/Dubbo/Spring 三種 SPI 機制,誰更好? 7、小團隊真的適合引入Spring Cloud微服務嗎? 8、人臉識別的時候,一定要穿上衣服?。》駝t。。。 9、知乎高贊:PDD和國家電網(wǎng),選哪個? 10、IDEA 中的熱部署神器!
評論
圖片
表情
