基于@Autowired依賴注入的原理分析
@Autowired 是 Spring 框架中的一個(gè)注解,用于自動(dòng)注入依賴。
在 Spring 中,依賴注入(Dependency Injection, DI)是一種設(shè)計(jì)模式,允許在運(yùn)行時(shí)將對(duì)象的依賴關(guān)系(即所需的其他對(duì)象)自動(dòng)提供給它,而不是在代碼中手動(dòng)創(chuàng)建依賴對(duì)象。
1、介紹
以下為注解的結(jié)構(gòu)圖
該注解支持標(biāo)注在字段、構(gòu)造函數(shù)、方法以及參數(shù)上。
注意:
其中的關(guān)鍵屬性 required 默認(rèn)值為 true
,表示如果沒(méi)有合適的 Bean 可以注入,則會(huì)拋出異常。required 如果為false的時(shí)候,表示可以暫時(shí)不實(shí)例化。
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Autowired { boolean required() default true; }
2、實(shí)現(xiàn)原理
1、掃描及注冊(cè) Bean
Spring 在啟動(dòng)時(shí)會(huì)掃描配置的包,找到被@Compoent、@Service、@Repository和@Controller等注解標(biāo)記的類,并將它們注冊(cè)為 Spring 容器中的 Beans。
2、解析依賴
當(dāng) Spring 創(chuàng)建一個(gè) Bean 時(shí),它會(huì)檢查該 Bean 的構(gòu)造函數(shù)、方法或字段中是否有@Autowired注解。
這個(gè)注解表明該字段、構(gòu)造函數(shù)或方法是需要進(jìn)行依賴注入的。
3、類型匹配
Spring 會(huì)根據(jù)變量的類型去容器中查找匹配的 Bean,默認(rèn)情況下,Spring 使用的是按類型自動(dòng)裝配。
- 如果找到唯一的匹配項(xiàng),就會(huì)將其注入。
- 如果有多個(gè)匹配項(xiàng),Spring 會(huì)拋出異常;可以通過(guò)設(shè)置required
=
false或者使用 @Qualifier或者
@Primary來(lái)解決歧義問(wèn)題。 - 如果沒(méi)有匹配項(xiàng),通常會(huì)返回 null(前提是字段是可空的)。
3、使用場(chǎng)景
3.1、背景
當(dāng) Spring 容器檢測(cè)到有多個(gè)同類型的 Bean 可供注入時(shí),默認(rèn)的行為是拋出 NoUniqueBeanDefinitionException 異常。
3.2、處理方式
1、@Qualifier
如果有多個(gè)相同類型的 Bean 時(shí),可以使用 @Qualifier 注解來(lái)明確指定要注入的 Bean。
代碼示例:
@Autowired @Qualifier("specificBeanName") private MyService myService;
2、@Primary注解
1、目的
主要目的是在 Spring 容器中有多個(gè)相同類型的 Bean 存在時(shí),提供一種機(jī)制來(lái)指定哪個(gè) Bean 應(yīng)該被優(yōu)先考慮作為默認(rèn)注入的目標(biāo)。
2、原理
如果其中一個(gè) Bean 被標(biāo)記為 @Primary,那么 Spring 將會(huì)選擇它作為首選項(xiàng)并完成注入過(guò)程。
此邏輯發(fā)生在 Spring 的自動(dòng)裝配階段,在這一階段,容器會(huì)評(píng)估所有可用的候選 Bean,并根據(jù)各種規(guī)則(如 @Primary、@Qualifier 等)做出最終決策。
3、示例
@Configuration public class AppConfig { @Bean @Primary public MyService primaryMyService() { return new MyServiceImpl1(); } @Bean public MyService secondaryMyService() { return new MyServiceImpl2(); } }
在這個(gè)配置文件中,我們有兩個(gè)實(shí)現(xiàn)了 MyService接口的服務(wù)類——MyServiceImpl1 和 MyServiceImpl2。由于我們?cè)诘谝粋€(gè)服務(wù)實(shí)現(xiàn)上加了 @Primary 注解,因此無(wú)論何時(shí)需要注入 MyService類型的對(duì)象,都會(huì)優(yōu)先選取 MyServiceImpl1 實(shí)例。
3.3、替代方式
1. @Inject
@Inject 是 Java EE 提供的標(biāo)準(zhǔn)注解,也可以在 Spring 中使用。
它的功能類似于@Autowired,但語(yǔ)義更加清晰,因?yàn)樗鼉H關(guān)注依賴注入本身而不涉及 Spring 特定的功能。
public class ExampleService { private final AnotherService anotherService; @Inject public ExampleService(AnotherService anotherService) { this.anotherService = anotherService; } }
2. 構(gòu)造函數(shù)注入
這種方式被認(rèn)為是最佳實(shí)踐之一,尤其適用于強(qiáng)制性依賴項(xiàng)。相比于字段注入或 setter 方法注入,構(gòu)造函數(shù)注入能更好地滿足不可變性和測(cè)試需求。
示例:
public class UserService { private final UserRepository userRepository; public UserService(UserRepository userRepository) { // 構(gòu)造函數(shù)注入 this.userRepository = userRepository; } }
3. @Resource
@Resource 是 JNDI 規(guī)范的一部分,通?;诿Q匹配來(lái)查找目標(biāo) Bean。
如果沒(méi)有找到與名字完全一致的 Bean,則會(huì)退回到按類型匹配的方式。相比 @Autowired 更加靈活,因?yàn)榭梢灾苯又付ㄒ⑷氲木唧w資源名。
示例:
public class MovieRecommender { @Resource(name="mainDataSource") // 明確指定了數(shù)據(jù)源的名字 DataSource dataSource; public void recommend() {} }
4、總結(jié)
通俗講解
把@Autowired看成是一種“智能化的手”。當(dāng)你需要一個(gè)助手(依賴對(duì)象)來(lái)完成某個(gè)任務(wù)時(shí),你不需要去親自找尋和雇傭這個(gè)助手,Spring 就像一個(gè)高效的秘書(shū),知道你需要什么助手,會(huì)自動(dòng)給你安排好。
你只需要告訴 Spring 你需要的助手的類型,它就會(huì)負(fù)責(zé)尋找并提供給你,而不需要你關(guān)心具體的細(xì)節(jié)。
這樣做的好處是可以降低代碼的耦合度,讓程序的各個(gè)部分更容易進(jìn)行單元測(cè)試和維護(hù)。只要我們?cè)诖a中聲明需要的依賴,Spring 就會(huì)負(fù)責(zé)去處理這些依賴,讓開(kāi)發(fā)者能更專注于業(yè)務(wù)邏輯的實(shí)現(xiàn)。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot Maven Clean報(bào)錯(cuò)解決方案
這篇文章主要介紹了SpringBoot Maven Clean報(bào)錯(cuò)解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03SpringSecurity解決POST方式下CSRF問(wèn)題
本文主要介紹了SpringSecurity解決POST方式下CSRF問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07基于Java實(shí)現(xiàn)抽獎(jiǎng)系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了基于Java實(shí)現(xiàn)抽獎(jiǎng)系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01Spring框架原理之實(shí)例化bean和@Autowired實(shí)現(xiàn)原理方式
這篇文章主要介紹了Spring框架原理之實(shí)例化bean和@Autowired實(shí)現(xiàn)原理方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-05-05Java Swing實(shí)現(xiàn)JTable檢測(cè)單元格數(shù)據(jù)變更事件的方法示例
這篇文章主要介紹了Java Swing實(shí)現(xiàn)JTable檢測(cè)單元格數(shù)據(jù)變更事件的方法,結(jié)合完整實(shí)例形式分析了Swing實(shí)現(xiàn)JTable檢測(cè)單元格數(shù)據(jù)變更事件過(guò)程中出現(xiàn)的問(wèn)題與相關(guān)解決方法,需要的朋友可以參考下2017-11-11java利用Tabula實(shí)現(xiàn)對(duì)PDF內(nèi)表格數(shù)據(jù)提取
Tabula是一個(gè)開(kāi)源工具,用于從PDF文檔中提取表格數(shù)據(jù),下面小編就來(lái)和大家詳細(xì)介紹一下java如何通過(guò)Tabula對(duì)PDF文件內(nèi)表格進(jìn)行數(shù)據(jù)提取吧2023-09-09