Spring DI詳解--依賴注入的三種方式及優缺點分析

一、什么是DI?

DI(Dependency Injection,依賴注入)是 IoC(控制反轉) 思想的最典型實現方式,核心目標只有一個:

讓對象不再自己“找”依賴,而是由外部容器“送”依賴進來,從而徹底解耦。

一句話看懂DI:以創建狗這個實例舉例

過去:自己 new

//當我們創建狗這個實例時,需要通過 new 關鍵實現對象的實例化
public class testDiBlog {public static void main(String[] args) {Dog dog=new Dog();}
}

現在:依賴注入(這里是構造注入)

public class testDiBlog {private Dog dog; //只聲明,不創建,容器將實例送進來public testDiBlog(Dog dog) { //解耦this.dog = dog;}
}

spring 依賴注入的方式有三種,上述是其中一種(構造方法注入)其他兩種分別是:屬性注入、Setter方法注入,接下來讓我們仔細的來看看這三種注入方式該如何進行書寫。

二、依賴注入

?首先創建 Dog 類,創建 run 方法

@Getter @Setter
public class Dog {private String name;public void run(){System.out.println("running....");}
}

將 Dog 類交給Spring 進行管理(PS:@Bean(方法注解) 需要搭配 五大類注解(@Controller、@Service、@Component、@Repository、@Configuration)使用)

@Configuration
public class DogConfig {@Beanpublic Dog dog(){Dog dog=new Dog();dog.setName("旺財");return dog;}}

對上面代碼的解釋:

  1. Spring 管理的對象 = dog() 方法返回的那只 Dog 實例

  2. 類型 = Dog(返回值類型)

  3. Bean 名稱 = dog(默認等于方法名,等價于 @Bean("dog")

2.1 屬性注入

屬性注入是使用 @Autowired 注解

@SpringBootTest
class SpringPrincipleApplicationTests {@Autowired //屬性注入,使用注解Dog dog; //拿到 dog 實例@Testvoid DogTest(){dog.run(); //調用實例方法}
}

當 @Autowired 被注釋掉時,此時的執行結果顯示空指針異常

2.2 構造方法注入

使用構造方法注入Bean

//注入Bean
@Controller
public class TestDog2 {private Dog dog;public TestDog2(Dog dog) { this.dog = dog;}public void run(){dog.run();}}//啟動spring(此段代碼與上面的代碼在idea中不是位于同一個類中,這里是為了方便觀看寫在一起)@SpringBootApplication
public class SpringPrincipleApplication {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(SpringPrincipleApplication.class, args);TestDog2 bean = context.getBean(TestDog2.class);bean.run();}
}

執行結果如下:

此時 TestDog2 方法中只有一個構造方法,我們知道如果當類中沒有構造方法時,編譯器會默認調用一個無參的構造方法,既然現在是構造方法注入,如果現在我們將這個默認的無參構造函數加上,程序執行的結果還會是這樣嗎??

