【Spring】淺談spring為什么推薦使用構造器注入

目錄

一、前言
二、常見的三種注入方式
2.1 field注入
2.2 構造器注入
2.3 setter注入
三、構造器注入的好處
四、答疑
五、總結

一、前言

? Spring框架對Java開發的重要性不言而喻,其核心特性就是IOC(Inversion of Control, 控制反轉)和AOP,平時使用最多的就是其中的IOC,我們通過將組件交由Spring的IOC容器管理,將對象的依賴關系由Spring控制,避免硬編碼所造成的過度程序耦合。前幾天的時候,筆者的同事問我為什么要使用構造器的注入方式,我回答說因為Spring文檔推薦這種,而說不出為什么 T^T,后面抽時間了解了一下,下面就是筆者要討論的就是其注入方式。
?

二、常見的三種注入方式

? 筆者為了方便起見就只是用注解的方式注入(現在也很少使用xml了吧,(~ ̄▽ ̄)~)

2.1 field注入

@Controller
public class FooController {@Autowired//@Injectprivate FooService fooService;//簡單的使用例子,下同public List<Foo> listFoo() {return fooService.list();}
}

這種注入方式應該是筆者目前為止開發中見到的最常見的注入方式。原因很簡單:

  1. 注入方式非常簡單:加入要注入的字段,附上@Autowired,即可完成。
  2. 使得整體代碼簡潔明了,看起來美觀大方。
    ?

2.2 構造器注入

@Controller
public class FooController {private final FooService fooService;@Autowiredpublic FooController(FooService fooService) {this.fooService = fooService;}//使用方式上同,略
}

