源碼解讀:如何正確使用并區分@Resource和@Autowired注解?

環境:Spring5.3.23

源碼解讀:如何正確使用@Resource和@Autowired注解?

1.注解區別

@Resource 和 @Autowired 都可以用于,依賴注入。但它們之間存在一些明顯的區別。

1.提供方:

  • @Autowired 是 Spring 提供的注解。
  • @Resource 是 JDK提供的注解。

2.裝配方式:

  • @Autowired 默認按類型裝配,即默認情況下必須要求依賴對象存在。如果要允許 null 值,可以設置它的 required 屬性為
    false。如果想使用名稱裝配可以結合 @Qualifier 注解進行使用。
  • @Resource 默認按照名稱進行裝配,名稱可以通過 name 屬性進行指定。如果沒有指定 name
    屬性,當注解寫在字段上時,默認取字段名進行名稱查找。如果注解寫在 setter 方法上默認取屬性名進行裝配。當找不到與名稱匹配的 bean時才按照類型進行裝配。

綜合來看,@Resource 和 @Autowired 在提供方和裝配方式上存在明顯的區別。


2.源碼分析

這里都將以字段注入的方式分析。

@Resource注解

該注解的處理器是CommonAnnotationBeanPostProcessor。

public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {// 獲取所有使用@Resource注解的字段,InjectionMetadata包含了一個List集合InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);metadata.inject(bean, beanName, pvs);return pvs;
}

InjectionMetadata注入核心類

