@Autowired
public void setDessert(Dessert dessert){
this.dessert = dessert;
}
我們使用@Autowired註解標註了setDessert()方法,而他依賴Dessert接口,但是Dessert接口有三個實現類,並且都使用了@Component註解,那麼組件掃描的時候,會爲這三個實現類實例化bean在Spring上下文中存儲,那麼setDessert()方法在選擇時,由於有三個實現類,不知道使用哪個,Spring則會報錯,拋出NoUniqueBeanDefinitionException異常。
解決:我們可以設置某一個爲首選bean(使用@Primary)
@Component
@Primary
public class IceCream extends Dessert(){ ......... }
或則通過顯示配置的聲明IceCream,那麼@Bean方法如下所示:
@Bean
@Primiary
public Dessert iceCream(){
return new IceCream();
}
如果你使用的是xml配置的話,那麼配置如下:
<bean id = "iceCream" class = "com.desserteater.IceCream" primary="true" />
無論哪種方式,效果一樣,都是告訴Spring在遇到歧義性的時候首先該bean.
但是,如果兩個bean或則更多的首選bean使用@Primiart,Spring就無法正常工作,因爲仍然會有歧義性。
@Qualifier
@Qualifier註解是使用限定符的主要方式,他可以與@Autowired和@Inject協同使用,在注入的時候指定想要注入的bean
@Autowired
@Qualifier("iceCream")
public void setDessert(Dessert dessert){
this.dessert = dessert;
}
這是最簡單的限定符使用例子,爲@Qualifier註解所設置的參數就是想要注入的bean的ID,所有使用@Component註解聲明的類都會創建爲bean,並且bean的ID爲首字母變小寫的類名。因此,@Qualifier("iceCream")指向的是組件掃描時所創建的bean,並且這個bean是IceCream類的實例。
更準確的講,@Qualifier("iceCream")所引用的bean要具有String類型的“iceCream”作爲限定符。如果沒有指定其他限定符的話,所有的bean都會給定一個默認的限定符,這個限定符與bean的ID相同,即爲首字母變小寫的類名。因此框架會將有“iceCream”限定符的bean注入到setDessert()方法中。
問題:基於默認的beanID它是爲首字母變小寫的類名,如果重構了該類,重命名爲其他類名的話,比如將IceCream重命名爲Gelato,那麼上面的setDesser()方法將無法通過限定符匹配,自動裝配會失敗。
解決:創建自定義限定符⬇️⬇️⬇️
創建自定義的限定符
在bean聲明上添加@Qualifier註解,他可以和@Component組合使用
@Component
@Qualifier("cold")
public class IceCream implements Dessert { ...... }
在這種情況下,cold限定符分配給了IceCream bean,因爲它沒有耦合的類名,所以可以任意重構IceCream的類名,而不必擔心上面的破壞自動裝配問題。在注入的地方引入cold限定符就可以了。
當通過java顯示配置bean的時候,@Qualifier也可以和@Bean一起使用
@Bean
@Qualifier("cold")
public Dessert iceCream(){
return new IceCream();
}
問題:如果相同類型的類都使用一樣的@Qualifier註解,那怎麼辦
Java不允許同一個條目上重複出現相同類型的多個註解。否則會報錯,
(注:java 8 允許出現重複的註解,只要這個註解本身定義的時候帶有@Repeatable註解就可以。不過Spring的@Qualifier註解並沒有在定義時添加@Repeatable註解。)
//java8中允許使用重複註解的demo,註解需要添加@Repeatable
@Repeatable(Authorities.class)
public @interface Authority {
String role();
}
public @interface Authorities {
Authority[] value();
}
public class RepeatAnnotationUseNewVersion {
@Authority(role="Admin")
@Authority(role="Manager")
public void doSomeThing(){ }
}
如:
@Component
@Qualifier("cold")
@Qualifier("creamy")
public class IceCream implements Dessert { ...... }
上面不允許這樣操作的,因爲@Qualifier沒有@Repeatable註解,那麼怎麼解決呢?
我們可以創建自定義的限定符註解,藉助這樣的註解來使bean唯一。我們將不再使用@Qualifier("code"),而是使用自定義的@Code註解,如下所示:
@Target( {ElementType.CONSTRUCTOR, ElementType.FIELD ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Cold { }
當不想使用@Qualifier時,我們通過自定義限定符註解時,添加@Qualifier註解,自定義註解就覺有@Qualifier註解的特性。他們本身實際上也就成爲了限定符註解。
那麼如果出現上面那個問題,我們可以按層次定義多個註解即可:如
@Component
@Cold
@Creamy
public class IceCream implements Dessert { ...... }
@Component
@Cold
@Fruity
public class IceCream implements Dessert { ...... }