咱們已經看到如何使用自動裝配讓Spring徹底負責將bean引用注入到構造參數和屬性中。自動裝配可以提供很大的幫助。不過,spring容器中僅有一個bean匹配所需的結果時,自動裝配纔是有效的。若是不只有一個bean可以匹配結果的話,Spring此時別無他法,只好宣告失敗並拋出異常。更精確地講,Spring會拋出NoUniqueBeanDefinitionException。java
當確實發生歧義性時,Spring提供了多種可選方案來解決這樣的問題。你能夠將可選bean中的某一個設爲首選(primary)的bean,或者使用限定符(qualifier)來幫助Spring將可選的bean的範圍縮小到只有一個bean。spring
例子:安全
須要注入的bean:ui
在本例中,Dessert是一個接口,而且有三個類實現了這個接口,分別爲Cake、Cookies和IceCream:3d
這三個實現均使用了@Component註解,在組件掃描的時候,可以發現它們並將其建立爲Spring應用上下文裏面的bean。而後,當Spring試圖自動裝配setDessert()中的Dessert參數時,它並無惟1、無歧義的可選值。xml
Spring提供了多種可選方案來解決這樣的問題。你能夠將可選bean中的某一個設爲首選(primary)的bean,或者使用限定符(qualifier)來幫助Spring將可選的bean的範圍縮小到只有一個bean。blog
一、隱式(@Component)繼承
二、顯式(@Bean)接口
三、xml模式:編譯器
在聲明bean的時候,經過將其中一個可選的bean設置爲首選(primary)bean可以避免自動裝配時的歧義性。當遇到歧義性的時候,Spring將會使用首選的bean,而不是其餘可選的bean。實際上,你所聲明就是「最喜歡」的bean。
若是有兩個繼承相同接口的類同時設置primary,則仍然會有歧義,所以引入Qualifier。Qualifier註解是使用限定符的主要方式,它能夠與@AutoWired和@Inject協同使用,在注入的時候指定想要注入進去的是哪一個bean。
例如,咱們想要確保要將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()方法中的限定符。自動裝配會失敗。
爲bean設置本身的限定符,而不是依賴於將bean ID做爲限定符。在bean聲明上添加@Qualifier註解。
在這種狀況下,cold限定符分配給了IceCreambean。由於它沒有耦合類名,所以你能夠隨意重構IceCream的類名,而沒必要擔憂會破壞自動裝配。在注入的地方,只要引用cold限定符就能夠了:
在顯式模式中:
錯誤示範:多個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類型來指定限定符,自定義的註解也更爲類型安全。