1. 程式人生 > >Spring入門(八):自動裝配的歧義性

Spring入門(八):自動裝配的歧義性

1. 什麼是自動裝配的歧義性?

在Spring中,裝配bean有以下3種方式:

  1. 自動裝配
  2. Java配置
  3. xml配置

在這3種方式中,自動裝配為我們帶來了很大的便利,大大的降低了我們需要手動裝配bean的程式碼量。

不過,自動裝配也不是萬能的,因為僅有一個bean匹配條件時,Spring才能實現自動裝配,如果出現不止1個bean匹配條件時,Spring就會不知道要裝配哪個bean,丟擲org.springframework.beans.factory.NoUniqueBeanDefinitionException異常,這就是自動裝配的歧義性。

為了方便理解,我們舉個具體的例子。

首先,我們新建個介面Dessert,該介面僅有1個方法showName():

package chapter03.ambiguity;

public interface Dessert {
    void showName();
}

然後定義3個該介面的實現類Cake,Cookies,IceCream:

package chapter03.ambiguity;

import org.springframework.stereotype.Component;

@Component
public class Cake implements Dessert {
    @Override
    public void showName() {
        System.out.println("蛋糕");
    }
}
package chapter03.ambiguity;

import org.springframework.stereotype.Component;

@Component
public class Cookies implements Dessert {
    @Override
    public void showName() {
        System.out.println("餅乾");
    }
}
package chapter03.ambiguity;

import org.springframework.stereotype.Component;

@Component
public class IceCream implements Dessert {
    @Override
    public void showName() {
        System.out.println("冰激凌");
    }
}

然後新建甜點店類DessertShop,該類的setDessert()方法需要裝配1個Dessert的例項bean:

package chapter03.ambiguity;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class DessertShop {
    private Dessert dessert;

    public Dessert getDessert() {
        return dessert;
    }

    @Autowired
    public void setDessert(Dessert dessert) {
        this.dessert = dessert;
    }

    public void showDessertName() {
        this.dessert.showName();
    }
}

不過現在符合裝配條件的有3個bean,它們的bean ID(預設情況下是類名首字母小寫)分別為cake,cookies,iceCream,Spring該自動裝配哪個呢?

帶著這個疑問,我們先新建配置類AmbiguityConfig:

package chapter03.ambiguity;

import org.springframework.context.annotation.ComponentScan;

@ComponentScan
public class AmbiguityConfig {
}

這個類的關鍵是添加了@ComponentScan註解,讓Spring自動掃描已經定義好的bean。

最後,新建類Main,在其main()方法中新增如下測試程式碼:

package chapter03.ambiguity;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AmbiguityConfig.class);

        DessertShop dessertShop = context.getBean(DessertShop.class);
        dessertShop.showDessertName();

        context.close();
    }
}

執行程式碼,發現丟擲org.springframework.beans.factory.NoUniqueBeanDefinitionException異常,如下所示:

那麼如何解決自動裝配的歧義性呢?Spring提供了以下2種方案:

  1. 標記首選的bean
  2. 使用限定符

2. 標記首選的bean

既然現在有3個匹配條件的bean,我們可以通過@Primary註解標記下哪個是首選的bean,這樣當Spring發現有不止1個匹配條件的bean時,就會選擇這個首選的bean。

比如3種甜點裡,我最喜歡吃餅乾,那麼我就把Cookies標記為首選的bean:

package chapter03.ambiguity;

import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

@Component
@Primary
public class Cookies implements Dessert {
    @Override
    public void showName() {
        System.out.println("餅乾");
    }
}

再次執行測試程式碼,輸出結果如下所示:

餅乾

圓滿解決了歧義性的問題,不過有一天,有個同事不小心在IceCream上也添加了@Primary註解:

package chapter03.ambiguity;

import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

@Component
@Primary
public class IceCream implements Dessert {
    @Override
    public void showName() {
        System.out.println("冰激凌");
    }
}

