Spring 官方已不推薦使用 Autowired 欄位/內容註入 bean,一些大公司的新計畫也明令禁止使用了。
說明
最近公司升級框架,由原來的 spring framework 3.0 升級到 5.0,然後寫程式碼的時候突然發現 idea 在內容註入的 @Autowired 註解上給出警告提示,就像下面這樣的,也是挺懵逼的,畢竟這麽寫也很多年了。
Field injection is not recommended ❞
查閱了相關文件了解了一下,原來這個提示是 spring framework 4.0 以後開始出現的,spring 4.0 開始就不推薦使用內容註入,改為推薦構造器註入和 setter 註入。
下面將展示了 spring 框架可以使用的不同型別的依賴註入,以及每種依賴註入的適用情況。
依賴註入的型別
盡管針對 spring framework 5.1.3 的文件只定義了兩種主要的依賴註入型別,但實際上有三種:
基於構造器的依賴註入
基於 setter 的依賴註入
基於欄位的依賴註入
其中基於欄位的依賴註入被廣泛使用,但是 idea 或者其他靜態代分碼析工具會給出提示資訊,不推薦使用。
甚至可以在一些 Spring 官方指南中看到這種註入方法:
2.1 基於構造器的依賴註入
在基於建構函式的依賴註入中,類建構函式被標註為 @Autowired,並包含了許多與要註入的物件相關的參數。
@Component
public classConstructorBasedInjection{
privatefinal InjectedBean injectedBean;
@Autowired
publicConstructorBasedInjection(InjectedBean injectedBean){
this.injectedBean = injectedBean;
}
}
然後在spring官方文件中,@Autowired 註解也是可以省去的。
public classSimpleMovieLister{
// the SimpleMovieLister has a dependency on a MovieFinder
private MovieFinder movieFinder;
// a constructor so that the Spring container can inject a MovieFinder
publicSimpleMovieLister(MovieFinder movieFinder){
this.movieFinder = movieFinder;
}
// business logic that actually uses the injected MovieFinder is omitted...
}
基於建構函式註入的主要優點是可以將需要註入的欄位聲明為 final, 使得它們會在類例項化期間被初始化,這對於所需的依賴項很方便。
2.2 基於 Setter 的依賴註入
在基於 setter 的依賴註入中,setter 方法被標註為 @Autowired。一旦使用無參數建構函式或無參數靜態工廠方法例項化 Bean,為了註入 Bean 的依賴項,Spring 容器將呼叫這些 setter 方法。
@Component
public classSetterBasedInjection{
private InjectedBean injectedBean;
@Autowired
publicvoidsetInjectedBean(InjectedBean injectedBean){
this.injectedBean = injectedBean;
}
}
和基於構造器的依賴註入一樣,在官方文件中,基於 Setter 的依賴註入中的 @Autowired 也可以省去。
public classSimpleMovieLister{
// the SimpleMovieLister has a dependency on the MovieFinder
private MovieFinder movieFinder;
// a setter method so that the Spring container can inject a MovieFinder
publicvoidsetMovieFinder(MovieFinder movieFinder){
this.movieFinder = movieFinder;
}
// business logic that actually uses the injected MovieFinder is omitted...
}
2.3 基於內容的依賴註入
在基於內容的依賴註入中,欄位/內容被標註為 @Autowired。一旦類被例項化,Spring 容器將設定這些欄位。
@Component
public classFieldBasedInjection{
@Autowired
private InjectedBean injectedBean;
}
正如所看到的,這是依賴註入最幹凈的方法,因為它避免了添加樣板程式碼,並且不需要聲明類的建構函式。程式碼看起來很幹凈簡潔,但是正如程式碼檢查器已經向我們暗示的那樣,這種方法有一些缺點。
基於欄位的依賴註入缺陷
3.1 不允許聲明不可變域
基於欄位的依賴註入在聲明為 final/immutable 的欄位上不起作用,因為這些欄位必須在類例項化時例項化。聲明不可變依賴項的唯一方法是使用基於構造器的依賴註入。
3.2 容易違反單一職責設計原則
在物件導向的編程中,五大設計原則SOLID被廣泛套用,(國內一般為六大設計原則),用以提高程式碼的重用性,可讀性,可靠性和可維護性。
S 在 SOLID 中代表單一職責原則,即一個類應該只負責一項職責,這個類提供的所有服務都應該只為它負責的職責服務。
使用基於欄位的依賴註入,高頻使用的類隨著時間的推移,我們會在類中逐漸添加越來越多的依賴項,我們用著很爽,很容易忽略類中的依賴已經太多了。但是如果使用基於建構函式的依賴註入,隨著越來越多的依賴項被添加到類中,建構函式會變得越來越大,我們一眼就可以察覺到哪裏不對勁。
有一個有超過10個參數的建構函式是一個明顯的訊號,表明類已經轉變一個大而全的功能合集,需要將類分割成更小、更容易維護的塊。
因此,盡管內容註入並不是破壞單一責任原則的直接原因,但它隱藏了訊號,使我們很容易忽略這些訊號。
3.3 與依賴註入容器緊密耦合
使用基於欄位的依賴註入的主要原因是為了避免 getter 和 setter 的樣板程式碼或為類建立建構函式。最後,這意味著設定這些欄位的唯一方法是透過Spring容器例項化類並使用反射註入它們,否則欄位將保持 null。
依賴註入設計模式將類依賴項的建立與類本身分離開來,並將此責任轉移到類註入容器,從而允許程式設計解耦,並遵循單一職責和依賴項倒置原則(同樣可靠)。因此,透過自動裝配(autowiring)欄位來實作的類的解耦,最終會因為再次與類註入容器(在本例中是 Spring)耦合而遺失,從而使類在Spring容器之外變得無用。
這意味著,如果您想在應用程式容器之外使用您的類,例如用於單元測試,您將被迫使用 Spring 容器來例項化您的類,因為沒有其他可能的方法(除了反射)來設定自動裝配欄位。
3.4 隱藏依賴關系
在使用依賴註入時,受影響的類應該使用公共介面清楚地公開這些依賴項,方法是在建構函式中公開所需的依賴項,或者使用方法(setter)公開可選的依賴項。當使用基於欄位的依賴註入時,實質上是將這些依賴對外隱藏了。
總結
我們已經看到,基於欄位的註入應該盡可能地避免,因為它有許多缺點,無論它看起來多麽優雅。推薦的方法是使用基於建構函式和基於setter的依賴註入。對於必需的依賴,建議使用基於建構函式的註入,設定它們為不可變的,並防止它們為 null。對於可選的依賴項,建議使用基於 setter 的註入。
參考文件
Field injection is not recommended – Spring IOC by Marc Nuri
spring官方文件 1.4. Dependencies
來源:juejin.cn/post/7275009721760432168
>>
END
精品資料,超贊福利,免費領
微信掃碼/長按辨識 添加【技術交流群】
群內每天分享精品學習資料
最近開發整理了一個用於速刷面試題的小程式;其中收錄了上千道常見面試題及答案(包含基礎、並行、JVM、MySQL、Redis、Spring、SpringMVC、SpringBoot、SpringCloud、訊息佇列等多個型別),歡迎您的使用。
👇👇
👇點選"閱讀原文",獲取更多資料(持續更新中)