一.歧义性

当我们使用spring的注解进行自动装配bean时,如果不仅有一个bean能够匹配结果的话,会抛出NoUniqueBeanDefinitionException:

例如本例中

spring 如何使用cglib spring charged_spring 如何使用cglib

spring 如何使用cglib spring charged_spring_02

spring 如何使用cglib spring charged_自定义_03

spring 如何使用cglib spring charged_spring_04

当spring尝试为DuckBasket类注入duck属性时就会抛出该异常

spring 如何使用cglib spring charged_自定义_05

因为greenDuck类,和redDuck类都实现了Duck接口,换言之,Duck类型有两个实现类,也就是有两个可以匹配的bean造成了歧义性,spring不知道该注入哪个bean给该属性。

二.解决方法

(1)标识首选的bean--通过@primary注解

spring 如何使用cglib spring charged_spring_06

加上@primary注解之后,GreenDuck类就会成为Duck类型的bean中的首选bean,换言之当要注入Duck类型的属性时,会注入GreenDuck类型的实例。

或者当你使用java显示配置bean时,@Primary注解也可以与@bean注解一起使用

spring 如何使用cglib spring charged_spring 如何使用cglib_07

或者当你使用XML配置Bean时,可将bean的Primary属性设置为true

spring 如何使用cglib spring charged_spring_08

(2)给bean配置限定符--@Qualifier注解

@primary 注解缺点在于无法存在多个同类型的首选bean,例如上例中把RedDuck类也用@Primary注解标识,装配时则又会产生NoUniqueBeanDefinitionException:

因为此时RedDuck类与GreenDuck类又处在相同优先级上了。

@Qualifier注解的使用方式如下

spring 如何使用cglib spring charged_自定义_09

spring 如何使用cglib spring charged_spring 如何使用cglib_10

首先用@Qualifier注解给bean添加限定符,然后在要注入该bean的地方再用@Qualifier注解给该属性配置限定符,告诉spring该装配哪个bean。

当你使用Java配置类显示配置bean时,@Qualifier注解也可以和@bean注解一起使用

spring 如何使用cglib spring charged_spring 如何使用cglib_11

 

注:若不在bean上添加@Qualifier注解,即上例中GreenDuck类不添加@Qualifier注解,在DcukBasket类该属性中可将@Qualifier注解改为

@Qualifier("greenDuck"),即Spring的自动装配方案中,bean的默认ID为类名的第一个字母改成小写,后面不变,默认限定符也是如此,但是使用

默认限定符当类名发生变化时,会导致spring装配失败,因为此时默认限定符也发生了改变,所以建议使用图片中自定义限定符的方式进行注解。

(3)使用自定义限定符注解

在(2)中,若RedDuck类也添加了与GreenDuck类相同的注解,则又会导致歧义性,因为此时该限定符下的bean同样不止一个

spring 如何使用cglib spring charged_限定符_12

当遇到这种情况时,我们可能想到在GreenDuck类上在添加限定符来进一步限定唯一的Bean,如下图

spring 如何使用cglib spring charged_限定符_13

但是java不支持相同的注解出现两次,所以此时就需要我们创建自定义限定符注解,创建方式如下

spring 如何使用cglib spring charged_spring_14

注解使用关键词@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了,例如

spring 如何使用cglib spring charged_spring 如何使用cglib_15

 

spring 如何使用cglib spring charged_spring_16

此时便可以在一个限定符的基础上进一步限定唯一bean了,由于自定义的限定符注释不存在同名的情况,所以自定义限定符是可以一层一层限定的

spring 如何使用cglib spring charged_spring_17