編譯都正常,因此都沒注意,但釋出後執行時,卻丟擲如下異常:

意思就是發現了不止1個首選的bean,因為此時Spring又不知道該選擇哪個了,也就是有了新的歧義性,所以甩鍋丟擲了異常。

3. 使用限定符

3.1 基於bean ID的限定符

Spring還提供了另一個註解@Qualifier註解來解決自動裝配的歧義性,它可以與@Autowired或者@Inject一起使用,在注入的時候指定想要注入哪個bean。

比如,我們把IceCream注入到setDessert()的方法引數之中:

@Autowired
@Qualifier("iceCream")
public void setDessert(Dessert dessert) {
    this.dessert = dessert;
}

這裡傳遞的iceCream指的是IceCream類預設生成的bean ID。

再次執行測試程式碼,輸出結果如下所示:

冰激凌

我們可以發現,使用了@Qualifier註解後,我們之前標記的@Primary註解被忽略了,也就是說,@Qualifier註解的優先順序比@Primary註解的優先順序高。

使用預設的限定符雖然解決了問題,不過可能會引入一些問題。比如我在重構程式碼時,將IceCream類名修改成了Gelato:

package chapter03.ambiguity;

import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

@Component
@Primary
public class Gelato implements Dessert {
    @Override
    public void showName() {
        System.out.println("冰激凌");
    }
}

此時執行程式碼,會發現丟擲org.springframework.beans.factory.NoSuchBeanDefinitionException異常,如下所示:

這是因為IceCream重新命名為Gelato之後,bean ID由iceCream變成了gelato,但我們注入地方的程式碼仍然使用的是iceCream這個bean ID,導致沒有找到匹配條件的bean。

鑑於使用預設的限定符的這種侷限性,我們可以使用自定義的限定符來解決這個問題。

為不影響後面程式碼的測試結果,將Gelato類再改回IceCream

3.2 基於面向特性的限定符

為了避免因為修改類名而導致自動裝配失效的問題,我們可以在@Component或者@Bean註解宣告bean時新增上@Qualifier註解,如下所示:

package chapter03.ambiguity;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

@Component
@Qualifier("cold")
public class IceCream implements Dessert {
    @Override
    public void showName() {
        System.out.println("冰激凌");
    }
}

然後在注入的地方,不再使用預設生成的bean ID,而是使用剛剛指定的cold限定符:

@Autowired
@Qualifier("cold")
public void setDessert(Dessert dessert) {
    this.dessert = dessert;
}

執行測試程式碼,輸入結果如下所示:

冰激凌

此時將IceCream類重新命名為Gelato,程式碼可以正常執行,不會受影響。

然後有一天,某位開發又新建了類Popsicle,該類也使用了cold限定符:

package chapter03.ambiguity;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

@Component
@Qualifier("cold")
public class Popsicle implements Dessert {
    @Override
    public void showName() {
        System.out.println("棒冰");
    }
}

此時又帶來了新的歧義性問題,因為Spring又不知道該如何選擇了,執行程式碼會丟擲org.springframework.beans.factory.NoUniqueBeanDefinitionException異常,如下所示:

此時,我們就需要用到自定義的限定符了。

3.3 自定義的限定符註解

首先,我們新建以下3個註解:

package chapter03.ambiguity;

import org.springframework.beans.factory.annotation.Qualifier;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Cold {
}
package chapter03.ambiguity;

import org.springframework.beans.factory.annotation.Qualifier;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Creamy {
}
package chapter03.ambiguity;

import org.springframework.beans.factory.annotation.Qualifier;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Fruity {
}

注意事項:這3個註解在定義時都添加了@Qualifier註解,因此它們具有了@Qualifier註解的特性

然後將IceCream類修改為:

package chapter03.ambiguity;

import org.springframework.stereotype.Component;

