最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring如何解決接口多實現(xiàn)類的依賴注入沖突

 更新時間:2025年08月07日 10:47:23   作者:招風(fēng)的黑耳  
在 Spring 中,當(dāng)一個接口有多個實現(xiàn)類時,依賴注入(DI)的歧義性問題是一個常見挑戰(zhàn),本文為大家介紹了如何根據(jù)場景選擇最優(yōu)方案,希望對大家有所幫助

前言

下圖中Spring項目啟動時報錯,啟動時Spring容器在自動裝配menuService字段時,發(fā)現(xiàn)了兩個同類型的Bean(可能是同一個接口的兩個實現(xiàn)類),導(dǎo)致無法確定應(yīng)該注入哪一個。

在 Spring 中,當(dāng)一個接口有多個實現(xiàn)類時,依賴注入(DI)的歧義性問題是一個常見挑戰(zhàn)。以下是更深入的源碼分析和實際項目中的最佳實踐總結(jié),結(jié)合 @Autowired@Resource@Qualifier 的底層機制,以及如何根據(jù)場景選擇最優(yōu)方案。

1. 底層機制分析

(1)@Autowired的工作原理

默認(rèn)按類型(byType)匹配:Spring 通過 DefaultListableBeanFactoryresolveDependency() 方法查找匹配的 Bean。

多個實現(xiàn)時的處理

  • 如果找到唯一匹配的 Bean,直接注入。
  • 如果找到多個同類型 Bean,Spring 會嘗試通過 @Primary@Qualifier 進(jìn)一步篩選。
  • 如果仍未解決歧義,拋出 NoUniqueBeanDefinitionException。

源碼關(guān)鍵點

// org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency
public Object resolveDependency(
    DependencyDescriptor descriptor, String requestingBeanName, 
    Set<String> autowiredBeanNames, TypeConverter typeConverter) {
    
    // 1. 嘗試按類型匹配所有候選 Bean
    Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
    
    // 2. 如果候選 Bean 超過 1 個,檢查是否有 @Primary 或 @Qualifier
    if (matchingBeans.size() > 1) {
        Object primaryCandidate = determinePrimaryCandidate(matchingBeans, descriptor.getDependencyType());
        if (primaryCandidate != null) {
            return primaryCandidate;
        }
        // 如果沒有 @Primary,檢查 @Qualifier
        Object qualifierCandidate = determineQualifierCandidate(matchingBeans, descriptor);
        if (qualifierCandidate != null) {
            return qualifierCandidate;
        }
        // 仍無法解決則拋出異常
        throw new NoUniqueBeanDefinitionException(type, matchingBeans.keySet());
    }
    // ...
}

