咱们已经看到如何使用自动装配让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类型来指定限定符,自定义的注解也更为类型安全。