@Controller
public class TestDog2 {private Dog dog;public TestDog2() {System.out.println("無參構造方法...");//這里打印一下日志為了方便觀察}public TestDog2(Dog dog) {System.out.println("有參構造方法...");this.dog = dog;}public void run(){dog.run();}}

結果執行如下:

可以看到這里執行了這個無參的構造方法,且報出了空指針異常,報空指針異常是因為沒有執行下面的有參構造方法,dog 沒有進行賦值,后面調用了 dog 的 run 方法,此時 dog 為null。此時的解決方法是在有參構造方法上添加 @Autowried 注解,當添加注解后會告訴 Spring? 默認幫我執行帶注解的構造方法。修改如下:

@Controller
public class TestDog2 {private Dog dog;public TestDog2() {System.out.println("無參構造方法...");}@Autowired //添加注解,指定默認的構造方法public TestDog2(Dog dog) {System.out.println("有參構造方法...");this.dog = dog;}public void run(){dog.run();}}

執行結果如下:

不知道有沒有小伙伴注意到下圖這里的 dog 參數,在代碼中我們沒有給它傳遞參數,這個 dog 是從哪兒來的呢?構造函數注入時,Spring 必須能把所有參數都解析成容器里的 Bean,去進行查找,如果找到了就進行相應的賦值,只要有一個參數匹配不到Bean(類型+名稱)啟動就會失敗并拋出:

No qualifying bean of type 'xxx.xxx' available: expected at least 1 bean which qualifies as autowire candidate.

2.3 Setter方法注入

Setter 注?和屬性的 Setter ?法實現類似,只不過在設置 set ?法的時候需要加上 @Autowired 注解

@Controller
public class TestDog3 {private Dog dog;@Autowiredpublic void setDog(Dog dog) {this.dog = dog;}public void run(){System.out.println("這是TestDog3....");dog.run();}
}

執行結果如下:

去掉 @Autowired 后,執行結果如下:

三、優缺點分析

再進行優缺點分析之前,主播先提出一個問題,不知道小伙伴們是否還記得 final 關鍵字修飾得變量有什么特點?我們知道被 final 修飾得變量初始化要么再最初定義變量得時候就初始化,要么就是再構造器中被初始化。當我們回想起這一點后我們再來看這三種注入方式,不難發現,只有構造方法注入可以注入 final 修飾得變量,Setter 和屬性注入不可以注入 final 修飾得變量。

3.1 原因分析

首先我們來看看 spring 創建對象的流程:① 分配空白內存 → ② 默認值(0/null) → ③ 構造代碼塊/構造器(final 唯一合法寫入點) → ④ 對象頭設置 → ⑤ 返回引用。這里一旦構造器返回,final 修飾的字段就進入了“只讀”模式,后續任何賦值都會編譯失敗。

public class User {private final String name;public User(String name){ this.name = name; } // 合法public void setName(String name){ this.name = name; } // ? 編譯錯誤
}

屬性注入發生的時間段在返回引用之后,也就是流程⑤之后,spring 屬性注入時機(源碼級)

//java源碼
AbstractAutowireCapableBeanFactory#populateBean
Field field = UserController.class.getDeclaredField("userService");
field.set(controllerInstance, userServiceImpl);   // 反射 putfield
  • JVM 校驗:發現對 final 字段 執行 putfield → 直接拋

