一.歧义性
当我们使用spring的注解进行自动装配bean时,如果不仅有一个bean能够匹配结果的话,会抛出NoUniqueBeanDefinitionException:
例如本例中
当spring尝试为DuckBasket类注入duck属性时就会抛出该异常
因为greenDuck类,和redDuck类都实现了Duck接口,换言之,Duck类型有两个实现类,也就是有两个可以匹配的bean造成了歧义性,spring不知道该注入哪个bean给该属性。
二.解决方法
(1)标识首选的bean--通过@primary注解
加上@primary注解之后,GreenDuck类就会成为Duck类型的bean中的首选bean,换言之当要注入Duck类型的属性时,会注入GreenDuck类型的实例。
或者当你使用java显示配置bean时,@Primary注解也可以与@bean注解一起使用
或者当你使用XML配置Bean时,可将bean的Primary属性设置为true
(2)给bean配置限定符--@Qualifier注解
@primary 注解缺点在于无法存在多个同类型的首选bean,例如上例中把RedDuck类也用@Primary注解标识,装配时则又会产生NoUniqueBeanDefinitionException:
因为此时RedDuck类与GreenDuck类又处在相同优先级上了。
@Qualifier注解的使用方式如下
首先用@Qualifier注解给bean添加限定符,然后在要注入该bean的地方再用@Qualifier注解给该属性配置限定符,告诉spring该装配哪个bean。
当你使用Java配置类显示配置bean时,@Qualifier注解也可以和@bean注解一起使用
注:若不在bean上添加@Qualifier注解,即上例中GreenDuck类不添加@Qualifier注解,在DcukBasket类该属性中可将@Qualifier注解改为
@Qualifier("greenDuck"),即Spring的自动装配方案中,bean的默认ID为类名的第一个字母改成小写,后面不变,默认限定符也是如此,但是使用
默认限定符当类名发生变化时,会导致spring装配失败,因为此时默认限定符也发生了改变,所以建议使用图片中自定义限定符的方式进行注解。
(3)使用自定义限定符注解
在(2)中,若RedDuck类也添加了与GreenDuck类相同的注解,则又会导致歧义性,因为此时该限定符下的bean同样不止一个
当遇到这种情况时,我们可能想到在GreenDuck类上在添加限定符来进一步限定唯一的Bean,如下图
但是java不支持相同的注解出现两次,所以此时就需要我们创建自定义限定符注解,创建方式如下
注解使用关键词@interface定义,
@Qualifier注解修饰该自定义注解是一个限定符注解
其中@Target @Retention的作用如下
@Retention :用来说明该注解类的生命周期。它有以下三个参数:
RetentionPolicy.SOURCE : 注解只保留在源文件中
RetentionPolicy.CLASS : 注解保留在class文件中,在加载到JVM虚拟机时丢弃
RetentionPolicy.RUNTIME : 注解保留在程序运行期间,此时可以通过反射获得定义在某个类上的所有注解。
@Target : 用来说明该注解可以被声明在那些元素之前。
ElementType.TYPE:说明该注解只能被声明在一个类前。
ElementType.FIELD:说明该注解只能被声明在一个类的字段前。
ElementType.METHOD:说明该注解只能被声明在一个类的方法前。
ElementType.PARAMETER:说明该注解只能被声明在一个方法参数前。
ElementType.CONSTRUCTOR:说明该注解只能声明在一个类的构造方法前。
ElementType.LOCAL_VARIABLE:说明该注解只能声明在一个局部变量前。
ElementType.ANNOTATION_TYPE:说明该注解只能声明在一个注解类型前。
ElementType.PACKAGE:说明该注解只能声明在一个包名前。
创建好限定符注解后,就可以是用该注解限定bean了,例如
此时便可以在一个限定符的基础上进一步限定唯一bean了,由于自定义的限定符注释不存在同名的情况,所以自定义限定符是可以一层一层限定的