(2)@Resource的工作原理

  • 默認(rèn)按名稱(byName)匹配:如果未指定 name 屬性,默認(rèn)使用字段名或 setter 方法名作為 Bean 名稱。
  • 名稱匹配失敗時回退到類型:如果按名稱找不到 Bean,會嘗試按類型匹配(但可能仍會因多個實現(xiàn)而失?。?。

源碼關(guān)鍵點

// org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#autowireResource
protected void autowireResource(
    ResourceElement element, Object bean, String requestingBeanName) {
    
    String name = element.name; // @Resource 的 name 屬性
    Object resource;
    
    // 1. 嘗試按名稱注入
    if (resource != null) {
        resource = getResource(element, requestingBeanName);
    } else {
        // 2. 名稱未指定時,回退到按類型注入(可能仍會失?。?
        resource = findResourceByType(element.lookupType);
    }
    
    if (resource == null) {
        throw new NoSuchBeanDefinitionException(...);
    }
    // ...
}

2. 實際項目中的解決方案

(1) 明確指定 Bean 名稱

適用場景:需要精確控制注入哪個實現(xiàn)類。

方案對比

  • @Autowired + @Qualifier:Spring 原生方式,適合 Spring 生態(tài)。
  • @Resource:JSR-250 標(biāo)準(zhǔn),適合需要兼容 JSR 的場景(如遷移到 Jakarta EE)。

示例

// 方式 1:@Autowired + @Qualifier
@Autowired
@Qualifier("nzxxServiceImpl1")
private NzxxService nzxxService;

// 方式 2:@Resource
@Resource(name = "nzxxServiceImpl1")
private NzxxService nzxxService;

(2) 使用@Primary標(biāo)記默認(rèn)實現(xiàn)

  • 適用場景:大多數(shù)情況下使用某個默認(rèn)實現(xiàn),僅少數(shù)場景需切換。
  • 優(yōu)點:減少重復(fù)的 @Qualifier 注解。

示例

@Service
@Primary // 標(biāo)記為默認(rèn)實現(xiàn)
public class NzxxServiceImpl1 implements NzxxService { ... }

@Service("nzxxServiceImpl2")
public class NzxxServiceImpl2 implements NzxxService { ... }

// 無需 @Qualifier,自動注入 NzxxServiceImpl1
@Autowired
private NzxxService nzxxService;

(3) 條件化注入(@Conditional或@Profile)

適用場景:根據(jù)環(huán)境(如開發(fā)/生產(chǎn))或配置動態(tài)選擇實現(xiàn)。

方案

  • @Profile:基于 Spring 的 Profile 機制。
  • @Conditional:自定義條件邏輯。

示例

@Service
@Profile("dev") // 僅在 dev 環(huán)境激活
public class NzxxServiceDevImpl implements NzxxService { ... }

@Service
@Profile("prod") // 僅在 prod 環(huán)境激活
public class NzxxServiceProdImpl implements NzxxService { ... }

(4) 通過工廠方法或ObjectProvider延遲注入

適用場景:需要運行時動態(tài)選擇實現(xiàn)類。

方案

  • ObjectProvider:延遲注入,支持按名稱獲取。
  • 工廠方法:通過 @Bean 方法返回具體實現(xiàn)。

示例

@Autowired
private ObjectProvider<NzxxService> nzxxServiceProvider;

public void someMethod() {
    // 運行時決定使用哪個實現(xiàn)
    NzxxService service = nzxxServiceProvider.getObject("nzxxServiceImpl1");
    service.doSomething();
}

3. 最佳實踐總結(jié)

場景推薦方案代碼示例
明確指定實現(xiàn)類@Qualifier 或 @Resource@Qualifier("beanName")
默認(rèn)實現(xiàn) + 特殊場景@Primary + @Qualifier主實現(xiàn)類標(biāo)記 @Primary
環(huán)境差異化注入@Profile 或 @Conditional@Profile("dev")
運行時動態(tài)選擇ObjectProviderobjectProvider.getObject("beanName")

4. 常見問題與調(diào)試技巧

為什么@Resource有時按類型注入失敗

原因@Resource 默認(rèn)按名稱注入,如果名稱未指定且名稱匹配失敗,回退到類型時可能仍存在多個候選 Bean。

解決:始終顯式指定 name 屬性。

如何調(diào)試 Bean 加載過程

方法

1.在 application.properties 中啟用調(diào)試日志:

logging.level.org.springframework.beans.factory=DEBUG

2.查看 Spring 啟動日志中的 Bean 定義和依賴注入過程。

5. 總結(jié)

@Autowired vs @Resource

  • @Autowired 是 Spring 原生,適合大多數(shù)場景,需配合 @Qualifier 解決歧義。
  • @Resource 是 JSR 標(biāo)準(zhǔn),默認(rèn)按名稱注入,適合需要兼容性的場景。

實際項目建議

  • 優(yōu)先使用 @Primary + @Qualifier 組合。
  • 復(fù)雜場景用 ObjectProvider 或工廠方法。
  • 環(huán)境差異化用 @Profile。

通過結(jié)合源碼分析和實際場景,可以更靈活地解決 Spring 中的多實現(xiàn)類注入問題。

到此這篇關(guān)于Spring如何解決接口多實現(xiàn)類的依賴注入沖突的文章就介紹到這了,更多相關(guān)Spring解決依賴注入沖突內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java調(diào)用Shell命令和腳本的實現(xiàn)

    Java調(diào)用Shell命令和腳本的實現(xiàn)

    這篇文章主要介紹了Java調(diào)用Shell命令和腳本的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-02-02
  • 如何使用idea里面自帶的翻譯插件

    如何使用idea里面自帶的翻譯插件

    這篇文章主要介紹了idea里面自帶的翻譯插件,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-11-11
  • Java詳解表格的創(chuàng)建與使用流程

    Java詳解表格的創(chuàng)建與使用流程

    這篇文章主要介紹了怎么用Java來創(chuàng)建和使用表格,表格是我們經(jīng)常要用的工具,但是你有想過自己怎么去實現(xiàn)它嗎,感興趣的朋友跟隨文章往下看看吧
    2022-04-04
  • Spring Boot集成MinIO進(jìn)行文件存儲和管理的詳細(xì)步驟

    Spring Boot集成MinIO進(jìn)行文件存儲和管理的詳細(xì)步驟

    這篇文章主要介紹了Spring Boot集成MinIO進(jìn)行文件存儲和管理的詳細(xì)步驟,本文通過實例代碼給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧
    2025-04-04
  • IDEA?一直scanning?files?to?index的四種完美解決方法(VIP典藏版)

    IDEA?一直scanning?files?to?index的四種完美解決方法(VIP典藏版)

    這篇文章主要介紹了IDEA?一直scanning?files?to?index的四種完美解決方法(VIP典藏版),推薦第四種方法,第四種方法摸索研究后得出,親測好用,需要的朋友參考下吧
    2023-10-10
  • SpringBoot中使用Redis?Stream實現(xiàn)消息監(jiān)聽示例

    SpringBoot中使用Redis?Stream實現(xiàn)消息監(jiān)聽示例

    本文主要介紹了SpringBoot中使用Redis?Stream實現(xiàn)消息監(jiān)聽示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • Mybatis中單雙引號引發(fā)的慘案及解決

    Mybatis中單雙引號引發(fā)的慘案及解決

    這篇文章主要介紹了Mybatis中單雙引號引發(fā)的慘案及解決方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • RestTemplate().postForEntity的參數(shù)使用及說明

    RestTemplate().postForEntity的參數(shù)使用及說明

    `RestTemplate().postForEntity()`是SpringFramework中用于發(fā)送HTTP POST請求并獲取響應(yīng)的方法,該方法接受目標(biāo)URL、請求體、期望的響應(yīng)類型以及可選的變量參數(shù),并返回一個包含HTTP響應(yīng)信息的`ResponseEntity`對象
    2025-12-12
  • 29個要點幫你完成java代碼優(yōu)化

    29個要點幫你完成java代碼優(yōu)化

    本文給大家分享的是個人總結(jié)的29個java優(yōu)化需要注意的地方,非常的全面細(xì)致,推薦給大家,有需要的小伙伴可以參考下
    2015-03-03
  • Spring BeanName 的自動生成原理示例詳解

    Spring BeanName 的自動生成原理示例詳解

    這篇文章主要介紹了Spring BeanName 的自動生成原理示例詳解,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-09-09

最新評論

逊克县| 水城县| 广饶县| 都昌县| 乾安县| 长兴县| 象州县| 阿瓦提县| 千阳县| 新化县| 惠水县| 浠水县| 彝良县| 麦盖提县| 东辽县| 禹州市| 宣恩县| 保山市| 博乐市| 酒泉市| 福海县| 宕昌县| 乃东县| 青海省| 贵阳市| 凤山县| 临高县| 余江县| 荃湾区| 申扎县| 东莞市| 巴彦淖尔市| 南京市| 台中市| 南部县| 密山市| 舟曲县| 中山市| 三河市| 偃师市| 兴仁县|