场景重现
当我们注入的依赖存在多个候选者,我们得使用一些方法来筛选出唯一候选者,否则就会抛出异常
demo演示
创建接口Car,以及两个实现其接口的bean
public interface Car {
}@Component
public class RedCar implements Car {
}@Component
public class WhiteCar implements Car {
}
创建类型为Person的bean
@Component
public class Person {@Autowiredprivate Car car;
}
创建配置类以及Main类
@ComponentScan("com.test.qualifier")
public class AppConfig {}public class Main {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);Person bean = context.getBean(Person.class);System.out.println(bean);}
}
运行main方法
解决方法
- 在WhiteCar或者RedCar所在的类上加@Primary注解
- 将private Car car改成private Car redCar
- 使用@Qualifier注解
查看@Qualifier注解源码
从源码中我们知道这个注解可以作用于属性、方法、参数、类、注解上面
作用于属性上
我们以demo演示代码为前提,使用@Qualifier注解
改造Person类
@Component
public class Person {@Autowired@Qualifier("redCar")private Car car;}
运行main方法,查看运行结果
作用于方法上
创建一个接口Animal,以及两个实现类
public interface Animal {
}public class Dog implements Animal {
}public class Cat implements Animal {
}
创建配置类
@Configuration
public class AnimalConfig {@Bean@Qualifier("mimi")public Cat cat(){return new Cat();}@Bean@Qualifier("wangcai")public Dog dog(){return new Dog();}
}
改造Person类
@Component
public class Person {@Autowired@Qualifier("redCar")private Car car;@Autowired@Qualifier("mimi")private Animal animal;}
运行main方法,查看运行结果
作用于类上
改造Person和RedCar
@Component
@Qualifier("car666")
public class RedCar implements Car {
}@Component
public class Person {@Autowired@Qualifier("car666")private Car car;@Autowired@Qualifier("mimi")private Animal animal;}
运行main方法,查看运行结果
作用于参数上
改造Person类
@Component
public class Person {@Autowired@Qualifier("car666")private Car car;private Animal animal;public Person(@Qualifier("wangcai") Animal animal) {this.animal = animal;}
}
运行main方法,查看运行结果
作用于注解上
创建自定义注解NestQualifier
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface NestQualifier {@AliasFor(annotation = Qualifier.class, attribute = "value")String value() default "";}
case1:改造Person类
@Component
public class Person {@Autowired@NestQualifier("redCar")private Car car;private Animal animal;public Person(@Qualifier("wangcai") Animal animal) {this.animal = animal;}
}
case2:改造Person类、RedCar类
@Component
public class Person {@Autowired@NestQualifier("car666")private Car car;private Animal animal;public Person(@Qualifier("wangcai") Animal animal) {this.animal = animal;}
}@Component
@NestQualifier("car666")
public class RedCar implements Car {
}
运行main方法,查看运行结果
小结
作用于方法上、作用于类上等于给bean添加了一个alias,作用于属性上、作用于参数上时等于注入指定标识符的bean,这个标识符既可以是beanName,也可以是alias。作用于注解上比较特殊,如果作用于方法上、作用于类上时用了包装注解,作用于属性上、作用于参数上也必须使用包装注解,否则标识符只能使用beanName,使用alias会报错
源码解析
QualifierAnnotationAutowireCandidateResolver#checkQualifier
protected boolean checkQualifier(BeanDefinitionHolder bdHolder, Annotation annotation, TypeConverter typeConverter) {Class<? extends Annotation> type = annotation.annotationType();RootBeanDefinition bd = (RootBeanDefinition) bdHolder.getBeanDefinition();// 这里以@NestQualifier注解为例// 判断是否存在名称为com.test.qualifier.annotations.NestQualifier的AutowireCandidateQualifierAutowireCandidateQualifier qualifier = bd.getQualifier(type.getName());if (qualifier == null) {// 判断是否存在名称为NestQualifier的AutowireCandidateQualifierqualifier = bd.getQualifier(ClassUtils.getShortName(type));}if (qualifier == null) {// 判断bd的qualifiedElement,是否存在@NestQualifier注解Annotation targetAnnotation = getQualifiedElementAnnotation(bd, type);// 判断bd的factoryMethod,是否存在@NestQualifier注解// PS:即@Bean标注的方法上是否存在@NestQualifier注解if (targetAnnotation == null) {targetAnnotation = getFactoryMethodAnnotation(bd, type);}// 判断bd是否存在装饰器bd,然后判断装饰器bd的factoryMethod,是否存在@NestQualifier注解if (targetAnnotation == null) {RootBeanDefinition dbd = getResolvedDecoratedDefinition(bd);if (dbd != null) {targetAnnotation = getFactoryMethodAnnotation(dbd, type);}}// 判断bd的实际类型是否存在@NestQualifier注解if (targetAnnotation == null) {// Look for matching annotation on the target classif (getBeanFactory() != null) {try {Class<?> beanType = getBeanFactory().getType(bdHolder.getBeanName());if (beanType != null) {targetAnnotation = AnnotationUtils.getAnnotation(ClassUtils.getUserClass(beanType), type);}} catch (NoSuchBeanDefinitionException ex) {// Not the usual case - simply forget about the type check...}}// 判断bd的实际类型是否存在@NestQualifier注解,这里主要针对没有传入beanFactory的情况if (targetAnnotation == null && bd.hasBeanClass()) {targetAnnotation = AnnotationUtils.getAnnotation(ClassUtils.getUserClass(bd.getBeanClass()), type);}}// 如果目标注解等于方法传入的注解,则返回true// 即属性注入的value值和类上或者方法上的value值一致,则返回trueif (targetAnnotation != null && targetAnnotation.equals(annotation)) {return true;}}Map<String, Object> attributes = AnnotationUtils.getAnnotationAttributes(annotation);if (attributes.isEmpty() && qualifier == null) {// If no attributes, the qualifier must be presentreturn false;}for (Map.Entry<String, Object> entry : attributes.entrySet()) {// 获取注解的属性和属性值String attributeName = entry.getKey();Object expectedValue = entry.getValue();Object actualValue = null;// 通过qualifier获取actualValueif (qualifier != null) {actualValue = qualifier.getAttribute(attributeName);}// 通过bd获取actualValueif (actualValue == null) {// Fall back on bean definition attributeactualValue = bd.getAttribute(attributeName);}// 即属性注入的value值和beanName的值相等则,返回trueif (actualValue == null && attributeName.equals(AutowireCandidateQualifier.VALUE_KEY) &&expectedValue instanceof String && bdHolder.matchesName((String) expectedValue)) {// Fall back on bean name (or alias) matchcontinue;}// actualValue等于null,qualifier不等于null,获取value的默认值if (actualValue == null && qualifier != null) {// Fall back on default, but only if the qualifier is presentactualValue = AnnotationUtils.getDefaultValue(annotation, attributeName);}if (actualValue != null) {actualValue = typeConverter.convertIfNecessary(actualValue, expectedValue.getClass());}// 判断@NestQualifier注解设置的值是否与默认值相等if (!expectedValue.equals(actualValue)) {return false;}}return true;
}
上述源码的一些值需要通过bfpp来设置
@Component
public class FirstBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {ScannedGenericBeanDefinition scannedGenericBeanDefinition = (ScannedGenericBeanDefinition) registry.getBeanDefinition("redCar");RootBeanDefinition beanDefinition = new RootBeanDefinition();beanDefinition.setBeanClassName(scannedGenericBeanDefinition.getBeanClassName());// 设置qualifiedElementbeanDefinition.setQualifiedElement(RedCar.class);AutowireCandidateQualifier qualifier = new AutowireCandidateQualifier(NestQualifier.class);// 通过qualifier设置actualValuequalifier.setAttribute("value", "whiteCar");beanDefinition.addQualifier(qualifier);// 通过bd设置actualValuebeanDefinition.setAttribute("value", "redCar");registry.registerBeanDefinition("redCar", beanDefinition);}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {}
}
PS : 被@ComponentScan注解扫描的带有@Component注解的类会被解析成ScannedGenericBeanDefinition, 但是Spring在实例化bean的时候会把所有bd封装成RootBeanDefinition处理,如果不提前改造bd的话,RootBeanDefinition属性都是默认值