Spring探索:既生Resource,何生Autowired?

danny_2018發表於2023-03-30

提到Spring依賴注入,大家最先想到應該是@Resource和@Autowired,很多文章只是講解了功能上的區別,對於Spring為什麼要支援兩個這麼類似的註解卻未提到,屬於知其然而不知其所以然。不知大家在使用這兩個註解的時候有沒有想過,@Resource又支援名字又支援型別,還要@Autowired幹嘛,難道是Spring官方沒事做了?

真的是沒事做了嗎?讀了本文你將會了解到:

@Resource和@Autowired來源

Spring官方為什麼會支援這兩個功能如此相似的註解?

為什麼@Autowired屬性注入的時候Idea會曝出黃色的警告?

@Resource和@Autowired推薦用法

來源

既然要弄清楚,就要先了解他們的身世。

@Resource 於 2006年5月11日隨著JSR 250 釋出 ,官方解釋是:

Resource 註釋標記了應用程式需要的資源。該註解可以應用於應用程式元件類,或元件類的欄位或方法。當註解應用於欄位或方法時,容器將在元件初始化時將所請求資源的例項注入到應用程式元件中。如果註釋應用於元件類,則註釋宣告應用程式將在執行時查詢的資源。

可以看到它類似一個定義,而由其他的元件或框架自由實現。

@Autowired 於 2007年11月19日隨著Spring2.5釋出,同時官方也對@Resource進行了支援。@Autowired的官方解釋是:

將建構函式、欄位、設定方法或配置方法標記為由 Spring 的依賴注入工具自動裝配。

可以看到,@Autowired 是 Spring的親兒子,而@Resource是Spring對它定義的一種實現,它們的功能如此相似。那麼為什麼要支援了@Resource,又要自己搞個@Autowired呢?

對此專門查了一下Spring2.5的官方文件,文件中有一段這麼說到:

However, Spring 2.5 dramatically changes the landscape. As described above, the autowiring choices have now been extended with support for the JSR-250 @Resource annotation to enable autowiring of named resources on a per-method or per-field basis. However, the @Resource annotation alone does have some limitations. Spring 2.5 therefore introduces an @Autowired annotation to further increase the level of control.

大概的意思是說,Spring2.5 支援註解自動裝配啦, 現已經支援JSR-250 @Resource 基於每個方法或每個欄位的命名資源的自動裝配,但是隻有@Resource是不行的,我們還推出了“粒度”更大的@Autowired,來覆蓋更多場景了。

嗯哼,那麼官方說的“粒度”就是關鍵了,那“粒度”指的是什麼呢”?

既生“@Resource”,何生“@Autowired”

要想找到粒度是什麼,我們先從兩個註解的功能下手

@Autowired

型別注入

@Resource

名字注入優先,找不到名字找型別

論功能的“粒度”,@Resource已經包含@Autowired了啊,“粒度”更大啊,難道是Spring2.5的時候還不是這樣?我又去翻了下Spring2.5文件,上面明確的寫到:

When using @Resource without an explicitly provided name, if no Spring-managed object is found for the default name, the injection mechanism will fallback to a type-match.

這不是和現在一樣的嗎,我此時凌亂了。那麼“粒度”到底指的是什麼?在混跡眾多論壇後,其中stackoverflow的一段話引起了我的注意:

Both @Autowired and @Resource work equally well. But there is a conceptual difference or a difference in the meaning.

@Resource

means get me a known resource by name. The name is extracted from the name of the annotated setter or field, or it is taken from the name-Parameter.

@Inject

or

@Autowired

try to wire in a suitable other component by type.

So, basically these are two quite distinct concepts. Unfortunately the Spring-Implementation of @Resource has a built-in fallback, which kicks in when resolution by-name fails. In this case, it falls back to the @Autowired-kind resolution by-type. While this fallback is convenient, IMHO it causes a lot of confusion, because people are.

大概的意思是:Spring雖然實現了兩個功能類似的,但是存在概念上的差異或含義上的差異:

@Resource 這按名稱給我一個確定已知的資源。

@Autowired 嘗試按型別連線合適的其他元件。

但是@Resource當按名稱解析失敗時會啟動。在這種情況下,它會按型別解析,引起概念上的混亂,因為開發者沒有意識到概念上的差異,而是傾向於使用@Resource基於型別的自動裝配。

原來Spring官方說的“粒度”是指“資源範圍”,@Resource找尋的是確定的已知的資源,相當於給你一個座標,你直接去找。@Autowired是在一片區域裡面嘗試搜尋合適的資源。

所以上面的問題答案已經基本明確了。

Spring為什麼會支援兩個功能相似的註解呢?

它們的概念不同,@Resource更傾向於找已知資源,而Autowired傾向於嘗試按型別搜尋資源。

方便其他框架遷移,@Resource是一種規範,只要符合JSR-250規範的其他框架,Spring就可以相容。

既然@Resource更傾向於找已知資源,為什麼也有按型別注入的功能?

個人猜測:可能是為了相容從Spring切換到其他框架,開發者就算只使用Resource也是保持Spring強大的依賴注入功能。

Spring的區別對待

看到這相信大家對使用@Resource還是@Autowired有了自己的見解。在日常寫程式碼中有個小細節不知道大家有沒有注意到,使用@Autowired在屬性上的時候Idea會曝出黃色的警告,並且推薦我們使用構造方法注入,而Resource就不會,這是為什麼呢?警告如下:

為什麼@Autowired在屬性上的時候Idea會曝出黃色的警告,並且推薦我們使用構造方法注入?

其實Spring文件中已經給出了答案,主要有這幾點:

1、宣告不了常量的屬性

基於屬性的依賴注入不適用於宣告為 final 的欄位,因為此欄位必須在類例項化時去例項化。宣告不可變依賴項的唯一方法是使用基於建構函式的依賴項注入。

2、容易忽視類的單一原則

一個類應該只負責軟體應用程式功能的單個部分,並且它的所有服務都應該與該職責緊密結合。如果使用屬性的依賴注入,在你的類中很容易有很多依賴,一切看起來都很正常。但是如果改用基於建構函式的依賴注入,隨著更多的依賴被新增到你的類中,建構函式會變得越來越大,程式碼開始就開始出現“異味”,發出明確的訊號表明有問題。具有超過十個引數的建構函式清楚地表明該類有太多的依賴,讓你不得不注意該類的單一問題了。因此,屬性注入雖然不直接打破單一原則,但它卻可以幫你忽視單一原則。

3、迴圈依賴問題

A類透過建構函式注入需要B類的例項,B類透過建構函式注入需要A類的例項。如果你為類 A 和 B 配置 bean 以相互注入,使用構造方法就能很快發現。

4、依賴注入強依賴Spring容器

如果您想在容器之外使用這的類,例如用於單元測試,不得不使用 Spring 容器來例項化它,因為沒有其他可能的方法(除了反射)來設定自動裝配的欄位。

為什麼@Resource沒有呢?

在官方文件中,我沒有找到答案,查了一些資料說是:@Autowired 是 Spring 提供的,一旦切換到別的 IoC 框架,就無法支援注入了. 而@Resource 是 JSR-250 提供的,它是 Java 標準,我們使用的 IoC 容器應該和它相容,所以即使換了容器,它也能正常工作。

@Autowired和@Resource推薦用法

1. 什麼場景用什麼合適

記住一句話就行,@Resource傾向於確定性的單一資源,@Autowired為型別去匹配符合此型別所有資源。

如集合注入,@Resource也是可以的,但是建議使用@Autowired。idea左側的小綠標可以看出來,不建議使用@Resource注入集合資源,本質上集合注入不是單一,也是不確定性的。

來自 “ 阿里開發者 ”, 原文作者:汪軍伍(處軒);原文連結:https://mp.weixin.qq.com/s/2zTVFRb9zWEIJDhtzHrCCQ,如有侵權,請聯絡管理員刪除。

相關文章