@Component
@Cold
@Creamy
public class IceCream implements Dessert {
    @Override
    public void showName() {
        System.out.println("冰激凌");
    }
}

將Popsicle類修改為:

package chapter03.ambiguity;

import org.springframework.stereotype.Component;

@Component
@Cold
@Fruity
public class Popsicle implements Dessert {
    @Override
    public void showName() {
        System.out.println("棒冰");
    }
}

最後,修改下注入地方的程式碼,使其只能匹配到1個滿足條件的bean,如下所示:

@Autowired
@Cold
@Creamy
public void setDessert(Dessert dessert) {
    this.dessert = dessert;
}

執行測試程式碼,輸出結果如下所示:

冰激凌

由此,我們也可以發現,自定義註解與@Qualifier註解相比,有以下2個優點:

  1. 可以同時使用多個自定義註解,但@Qualifier註解只能使用1個
  2. 使用自定義註解比@Qualifier註解更為型別安全

4. 原始碼及參考

原始碼地址:https://github.com/zwwhnly/spring-action.git,歡迎下載。

Craig Walls 《Spring實戰(第4版)》

5. 最後

打個小廣告,歡迎掃碼關注微信公眾號:「申城異鄉人」,定期分享Java技術乾貨,讓我們一起進步。

相關推薦

Spring入門()自動裝配歧義

1. 什麼是自動裝配的歧義性? 在Spring中,裝配bean有以下3種方式: 自動裝配 Java配置 xml配置 在這3種方式中,自動裝配為我們帶來了很大的便利,大大的降低了我們需要手動裝配bean的程式碼量。 不過,自動裝配也不是萬能的,因為僅有一個bean匹配條件時,Spring才能實現自動裝配,如

Spring入門學習(自動裝配) 第四節

Spring入門學習 第四節 自動裝配 自動裝配 分別建立三個類Address.java,Car.java,Person.java 其中Perosn類包含有Address和Car的屬性package com.fafa.spring.au

Spring入門(二)自動化裝配bean

pep 自動化 tdi req ngs mcr 完成 injection 實現類 Spring從兩個角度來實現自動化裝配: 組件掃描(component scanning):Spring會自動發現應用上下文中需要創建的bean。 自動裝配(autowiring):Spri

3.spring自動裝配/Bean之間的關係/作用域/外部檔案/spel/

1.自動裝配/手動裝配 xml配置檔案裡的bean自動裝配 Spring IOC 容器裡可以自動的裝配Bean,需要做的僅僅是在<bean>的autowire屬性裡面指定自動裝配模式 ->byType(根據型別自動進行裝配):若IOC容器裡需要有多個與目標Bean型別一樣的Bean,在這種

3.spring自動裝配/Bean之間的關系/作用域/外部文件/spel/

depends ... 完全 構造器 外部文件 conn wire odi host 1.自動裝配/手動裝配 xml配置文件裏的bean自動裝配 Spring IOC 容器裏可以自動的裝配Bean,需要做的僅僅是在<bean>的autowire屬性裏面指定自動裝

Spring註解(三)自動裝配

13、自動裝配:【13-1】@Autowired:自動注入(推薦使用);可以在構造器(如果元件只有一個有參構造器,這個有參構造器的@Autowired可以省略,引數位置的元件還是可以自動從容器中獲取)、引數、方法(一般標註在SET方法上)、屬性中使用①預設優先按照型別去容器找

Spring中類型自動裝配--byType

log ext setw println create cat ssp XML ret 在Spring中,“類型自動裝配”的意思是如果一個bean的數據類型與其它bean屬性的數據類型相同,將自動兼容裝配它。 例如,一個“persion” bean 公開以“abili

Spring中通過構造自動裝配--constructor

getname ted val lan 如果 create 構造方法 detect pub 在Spring中,可以使用“通過構造自動裝配”,實際上是按構造函數的參數類型自動裝配。 這意味著,如果一個bean的數據類型與其他bean的構造器參數的數據類型是相同的,那麽將自動裝

