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

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

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

前言

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

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

1. 底層機(jī)制分析

(1)@Autowired的工作原理

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

多個(gè)實(shí)現(xiàn)時(shí)的處理

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

源碼關(guān)鍵點(diǎ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 超過(guò) 1 個(gè),檢查是否有 @Primary 或 @Qualifier
    if (matchingBeans.size() > 1) {
        Object primaryCandidate = determinePrimaryCandidate(matchingBeans, descriptor.getDependencyType());
        if (primaryCandidate != null) {
            return primaryCandidate;
        }
        // 如果沒(méi)有 @Primary,檢查 @Qualifier
        Object qualifierCandidate = determineQualifierCandidate(matchingBeans, descriptor);
        if (qualifierCandidate != null) {
            return qualifierCandidate;
        }
        // 仍無(wú)法解決則拋出異常
        throw new NoUniqueBeanDefinitionException(type, matchingBeans.keySet());
    }
    // ...
}

(2)@Resource的工作原理

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

源碼關(guān)鍵點(diǎ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. 名稱未指定時(shí),回退到按類型注入(可能仍會(huì)失?。?
        resource = findResourceByType(element.lookupType);
    }
    
    if (resource == null) {
        throw new NoSuchBeanDefinitionException(...);
    }
    // ...
}

2. 實(shí)際項(xiàng)目中的解決方案

(1) 明確指定 Bean 名稱

適用場(chǎng)景:需要精確控制注入哪個(gè)實(shí)現(xiàn)類。

方案對(duì)比

  • @Autowired + @Qualifier:Spring 原生方式,適合 Spring 生態(tài)。
  • @Resource:JSR-250 標(biāo)準(zhǔn),適合需要兼容 JSR 的場(chǎng)景(如遷移到 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)實(shí)現(xiàn)

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

示例

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

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

// 無(wú)需 @Qualifier,自動(dòng)注入 NzxxServiceImpl1
@Autowired
private NzxxService nzxxService;

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

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

方案

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

示例

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

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

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

適用場(chǎng)景:需要運(yùn)行時(shí)動(dòng)態(tài)選擇實(shí)現(xiàn)類。

方案

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

示例

@Autowired
private ObjectProvider<NzxxService> nzxxServiceProvider;

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

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

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

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

為什么@Resource有時(shí)按類型注入失敗

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

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

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

方法

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

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

2.查看 Spring 啟動(dòng)日志中的 Bean 定義和依賴注入過(guò)程。

5. 總結(jié)

@Autowired vs @Resource

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

實(shí)際項(xiàng)目建議

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

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

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

相關(guān)文章

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    29個(gè)要點(diǎn)幫你完成java代碼優(yōu)化

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

    Spring BeanName 的自動(dòng)生成原理示例詳解

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

最新評(píng)論

新津县| 岳池县| 华池县| 孟连| 牟定县| 教育| 防城港市| 汾阳市| 霍州市| 赞皇县| 儋州市| 万安县| 阿克陶县| 阳春市| 大厂| 和林格尔县| 潼关县| 鲁山县| 太谷县| 天等县| 正宁县| 新宾| 辛集市| 梅州市| 和田县| 孙吴县| 邓州市| 于都县| 宜都市| 信阳市| 龙口市| 平乐县| 大理市| 山阳县| 互助| 花莲市| 乌什县| 合川市| 武陟县| 白城市| 印江|