1. 程式人生 > >spring學習總結——高階裝配學習一(處理自動裝配的歧義性)

spring學習總結——高階裝配學習一(處理自動裝配的歧義性)

  我們已經看到如何使用自動裝配讓Spring完全負責將bean引用注入到構造引數和屬性中。自動裝配能夠提供很大的幫助。不過,spring容器中僅有一個bean匹配所需的結果時,自動裝配才是有效的。如果不僅有一個bean能夠匹配結果的話,Spring此時別無他法,只好宣告失敗並丟擲異常。更精確地講,Spring會丟擲NoUniqueBeanDefinitionException。

  當確實發生歧義性時,Spring提供了多種可選方案來解決這樣的問題。你可以將可選bean中的某一個設為首選(primary)的bean,或者使用限定符(qualifier)來幫助Spring將可選的bean的範圍縮小到只有一個bean。

 

例子:

需要注入的bean:

  

在本例中,Dessert是一個介面,並且有三個類實現了這個介面,分別為Cake、Cookies和IceCream:

   

這三個實現均使用了@Component註解,在元件掃描的時候,能夠發現它們並將其建立為Spring應用上下文裡面的bean。然後,當Spring試圖自動裝配setDessert()中的Dessert引數時,它並沒有唯一、無歧義的可選值。

Spring提供了多種可選方案來解決這樣的問題。你可以將可選bean中的某一個設為首選(primary)的bean,或者使用限定符(qualifier)來幫助Spring將可選的bean的範圍縮小到只有一個bean。

一、標示首選的bean(@Primary)

1、隱式(@Component)

  

2、顯式(@Bean)

  

3、xml模式:

  

 

  在宣告bean的時候,通過將其中一個可選的bean設定為首選(primary)bean能夠避免自動裝配時的歧義性。當遇到歧義性的時候,Spring將會使用首選的bean,而不是其他可選的bean。實際上,你所宣告就是“最喜歡”的bean。

二、@Qualifier限定符

  如果有兩個繼承相同介面的類同時設定primary,則仍然會有歧義,因此引入Qualifier。Qualifier註解是使用限定符的主要方式,它可以與@AutoWired和@Inject協同使用,在注入的時候指定想要注入進去的是哪個bean。

1、基於預設的bean ID作為限定符

 

例如,我們想要確保要將IceCream注入到setDessert()之中:

   

  為@Qualifier註解所設定的引數就是想要注入的bean的ID。所有使用@Component註解宣告的類都會建立為bean,並且bean的ID為首字母變為小寫的類名,並且如果沒有指定其他的限定符的話,所有的bean都會給定一個預設的限定符,這個限定符與bean的ID相同。因此,@Qualifier("iceCream")指向的是元件掃描時所建立的bean,並且這個bean是IceCream類的例項。

   基於預設的bean ID作為限定符是非常簡單的,但這有可能會引入一些問題。如果你重構了IceCream類,將其重新命名為Gelato的話,那此時會發生什麼情況呢?如果這樣的話,bean的ID和預設的限定符會變為gelato,這就無法匹配setDessert()方法中的限定符。自動裝配會失敗。

2、建立自定義的限定符

為bean設定自己的限定符,而不是依賴於將bean ID作為限定符。在bean宣告上新增@Qualifier註解。

   

 

在這種情況下,cold限定符分配給了IceCreambean。因為它沒有耦合類名,因此你可以隨意重構IceCream的類名,而不必擔心會破壞自動裝配。在注入的地方,只要引用cold限定符就可以了:

  

 

在顯式模式中:

  

 

3、使用自定義的限定符註解

錯誤示範:多個bean都具備相同特性的話,這種做法也會出現問題。可能想到的解決方案就是在注入點和bean定義的地方同時再新增另外一個@Qualifier註解

 

  

——————————————————————————

  

 

可是,如果有另外一個bean也同樣使用了cold限定符呢,還是會出現歧義,而java不允許同一個條目上重複出現相同型別的多個註解,否則編譯器會報錯,所以我們需要建立自定義的限定符註解,藉助這樣的註解來表達bean所希望限定的特性。

 

當你不想用@Qualifier註解的時候,可以類似地建立@Soft、@Crispy和@Fruity。通過在定義時新增@Qualifier註解,它們就具有了@Qualifier註解的特性。它們本身實際上就成為了限定符註解。

@Qualifier("cold")被代替:

  

@Qualifier("creamy")被代替:

  

使用例子:

 註解宣告bean:

IceCream類可以新增@Cold和@Creamy註解:

  

Popsicle類可以新增@Cold和@Fruity註解:

  

 注入bean:

   

通過宣告自定義的限定符註解,我們可以同時使用多個限定符,不會再有Java編譯器的限制或錯誤。與此同時,相對於使用原始的@Qualifier並藉助String型別來指定限定符,自定義的註解也更為型別安全。