Spring@Autowired註解與自動裝配

arp details auto 反射機制 java -i etc str spi 1 配置文件的方法 我們編寫spring 框架的代碼時候。一直遵循是這樣一個規則:所有在spring中註入的bean 都建議定義成私有的域變量。並且要配套寫上 get 和 set方法

Spring Cloud 服務容錯保護(Hystrix斷路器)【Dalston版】

sta 什麽 star 根據 .com 一次 href 進行 響應 斷路器 斷路器模式源於Martin Fowler的Circuit Breaker一文。“斷路器”本身是一種開關裝置,用於在電路上保護線路過載,當線路中有電器發生短路時,“斷路器”能夠及時的切斷故障電路,防止

spring cloud()Turbine的應用

spring cloud Turbine 1、概念Turbine 是聚合服務器發送事件流數據的一個工具,用來監控集群下 hystrix 的 metrics 情況。2、引入依賴 <dependency> <groupId>org.spring

spring入門框架整體簡介

mil object web開發 spa tor 對象 j2ee 就是 cor 1:spring的基本框架主要包含六大模塊:DAO、ORM、AOP、JEE、WEB、CORE   DAO:(Data Access Object) 數據訪問對象,是一個面向對象的數據庫接口。  

Spring入門(一)Spring注入

概念 Spring注入是指在啟動Spring容器載入bean配置的時候,完成對變數的賦值行為 常用的兩種注入方式 設值注入 構造注入 設值注入 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns

Spring註解開發之自動裝配

自動裝配:Spring利用依賴注入(DI),完成對IOC容器中中各個元件的依賴關係賦值; 自動裝配[email protected]&@Qualifier&@Primary 使用@Autowire註解可以為SpringBean物件自動注

Spring中Bean的自動裝配之@Resource、@Inject

Spring還支援使用@Resource(JSR250)和@Inject(JSR330)[java規範的註解]  *         @Resource:  *             可以和@Autowired一樣實現自動裝配功能;預設是按照元件名稱進行裝配的;  *   

Spring註解驅動開發——自動裝配(1)

自動裝配:Spring利用依賴注入(DI),完成對IOC容器中各個元件的依賴賦值 1、@Autowired:     預設先按照型別去容器中找相應的元件(applicationContext.get

python爬蟲入門八:多程序/多執行緒 python佇列Queue Python多執行緒(2)——執行緒同步機制 python學習筆記——多程序中共享記憶體Value & Array python 之 多程序 Python多程序 Python 使用multiprocessing 特別耗記

什麼是多執行緒/多程序 引用蟲師的解釋: 計算機程式只不過是磁碟中可執行的,二進位制(或其它型別)的資料。它們只有在被讀取到記憶體中,被作業系統呼叫的時候才開始它們的生命期。 程序(有時被稱為重量級程序)是程式的一次執行。每個程序都有自己的地址空間,記憶體,資料棧以及其它記錄其執行軌跡的輔助資料

Spring原始碼解讀之——自動裝配(隨筆)

Spring利用依賴注入(DI),完成對IOC容器中中各個元件的依賴關係賦值; 1、@Autowired:自動注入:     1)、預設優先按照型別去容器中找對應的元件:applicationContext.getBean(BookDao.class);找到就賦值   

spring 注入方式和自動裝配介紹

spring四種依賴注入方式 部落格分類: spring 平常的java開發中,程式設計師在某個類中需要依賴其它類的方法,則通常是new一個依賴類再呼叫類例項的方法,這種開發存在的問題是new的類例項不好統一管理,spring提出了依賴注入的思想,即依賴類不

Spring由構造方法自動裝配

在Spring中,可以使用“通過構造自動裝配”,實際上是按建構函式的引數型別自動裝配。 這意味著,如果一個bean的資料型別與其他bean的構造器引數的資料型別是相同的,那麼將自動裝配。下面看看Spri