    java.lang.IllegalAccessError: Update to final field

3.2 spring三種注入的時間軸

注入方式觸發時刻對象狀態能否再寫final
屬性注入第⑤步:對象引用返回對象創建完成? 拒絕
Setter注入第⑤步:對象引用返回對象創建完成? 拒絕
構造器注入第③步:構造器里對象正在創建? 允許

3.3 時間軸再次對照

步驟時刻final可否寫入spring屬性注入是否在此
類加載類裝載模板? ? ??? ? 不參與
new構造器類? 唯一機會?? ? 尚未開始
構造器返回對象已創建? 鎖死?? ? 尚未開始
populateBean反射字段賦值? 拋錯? 在這里發生 →失敗

3.4 優缺點總結

通過上面的分析我們可以總結出三種注入方式的優缺點

方式優點缺點
屬性注入
簡潔,使??便
?只能?于 IoC 容器,如果是? IoC 容器不可?,并且只有在使?的時候才會出現 NPE(空指
針異常)
? 不能注?一個Final修飾的屬性
構造方法注入
?可以注?final修飾的屬性
?注?的對象不會被修改
? 依賴對象在使?前?定會被完全初始化,因為依賴是在類的構造?法中執?的,?構造?法 是在類加載階段就會執?的?法.
? 通?性好, 構造?法是JDK?持的, 所以更換任何框架,他都是適?的
? 注?多個對象時, 代碼會?較繁瑣
Setter注入
?便在類實例之后, 重新對該對象進?配置或者注?
?不能注??個Final修飾的屬性
? 注?對象可能會被改變, 因為setter?法可能會被多次調?,就有被修改的風險

四、@Autowired存在的問題

當同一個類型的對象有多個的時候,此時又會發生什么狀況呢??

@Component
public class TestUser {@Beanpublic User user1(){User user=new User();user.setName("圖圖");return user;  //對象1}@Beanpublic User user2(){User user=new User();user.setName("小美");return user;  //對象2}
}

錯誤提示:這里不只有一個User Bean對象,當同?類型存在多個bean時, 使?@Autowired會存在問題。

如何解決上述問題呢?Spring提供了以下?種解決?案: ? @Primary ? @Qualifier ? @Resource

4.1 解決方法之 @Primary

使?@Primary注解:當存在多個相同類型的Bean注?時,加上@Primary注解,來確定默認的實現.

@Component
public class TestUser {@Primary  //指定該 Bean為默認的 Bean@Beanpublic User user1(){User user=new User();user.setName("圖圖");return user;}@Beanpublic User user2(){User user=new User();user.setName("小美");return user;}
}
@Controller
public class UserController {@Autowiredprivate User user; //注入成功沒有報錯public void desc(){user.desc();}
}

注意:@Qualifier注解不能單獨使?,必須配合@Autowired使?

4.2 解決方法之?@Qualifier

使? @Qualifier 注解:指定當前要注?的 bean 對象。 在 @Qualifier 的 value 屬性中,指定注?的 bean 的名稱。?
@Controller
public class UserController {@Qualifier("user1")  //添加注入指定Bean@Autowiredprivate User user;public void desc(){user.desc();}
}

4.3?解決方法之?@Resource

使?@Resource注解:是按照bean的名稱進?注?。通過name屬性指定要注?的bean的名稱。

@Controller
public class UserController {@Resource(name= "user1")private User user;public void desc(){user.desc();}
}

五、@Resource 和?@Autowired 的區別

1. @Autowired 是spring框架提供的注解,?@Resource是JDK提供的注解
2. @Autowired 默認是按照類型注?,當同類型有多個實例時,也會根據名稱去進行匹配,?@Resource是按照名稱注?,按名稱肯定也會需要類型是一致的.
3. 相?于 @Autowired 來說,@Resource ?持更多的參數設置,例如 name 設置,根據名稱獲取 Bean

六、@Autowired裝配順序


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

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

相關文章

PPT中如何將設置的文本框邊距設為默認

通常,在PPT中插入的文本框邊距比較窄,線條和填充都為空,我們可以根據自己的需要調整文本框的邊距,以及填充顏色、線條顏色和樣式等,并且把這個設置為默認的文本框,然后就可以直接插入相同邊距和樣式的文本框…

瘋狂星期四文案網第61天運營日記

網站運營第61天,點擊觀站: 瘋狂星期四 crazy-thursday.com 全網最全的瘋狂星期四文案網站 運營報告 今日訪問量 今日搜索引擎收錄情況 收錄好像便正常了,準備加快發布頻率了

開源容器管理平臺Rancher

Rancher 是一個開源的 容器管理平臺,用于簡化 Kubernetes 和 Docker 的部署、運維和安全管理。它提供了一套用戶友好的工具,幫助開發者和運維團隊在企業環境中高效地管理容器化應用。核心功能Kubernetes 管理 支持多集群管理(本地、云、邊緣等…

AI在目前會議直播系統中應用

AI在目前會議直播系統中有多種使用場景,以下是一些常見的例子: 會議內容實時處理 實時轉寫與翻譯:借助AI語音識別算法,會議直播系統可實現語音的實時轉寫,支持多種語言和方言,轉寫準確率達98%以上。同時,部分系統還配備實時翻譯功能,將發言語音實時翻譯成多種語言字幕,…

網絡安全A模塊專項練習任務十解析

任務十:Linux操作系統安全配置-3任務環境說明: (Linux)系統:用戶名root,密碼1234561.設置賬戶密碼有效期,密碼最大有效期為30,可修改密碼最小天數為5,密碼長度為6,密碼失效前4天通知…

WorkMagic-AI驅動的營銷SaaS服務平臺

本文轉載自:WorkMagic-AI驅動的營銷SaaS服務平臺 - Hello123工具導航 ** 一、🤖 WorkMagic:跨境電商的 AI 營銷自動化神器 WorkMagic 是一家專注于為跨境電商提供AI 驅動營銷自動化解決方案的 SaaS 平臺,成立于 2023 年。它通過…

Java 線程重點 面試筆記(線程狀態,安全停止線程..)

包括線程狀態、Thread.yield()、Thread.join()、線程安全停止、標志位、中斷等,都是線程這塊秋招的重點。1. 線程狀態(Thread.State)Java 中線程有 6 種狀態:狀態含義進入條件NEW新建狀態Thread t new Thread(...);RUNNABLE可運行…

Zigbee:Polling 終端設備的睡眠機制和功耗

一、Zigbee 設備類型與功耗基礎 首先,Zigbee網絡中的設備角色決定了其功耗特性。Zigbee定義了三種邏輯設備類型: 協調器 (Coordinator)??:網絡的中心,必須始終供電,不能睡眠。功耗最高。 路由器 (Router)??:負責中繼數據,擴展網絡范圍。通常也需持續供電,以保持網…

Python迭代協議完全指南:從基礎到高并發系統實現

引言:迭代協議的核心價值在Python編程中,迭代協議是構建高效、靈活數據結構的基石。根據2024年Python開發者調查報告:92%的高級數據結構依賴迭代協議85%的數據處理框架基于迭代協議構建78%的并發系統使用自定義迭代器65%的內存優化方案通過迭…

vsan高可用:確保可訪問性、全部數據遷移,兩種類型權衡

目錄1.如果我3臺機器,其中有1臺機器突然故障,那么走的是保證可用,還是全量數據遷移?這個怎么算?一、先明確:故障場景 vs 維護場景的核心差異二、3臺主機故障時,vSAN的具體處理邏輯(為…

51單片機1(單片機基礎,LED,數碼管)

1.嵌入式嵌入式(Embedded)指的是一種專用計算機系統,它被"嵌入"或內建到一個更大的設備、產品或系統中,作為其核心控制部分,專門用于執行特定的任務或功能。通俗來講就是以應用為中心,以計算機技…

Aerobits-用于 sUAS 和 UTM/U-Space 的微型 ADS-B 技術(收發器/接收器)和無人機跟蹤應答器

Aerobits-用于 sUAS 和 UTM/U-Space 的微型 ADS-B 技術(收發器/接收器)和無人機跟蹤應答器Aerobits 是一家專門為無人機 (UAV) 和無人駕駛飛機開發微型應答器和航空電子系統的公司。我們的硬件和軟件解決方案基于專利技術,采用極低 SWaP 封裝…

Spring Security資源服務器在高并發場景下的認證性能優化實踐指南

Spring Security資源服務器在高并發場景下的認證性能優化實踐指南 摘要:本文從原理與實踐兩個層面,深入解析Spring Security資源服務器在高并發場景下的認證性能優化策略,通過關鍵源碼解讀與實際示例,幫助開發者有效提升系統吞吐與…

SQL Server事務隔離級別

SQL Server 提供了多個事務隔離級別,用于控制并發事務如何訪問和修改數據時的可見性、鎖定行為以及可能遇到的并發問題(如臟讀、不可重復讀、幻讀)。這些級別在數據一致性、并發性能和鎖定開銷之間進行權衡。 以下是 SQL Server 支持的主要隔…

DeepSeek R1大模型微調實戰-llama-factory的安裝與使用

文章目錄概要1.安裝必要的環境2.安裝 PyTorch3.安裝 Transformers 和 Datasets4.克隆 LLaMA Factory 倉庫和安裝LLaMA Factory5.準備數據和模型配置6.運行 LLaMA Factory7.監控和調整8.后續步驟概要 LLaMA Factory 是一個簡單易用且高效的大型語言模型訓練與微調平臺。通過它&…

IDE mac M芯片安裝報錯:如何解決“InsCode.app 已損壞”,無法打開

IDE mac M芯片安裝報錯:如何解決“InsCode.app 已損壞”,無法打開 摘要 在 macOS 上安裝并運行 InsCode IDE 時,不少開發者會遇到這樣的報錯: “InsCode.app 已損壞,無法打開。您應該將它移到廢紙簍。” 這種情況在 …

EasyExcel:阿里開源的高效 Excel 處理工具,輕松解決 POI 內存溢出問題

在日常開發中,Excel 文件的導入導出是非常常見的需求。無論是數據批量導入、報表生成還是數據備份,我們都離不開對 Excel 的操作。但傳統的 POI 框架在處理大數據量 Excel 時,常常會遇到內存溢出的問題,讓開發者頭疼不已。 今天給…

軟件啟動時加配置文件 vs 不加配置文件

一、基本概念不加配置文件啟動直接執行啟動命令,使用軟件自帶的默認參數。方便、快速,適合測試環境。缺點:靈活性差、配置不可控、不安全。redis-server zookeeper-server-start.sh kafka-server-start.sh指定配置文件啟動啟動時加載外部配置…

[ubuntu][C++]onnxruntime安裝cpu版本后測試代碼

下載官方預編譯包后,怎么用呢。可以參考這個源碼跑測試環境:ubuntu22.04onnxruntime1.18.0測試代碼:CMakeLists.txtcmake_minimum_required(VERSION 3.12) project(onnx_test)# 設置C標準 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD…

棧:有效的括號

題目描述:給定一個只包含‘[’,{,(,),},]的字符串,判斷該字符串是否括號有效。 括號有效的要求是: 每個左括號都有對應的右括號。每個右括號都有對應的左括號。左括號必須以正確的順序閉合。 示例 1: 輸入:s "…