/*** InjectionMetadata類* target     待注入的實例對象 * beanName   當前Bean的名稱* pvs        因為是基于字段注入,所以這里沒有用*/
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {Collection<InjectedElement> elementsToIterate = ...for (InjectedElement element : elementsToIterate) {// 注入;這里當前的element實例是ResourceElement, 這里是調用父類InjectedElement方法element.inject(target, beanName, pvs);}
}
public abstract static class InjectedElement {protected void inject(...) {// 基于字段注入if (this.isField) {Field field = (Field) this.member;ReflectionUtils.makeAccessible(field);// getResourceToInject獲取實例field.set(target, getResourceToInject(target, requestingBeanName));}}
}
private class ResourceElement extends LookupElement {public ResourceElement() {Resource resource = ae.getAnnotation(Resource.class);// 是否指定了name屬性;指定要注入的beanNameString resourceName = resource.name();Class<?> resourceType = resource.type();// 如果設置了name,則為false,否則使用字段名,也就是truethis.isDefaultName = !StringUtils.hasLength(resourceName);if (this.isDefaultName) {// 獲取字段名resourceName = this.member.getName();// ...}// ...this.name = (resourceName != null ? resourceName : "");Lazy lazy = ae.getAnnotation(Lazy.class);// 字段上是否使用了@Lazy注解,我們這里不考慮@Lazy情況this.lazyLookup = (lazy != null && lazy.value());}protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {// 字段沒有添加@Lazy注解,所以為false,執行elsereturn (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) :// 執行這里getResource方法getResource(this, requestingBeanName));}
}

接下來進入到CommonAnnotationBeanPostProcessor中getResource方法

public class CommonAnnotationBeanPostProcessor {protected Object getResource(LookupElement element, @Nullable String requestingBeanName) {// ...return autowireResource(this.resourceFactory, element, requestingBeanName);  }protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName) {String name = element.name;if (factory instanceof AutowireCapableBeanFactory) {AutowireCapableBeanFactory beanFactory = (AutowireCapableBeanFactory) factory;// 默認如果@Resource沒有指定name屬性,所以這里的name為字段名// factory.containsBean(name) 判斷當前容器中是否有以該字段為名的 Bean// 不存在則進入;先按照名稱匹配,如果不存在則進入if,if中則按照類型查找if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) {autowiredBeanNames = new LinkedHashSet<>();// 這里的邏輯就是按照當前字段類型在容器中查找Beanresource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);if (resource == null) {throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");}} else {// 如果容器中存在字段名的Bean,則以beanName在容器中查找Beanresource = beanFactory.resolveBeanByName(name, descriptor);autowiredBeanNames = Collections.singleton(name);}}}
}

以上就是@Resource注解的原理


@Autowired注解

該注解的處理器是AutowiredAnnotationBeanPostProcessor。

public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {// 獲取所有使用@Autowired注解的字段,InjectionMetadata包含了一個List集合InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);// 進入InjectionMetadata#inject方法metadata.inject(bean, beanName, pvs);return pvs;
}
/*** InjectionMetadata* target     待注入的實例對象 * beanName   當前Bean的名稱* pvs        因為是基于字段注入,所以這里沒有用*/
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {Collection<InjectedElement> elementsToIterate = ...for (InjectedElement element : elementsToIterate) {// 注入;這里當前的element實例是AutowiredFieldElement, 這里是調用父類InjectedElement方法element.inject(target, beanName, pvs);}
}

進入AutowiredFieldElement#inject方法

private class AutowiredFieldElement {protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {Field field = (Field) this.member;Object value;// 首次cached=falseif (this.cached) {// ...} else {      // 解析字段獲取bean對象value = resolveFieldValue(field, bean, beanName);}if (value != null) {ReflectionUtils.makeAccessible(field);field.set(bean, value);}}private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {DependencyDescriptor desc = new DependencyDescriptor(field, this.required);desc.setContainingClass(bean.getClass());try {// 進入DefaultListableBeanFactory方法value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);}}
}

DefaultListableBeanFactory

public class DefaultListableBeanFactory {public Object resolveDependency(DependencyDescriptor descriptor, ...) {// ...Object result = ...result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);return result}public Object doResolveDependency(DependencyDescriptor descriptor, ...) {// 該方法中就會按照類型進行查找相應的bean。// 當有多個相同類型的bean時會調用下面的方法進行處理if (matchingBeans.size() > 1) {// 存在多個相同類型時,進行處理autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);}}protected String determineAutowireCandidate() {// 有沒有@Primary注解String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);if (primaryCandidate != null) {return primaryCandidate;}// 有沒有@Priority注解,值越小,優先級越高String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);if (priorityCandidate != null) {return priorityCandidate;}// Fallback 回退處理,如果以上情況都不存在則按照名稱匹配for (Map.Entry<String, Object> entry : candidates.entrySet()) {String candidateName = entry.getKey();Object beanInstance = entry.getValue();if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) ||// 按照名稱匹配matchesBeanName(candidateName, descriptor.getDependencyName())) {return candidateName;}}return null;}
}

到這里你應該清楚了@Resource@Autowired注入的區別了,自身再通過源碼走一遍流程,以后就不用在死記硬背這些東西了。

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

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

相關文章

[Labview] 改寫表格內容并儲存覆蓋Excel

在上一個功能的基礎上&#xff0c;新增表格改寫保存功能 [Labview] Excel讀表 & 輸出表單中選中的單元格內容https://blog.csdn.net/Katrina419/article/details/140120584 Excel修改前&#xff1a; 修改保存后&#xff0c;動態改寫儲存Excel&#xff0c;并重新寫入新的表…

[21] Opencv_CUDA應用之使用Haar級聯的對象檢測

Opencv_CUDA應用之使用Haar級聯的對象檢測 Haar級聯使用矩形特征來檢測對象,它使用不同大小的矩形來計算不同的線和邊緣特征。矩形包含一些黑色和白色區域,如下圖所示,它們在圖像的不同位置居中 類Haar特征檢測算法的思想是計算矩形內白色像素和黑色像素之間的差異這個方法的…

【HDC.2024】云原生中間件,構筑軟件安全可信的連接橋梁

近日&#xff0c;在華為云開發者大會2024期間&#xff0c;來自華為云PaaS服務&#xff0c;中間件領域產品團隊的資深專家、技術總監、高級產品經理等大咖們發表了以“云原生中間件&#xff0c;構筑軟件安全可信的連接橋梁”為主題的專題演講。 演講伊始&#xff0c;華為云產品…

Python編程使用openai的API訪問oneapi暴露的ollama qwen2大模型

首先安裝conda 安裝Python 3.12 &#xff08;低版本的可能缺少openai庫&#xff09; conda create -n py312 python3.12 conda activate py312 然后 pip install openai 提示&#xff1a; Installing collected packages: openai Successfully installed openai-1.35.…

關于 Mybatis 的開啟二級緩存返回對象不一致問題

做實驗報告的時候&#xff0c;跟著學習&#xff0c;發現我已經將 開啟 二級緩存的 配置都配置好了&#xff0c;但是返回值地址不一致&#xff0c;說明對象不一致&#xff0c;二級緩存命中失敗。 跟著流程配置&#xff1a; mybatis-config <settings><!-- 啟用 myba…

你喜歡波段交易嗎?

波段交易的核心在于精準捕捉市場中的長期趨勢波動&#xff0c;以實現更為穩健的收益。與剝頭皮和日內交易不同&#xff0c;波段交易者更傾向于持有交易頭寸數日乃至數周&#xff0c;以更寬廣的視角把握市場動態。 這種交易方式的優勢在于&#xff0c;它降低了對即時市場反應的…

【Gin】項目搭建 一

環境準備 首先確保自己電腦安裝了Golang 開始項目 1、初始化項目 mkdir gin-hello; # 創建文件夾 cd gin-hello; # 需要到剛創建的文件夾里操作 go mod init goserver; # 初始化項目&#xff0c;項目名稱&#xff1a;goserver go get -u github.com/gin-gonic/gin; # 下載…

動態規劃算法,完全零基礎小白教程!不是計算機的都能學會!萬字吐血詳解。

目錄 一、動態規劃算法概念 題一 1、算法解析 1&#xff09;確定狀態&#xff1a; ?2&#xff09;狀態轉移方程&#xff1a; ?3&#xff09;初始化&#xff1a; 4&#xff09;填表順序&#xff1a; 5&#xff09;返回值&#xff1a; 2、代碼 題二 1、算法解析 1、確…

如何理解MySql的MVCC機制

MVCC是什么 MySQL的MVCC機制&#xff0c;全稱為多版本并發控制&#xff08;Multi-VersionConcurrency Control&#xff09;&#xff0c;是一種提高數據庫并發性能的技術。MVCC的主要目的是在保證數據一致性的同時&#xff0c;提高數據庫的并發性能。 它通過為每個讀操作創建數…

【高中數學/三角函數】已知:x,y皆為實數,且4x^2+y^2+xy=1 求:2x+y的最大值

【問題】 已知&#xff1a;x,y皆為實數&#xff0c;且4x^2y^2xy1 求&#xff1a;2xy的最大值 【問題來源】 https://www.ixigua.com/7289764285772497448?logTag0d228277f3a8e049ab6d 【解答】 解&#xff1a; 由4x^2y^2xy1 可得 15/4*x^21/4*x^2xyy^21 得到(15開方/…

智能版面設計:指令跟隨模型在自動布局規劃中的應用

在廣告行業一個吸引人的視覺布局能夠顯著提升信息的傳播效果。但對于非專業設計師來說&#xff0c;創建既美觀又功能性強的布局常常是一項挑戰。他們往往缺乏必要的設計技能、審美訓練或資源來快速實現創意構想。傳統的設計軟件和在線工具雖然提供了一些模板和指導&#xff0c;…

0702_ARM6

練習&#xff1a; 中斷實驗 main.c #include "key.h" int main() {//初始化rcc gpiohal_key_rcc_gpio_init();//初始化extihal_key_exti_init();//初始化gichal_key_gic_init();while(1){}return 0; }key.c #include "key.h"//GPIOF初始化 void hal_key_…

Linux的一些雜項函數總結

getopt_long 解析命令行。 參考&#xff1a; C語言linux getopt_long()函數&#xff08;命令行解析&#xff09;&#xff08;getopt、getopt_long_only&#xff09;&#xff08;短選項 -&#xff0c;長選項 --&#xff09;&#xff08;option結構體&#xff09;&#xff08;opt…

vue3-openlayers marker 光暈擴散(光環擴散)(postrender 事件和 render 方法)

本篇介紹一下使用 vue3-openlayers marker 光暈擴散&#xff08;光環擴散&#xff09;&#xff08;postrender 事件和 render 方法&#xff09; 1 需求 marker 光暈擴散&#xff08;光環擴散&#xff09; 2 分析 marker 光暈擴散&#xff08;光環擴散&#xff09;使用 post…

中級java每日一道面試題-2024年7月2日

題目&#xff1a; 請解釋一下 Java 中的線程安全問題&#xff0c;并提供一些常見的解決方法。 答案&#xff1a; 線程安全問題是指在多線程環境下&#xff0c;多個線程同時訪問共享資源時可能出現的數據不一致或錯誤的情況。這可能導致程序的不可預測性和錯誤的結果。 常見的…

徐州三線服務器租用的優勢有哪些?

對于單線服務器與雙線服務器來說&#xff0c;三線服務器是能夠同時擁有電信、聯通和移動三條線路的服務器&#xff0c;同時也被稱為三線路由器或者是三線寬帶路由器&#xff0c;有著三個獨立的網卡和三個IP地址&#xff0c;使用戶無論是通過哪些線路連接都能夠進入服務器&#…

android.bp 靜態庫 依賴 動態庫

在Android平臺上&#xff0c;使用Android.bp文件來定義和構建Android靜態庫&#xff08;.so文件&#xff09;和動態庫&#xff08;.so文件&#xff09;之間的依賴關系是很常見的。以下是一個簡單的例子&#xff0c;展示了如何在Android.bp文件中定義一個靜態庫&#xff0c;它依…

SPI NAND、SD NAND和eMMC對比—MK米客方德

目錄 1. 容量: 2.封裝類型&#xff1a; 3.速度: 4.性能: 5.壽命: 6. 使用方式: 7. 其他優缺點: 8.常見應用場景: 1. 容量: SPI NAND通常提供從幾百MB到幾GB的存儲容量。 SD NAND的容量覆蓋范圍比SPI NAND更廣&#xff0c;從幾GB到幾十GB不等。 eMMC的容量范圍更大&a…

代碼隨想錄第41天|動態規劃

322. 零錢兌換 dp[j] : 最小硬幣數量, j 為金額(相當于背包空間)遞推公式 : dp[j] min(dp[j - coins[i]] 1, dp[j])初始化: 需要一個最大值, 避免覆蓋, dp[0] 0遍歷順序: 錢幣有序無序不影響, 因為求解最小個數, 結果相同(先遍歷物品后背包, 先背包后物品都可) class Solut…

【chatgpt】兩層gcn提取最后一層節點輸出特征,如何自定義簡單數據集

文章目錄 兩層gcn&#xff0c;提取最后一層節點輸出特征&#xff0c;10個節點&#xff0c;每個節點8個特征&#xff0c;連接關系隨機生成&#xff08;無全連接層&#xff09;如何計算MSE 100個樣本&#xff0c;并且使用批量大小為32進行訓練第一個版本定義數據集出錯&#xff0…