? 在Spring4.x版本中推薦的注入方式就是這種,相較于上面的field注入方式而言,就顯得有點難看,特別是當注入的依賴很多(5個以上)的時候,就會明顯的發現代碼顯得很臃腫。對于從field注入轉過來+有強迫癥的園友 來說,簡直可以說是石樂志 ?(`Д′*)9。對于這一點我們后面再來討論,別急。
?

2.3 setter注入

@Controller
public class FooController {private FooService fooService;//使用方式上同,略@Autowiredpublic void setFooService(FooService fooService) {this.fooService = fooService;}
}

? 在Spring3.x剛推出的時候,推薦使用注入的就是這種,筆者現在也基本沒看到過這種注解方式,寫起來麻煩,當初推薦Spring自然也有他的道理,這里我們引用一下Spring當時的原話:

The Spring team generally advocates setter injection, because large numbers of constructor arguments can get unwieldy, especially when properties are optional. Setter methods also make objects of that class amenable to reconfiguration or re-injection later. Management through?JMX MBeans?is a compelling use case.

Some purists favor constructor-based injection. Supplying all object dependencies means that the object is always returned to client (calling) code in a totally initialized state. The disadvantage is that the object becomes less amenable to reconfiguration and re-injection.

? 咳咳,簡單的翻譯一下就是:構造器注入參數太多了,顯得很笨重,另外setter的方式能用讓類在之后重新配置或者重新注入
?

? 那么后面為什么又換成構造器注入了呢?(喂喂喂,Spring你前一大版本還貶低構造器注入,后面就立刻捧人家了不好吧,不過能用于承認自己的錯誤,才是真正令人稱贊的地方吧 (??????)??)
?

三、構造器注入的好處

? 先來看看Spring在文檔里怎么說:

The Spring team generally advocates constructor injection as it enables one to implement application components as?immutable objects?and to ensure that required dependencies are not?null. Furthermore constructor-injected components are always returned to client (calling) code in a fully initialized state.

? 咳咳,再來簡單的翻譯一下:這個構造器注入的方式啊,能夠保證注入的組件不可變,并且確保需要的依賴不為空。此外,構造器注入的依賴總是能夠在返回客戶端(組件)代碼的時候保證完全初始化的狀態
?

下面來簡單的解釋一下:

  • 依賴不可變:其實說的就是final關鍵字,這里不再多解釋了。不明白的園友可以回去看看Java語法。
  • 依賴不為空(省去了我們對其檢查):當要實例化FooController的時候,由于自己實現了有參數的構造函數,所以不會調用默認構造函數,那么就需要Spring容器傳入所需要的參數,所以就兩種情況:1、有該類型的參數->傳入,OK 。2:無該類型的參數->報錯。所以保證不會為空,Spring總不至于傳一個null進去吧 😦
  • 完全初始化的狀態:這個可以跟上面的依賴不為空結合起來,向構造器傳參之前,要確保注入的內容不為空,那么肯定要調用依賴組件的構造方法完成實例化。而在Java類加載實例化的過程中,構造方法是最后一步(之前如果有父類先初始化父類,然后自己的成員變量,最后才是構造方法,這里不詳細展開。)。所以返回來的都是初始化之后的狀態。

等等,比較完了setter注入與構造器注入的優缺點,你還沒用說使用field注入與構造器的比較呢!那么我們再回頭看一看使用最多的field注入方式:

//承接上面field注入的代碼,假如客戶端代碼使用下面的調用(或者再Junit測試中使用)
//這里只是模擬一下,正常來說我們只會暴露接口給客戶端,不會暴露實現。
FooController fooController = new FooController();
fooController.listFoo(); // -> NullPointerException

如果使用field注入,缺點顯而易見,對于IOC容器以外的環境,除了使用反射來提供它需要的依賴之外,無法復用該實現類。而且將一直是個潛在的隱患,因為你不調用將一直無法發現NPE的存在。

還值得一提另外一點是:使用field注入可能會導致循環依賴,即A里面注入B,B里面又注入A:

public class A {@Autowiredprivate B b;
}public class B {@Autowiredprivate A a;
}

如果使用構造器注入,在spring項目啟動的時候,就會拋出:BeanCurrentlyInCreationException:Requested bean is currently in creation: Is there an unresolvable circular reference?從而提醒你避免循環依賴,如果是field注入的話,啟動的時候不會報錯,在使用那個bean的時候才會報錯


四、答疑

? 好了,相信已經園友們知道了構造器注入的好處,那么回到了在前面提到的問題:

Q1:跟3.x里說的一樣,我要是有大量的依賴要注入,構造方法不會顯得很臃腫嗎?

對于這個問題,說明你的類當中有太多的責任,那么你要好好想一想是不是自己違反了類的單一性職責原則,從而導致有這么多的依賴要注入。

Q2:是不是其他的注入方式都不適合用了呢?

當然不是,存在即是合理!setter的方式既然一開始被Spring推薦肯定是有它的道理,像之前提到的setter的方式能用讓類在之后重新配置或者重新注入,就是其優點之一。除此之外,如果一個依賴有多種實現方式,我們可以使用@Qualifier?,在構造方法里選擇對應的名字注入,也可以使用field或者setter的方式來手動配置要注入的實現。
?

五、總結

? 使用構造器注入的好處:

  1. 保證依賴不可變(final關鍵字)
  2. 保證依賴不為空(省去了我們對其檢查)
  3. 保證返回客戶端(調用)的代碼的時候是完全初始化的狀態
  4. 避免了循環依賴
  5. 提升了代碼的可復用性

另外,當有一個依賴有多個實現的使用,推薦使用field注入或者setter注入的方式來指定注入的類型。這是spring官方博客對setter注入方式和構造器注入的比較。謝謝各位園友觀看,如果有描述不對的地方歡迎指正,與大家共同進步!

參考鏈接:

Setter injection versus constructor injection and the use of @Required

Field Dependency Injection Considered Harmful | Vojtech Ruzicka's Programming Blog

?

作者:joemsu

出處:joemsu - 博客園

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/36038.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/36038.shtml
英文地址,請注明出處:http://en.pswp.cn/news/36038.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

RunLoop

1.CFRunLoopModeRef特征代表RunLoop對象內的運行模式(每個RunLoop對象內存中存在很多種運行模式,每個Mode運行模式下必然包含若干個有效的Source0/Source1/Timer/Observer數據序組) 2.RunLoop對象活躍(操作)啟動時能且僅能選擇某個Mode匹配currentMode(暗示Loop對象的操作運行必…

分類預測 | MATLAB實現BO-BiGRU貝葉斯優化雙向門控循環單元多輸入分類預測

分類預測 | MATLAB實現BO-BiGRU貝葉斯優化雙向門控循環單元多輸入分類預測 目錄 分類預測 | MATLAB實現BO-BiGRU貝葉斯優化雙向門控循環單元多輸入分類預測預測效果基本介紹模型描述程序設計參考資料 預測效果 基本介紹 1.Matlab實現BO-BiGRU貝葉斯優化雙向門控循環單元多特征分…

2.1.2 VisionOS——VisionOS 中的窗口化應用程序

在visionOS中&#xff0c;用戶可以使用窗口來呈現2D或3D內容&#xff0c;或者使用體積來呈現3D內容和對象。Unity 將這些窗口中的應用程序描述為“窗口應用程序”。 默認情況下&#xff0c;如果您構建針對visionOS 平臺的Unity 應用程序而未通過XR 插件管理器啟用PolySpatial …

React - useEffect函數的理解和使用

文章目錄 一&#xff0c;useEffect描述二&#xff0c;它的執行時機三&#xff0c;useEffect分情況使用1&#xff0c;不寫第二個參數 說明監測所有state&#xff0c;其中一個變化就會觸發此函數2&#xff0c;第二個參數如果是[]空數組&#xff0c;說明誰也不監測3&#xff0c;第…

gRPC vs REST:創建API的方法比較

本文對gRPC和REST的特征和區別進行了介紹&#xff0c;這可能是當今創建API最常用的兩種方法。 文章目錄 一、gRPC的介紹 二、什么是REST&#xff1f; 三、什么是gRPC? 四、gRPC和REST的比較 &#xff08;1&#xff09;底層HTTP協議 &#xff08;2&#xff09;支持的數據…

平替 Docker - 玩轉容器新利器 Podman Desktop (視頻)

《OpenShift 4.x HOL教程匯總》 在 podman-desktop 1.2.1 podman 4.4 環境中驗證。 文章目錄 什么是 podman 和 podman-desktop安裝 podman 和 podman-desktop 基本環境Image、Container 和 Pod 的基本操作拉取 Image運行 Container 將 Pod 部署到 Kubernetes安裝 Kind 擴展插…

Python爬蟲——selenium_元素定位

元素定位&#xff1a;自動化要做的就是模擬鼠標和鍵盤來操作這些元素&#xff0c;點擊&#xff0c;輸入等等。操作這些元素前首先要找到它們&#xff0c;WebDriver提供很多定位元素的方法 from selenium import webdriver# 創建瀏覽器對象 path files/chromedriver.exe brows…

【安全】淺談信息安全

信息安全 理解信息安全&#xff0c;要從“信息”、“安全”兩個角度入手。 信息 信息是對客觀世界的反映&#xff0c;表現客觀事物的運動狀態和變化的實質內容。 信息具有可識別、可傳載、可共享、可度量的基本特征。 信息系統 信息系統是獲取&#xff08;收集&#xff0…

中心對稱鏈表

文章目錄 1 題目2 思路2.1 思路一2.2 思路二2.3 考點2.4 擴展 3 實現3.1 思路13.2 思路23.3 完整例子 1 題目 已知長度為n&#xff08;n>1&#xff09;的單鏈表&#xff0c;表頭指針為L&#xff0c;結點結構由data和next兩個域構成&#xff0c;其中data域為字符型&#xff…

Linux RPM包安裝、卸載和升級(rpm命令)詳解

(轉載請刪除括號里的內容) 下面講解一下&#xff0c;如何使用 rpm 命令對 RPM 二進制包進行安裝、卸載和升級操作。我們以安裝 apache 程序為例。 RPM包默認安裝路徑 通常情況下&#xff0c;RPM 包采用系統默認的安裝路徑&#xff0c;所有安裝文件會按照類別分散安裝到下表所…

優漫動游 大廠需要什么樣的ui設計師呢?

通常來說大公司UI設計的流程主要是這樣的&#xff1a;創意-頭腦風暴-策劃方案-交互設計&評審-美術設計&評審-開發實施&#xff0c;不過實際上大多數公司都有自己的一套流程&#xff0c;源于公司的基因、公司組織體系、公司領導風格。一起了解大廠需要什么樣的ui設計師呢…

谷粒商城第十一天-品牌管理中關聯分類

目錄 一、總述 二、前端部分 1. 調整查詢調用 2. 關聯分類 三、后端部分 四、總結 一、總述 之前是在商品的分類管理中直接使用的若依的逆向代碼 有下面的幾個問題&#xff1a; 1. 表格上面的參數填寫之后&#xff0c;都是按照完全匹配進行搜索&#xff0c;沒有模糊匹配…

nodejs實現前后端websocket通信+心跳示例

nodejs后端代碼 server.js //需要安裝ws模塊 npm install ws const WebSocket require("ws") const port 8085const ws new WebSocket.Server({port})ws.on("connection", (socket) > {socket.on("message",(message) > {const da…

自定義hook之首頁數據請求動作封裝 hooks

本例子實現了自定義hook之首頁數據請求動作封裝 hooks&#xff0c;具體代碼如下 export type OrganData {dis: Array<{ disease: string; id: number }>;is_delete: number;name: string;organ_id: number;parent_id: number;sort: number; }; export type SwiperData …

【STM32】簡介

&#x1f6a9; WRITE IN FRONT &#x1f6a9; &#x1f50e; 介紹&#xff1a;"謓澤"正在路上朝著"攻城獅"方向"前進四" &#x1f50e;&#x1f3c5; 榮譽&#xff1a;2021|2022年度博客之星物聯網與嵌入式開發TOP5|TOP4、2021|2022博客之星T…

(2)linux虛擬機配置中文輸入法和如何下載軟件

&#xff08;一&#xff09;配置中文輸入法&#xff1a; 1、sudo apt-get install fcitx&#xff0c;安裝fcitx框架&#xff0c;安裝完成之后&#xff0c;選擇該框架 2、接下來輸入sudo apt-get install fcitx fcitx-googlepinyin&#xff0c;安裝谷歌輸入法之后&#xff0c;重…

WebSocket與消息推送

B/S結構的軟件項目中有時客戶端需要實時的獲得服務器消息&#xff0c;但默認HTTP協議只支持請求響應模式&#xff0c;這樣做可以簡化Web服務器&#xff0c;減少服務器的負擔&#xff0c;加快響應速度&#xff0c;因為服務器不需要與客戶端長時間建立一個通信鏈接&#xff0c;但…

Windows - UWP - 網絡不好的情況下安裝(微軟商店)MicrosoftStore的應用

Windows - UWP - 網絡不好的情況下安裝&#xff08;微軟商店&#xff09;MicrosoftStore的應用 前言 UWP雖然幾乎被微軟拋棄了&#xff0c;但不得不否認UWP應用給用戶帶來的體驗。沙箱的運行方式加上微軟的審核&#xff0c;用戶使用起來非常放心&#xff0c;并且完美契合Wind…

聚類與回歸

聚類 聚類屬于非監督式學習&#xff08;無監督學習&#xff09;&#xff0c;往往不知道因變量。 通過觀察學習&#xff0c;將數據分割成多個簇。 回歸 回歸屬于監督式學習&#xff08;有監督學習&#xff09;&#xff0c;知道因變量。 通過有標簽樣本的學習分類器 聚類和…

前端實現文件預覽功能

前端實現文件預覽功能 ? 需求&#xff1a;實現一個在線預覽pdf、excel、word、圖片等文件的功能。 介紹&#xff1a;支持pdf、xlsx、docx、jpg、png、jpeg。 以下使用Vue3代碼實現所有功能&#xff0c;建議以下的預覽文件標簽可以在外層包裹一層彈窗。 ? 圖片預覽 iframe標簽…