文章目錄
- 1. 引言
- 2. 不推薦使用@Autowired的原因
- 3. Spring提供了三種主要的依賴注入方式
- 3.1. 構造函數注入(Constructor Injection)
- 3.2. Setter方法注入(Setter Injection)
- 3.3. 字段注入(Field Injection)
- 4. 推薦方案
- 5. 參考博客
1. 引言
Field injection is not recommended
意思就是不推薦使用字段注入的方式,不是不推薦@Autowired
注解,以前為了簡便就直接使用
@Resource
代替,程序員都在不斷追求完美。。。 接下來我們實實在在的分析一下為啥不推薦,以及到底推薦那種方式注入。
2. 不推薦使用@Autowired的原因
不推薦使用@Autowired進行字段注入的原因有以下幾點:
-
緊耦合性(Tight Coupling):字段注入將依賴關系直接注入到類的字段上,導致類與依賴之間產生緊密的耦合。這使得代碼難以修改和擴展,并且增加了對具體實現的依賴性。
-
隱藏依賴關系(Hidden Dependencies):字段注入隱藏了類的依賴關系,使代碼不夠透明和可讀。讀取代碼時無法立即知道類所依賴的其他組件或服務。
-
單元測試困難(Difficult Unit Testing):由于字段注入需要依賴容器來自動注入依賴項,導致在編寫單元測試時必須依賴完整的容器環境。這增加了測試的復雜性,并且可能會導致測試變慢或不穩定。
-
難以發現依賴問題(Dependency Issues):字段注入使得依賴可以在運行時更改,這增加了代碼維護的復雜性。同時,如果依賴項沒有正確配置或不存在,就會在運行時出現錯誤,而不是在編譯時就能發現。
相比之下,構造器注入(Constructor Injection)或Setter方法注入(Setter Injection)提供了更好的可測試性、可維護性和代碼清晰度。它們明確列出了類所需的依賴項,并使得依賴關系更加透明和易于理解。這些方法也更容易進行單元測試,且不需要依賴完整的容器環境。
3. Spring提供了三種主要的依賴注入方式
3.1. 構造函數注入(Constructor Injection)
通過構造函數將依賴項傳遞給目標類。這種方式明確聲明了類所需的依賴項,并且使得類的實例在創建時就具備了必要的依賴關系。示例代碼如下:
@Component
public class SLFBClient {private final DataSourceFactory dataSourceFactory;/*** @Autowired 從spring4.3開始可以省略*/
// @Autowiredpublic SLFBClient(DataSourceFactory dataSourceFactory) {this.dataSourceFactory = dataSourceFactory;}}
3.2. Setter方法注入(Setter Injection)
通過Setter方法設置依賴項。這種方式允許使用默認構造函數創建類的實例,然后通過Setter方法來動態設置依賴項。示例代碼如下:
@Component
public class SLFBClient {private DataSourceFactory dataSourceFactory;/*** @Autowired 從spring4.3開始可以省略*/
// @Autowiredpublic void setDataSourceFactory(DataSourceFactory dataSourceFactory) {this.dataSourceFactory = dataSourceFactory;}
}
3.3. 字段注入(Field Injection)
通過直接將依賴項注入到類的字段上。這種方式最簡潔,但也最不推薦使用(在之前的回答中已經詳細解釋了原因)。示例代碼如下:
@Component
public class SLFBClient {// @Autowired
// @Resource@Injectprivate DataSourceFactory dataSourceFactory;}
@Autowired、@Resource和@Inject
是用于依賴注入的常見注解,它們在使用方式和一些細節上有一些區別。
-
@Autowired:
- 來自
Spring
框架。 - 默認按照類型(
byType
)進行依賴注入,會嘗試將匹配的bean
自動注入到目標字段、構造函數或方法參數中。 - 可以與
@Qualifier
一起使用,通過指定bean
的名稱或限定符來進一步指定要注入的bean。 @Autowired
是非強制性的,可以在某些情況下將依賴項標記為可選。
- 來自
-
@Resource:
- 是
Java EE
的標準注解,也可以被Spring
框架支持。 - 默認按照名稱(
byName
)進行依賴注入,通過指定bean
的名稱來解析并注入匹配的bean
。 - 可以使用
name
屬性指定要注入的bean
的名稱。 @Resource
是強制性的,要求找到匹配的bean
進行注入,否則會拋出異常。
- 是
-
@Inject:
- 是
Java CDI(Contexts and Dependency Injection)
規范的一部分,可以由Java EE
和一些其他框架(如Spring
)支持。 - 默認按照類型(
byType
)進行依賴注入,使用與@Autowired
類似的機制。 - 不支持
required
屬性,即所有注入都被視為必需的。 - 可以與
@Qualifier
一起使用,通過指定bean
的名稱或限定符來進一步指定要注入的bean
。
- 是
總結:
@Autowired
是Spring
特有的注解,默認按類型進行依賴注入。@Resource
是Java EE
的標準注解,可被Spring
支持,默認按名稱進行依賴注入。@Inject
是Java CDI
規范的注解,也可被Spring
等框架支持,默認按類型進行依賴注入。- 三個注解都可以與
@Qualifier
一起使用來指定具體要注入的bean
。 @Autowired
和@Inject
在功能上相似,而@Resource
功能稍有不同,但它們通常可以互相替代使用。
需要注意的是,具體在Spring
中使用哪個注解,可以根據項目的需求、框架的支持以及個人偏好來決定。
4. 推薦方案
使用構造器注入的好處:
- 保證依賴不可變(final關鍵字)
- 保證依賴不為空(省去了我們對其檢查)
- 保證返回客戶端(調用)的代碼的時候是完全初始化的狀態
- 避免了循環依賴
- 提升了代碼的可復用性
推薦使用Lombok
中的@RequiredArgsConstructor
注解
@Component
@RequiredArgsConstructor
public class SLFBClient {private final DataSourceFactory dataSourceFactory;}
接下來我們探討一下Lombok
的@NoArgsConstructor, @RequiredArgsConstructor, @AllArgsContructor
三個注解
@NoArgsConstructor:
- 自動生成一個無參構造函數。
- 適用于不需要傳入參數的情況。
import lombok.NoArgsConstructor;@NoArgsConstructor
public class MyClass {// Fields and methods
}
2. @RequiredArgsConstructor:
- 自動生成一個包含所有被標記為final和@NonNull的字段的構造函數。
- 適用于只關注部分字段并確保這些字段非空的情況。
import lombok.NonNull;
import lombok.RequiredArgsConstructor;@RequiredArgsConstructor
public class MyClass {private final String name;@NonNullprivate final Integer age;// Other fields and methods
}
@AllArgsConstructor:
- 自動生成一個包含所有類字段的構造函數。
- 適用于需要一次性傳遞所有字段值的情況。
import lombok.AllArgsConstructor;@AllArgsConstructor
public class MyClass {private String name;private int age;// Other fields and methods
}
5. 參考博客
Field Dependency Injection Considered Harmful
Field injection is not recommended(Spring團隊不推薦使用Field注入)
【Spring】淺談spring為什么推薦使用構造器注入