Spring之@Qualifier注解的具体使用
目录
- 1. 前言
- 2. demo 演示
- 2.1 创建接口 Car,以及两个实现其接口的子类
- 2.2 创建实体类 Person
- 2.3 创建配置类以及 Main 类
- 2.4 运行main方法
- 2.5 解决方法
- 3. @Qualifier 注解源码
- 3.1 作用于属性上
- 3.2 作用于方法上
- 3.3 作用于类上
- 3.4 作用于参数上
- 3.5 作用于注解上
- 3.6 小结
- 4. 源码解析
- 4.1 QualifierAnnotationAutowireCandidateResolver#checkQualifier
- 4.2 通过 BeanFactoryPostProcessor 来设置上述源码中的一些值
1. 前言
当我们注入的依赖存在多个候选者,我们得使用一些方法来筛选出唯一候选者,否则就会抛出异常
2. demo 演示
2.1 创www.devze.com建接口 Car,以及两个实现其接口的子类
public interface Car {
}
@Component
public class RedCar implements Car {
}
@Component
public class WhiteCar implements Car {
}
2.2 创建实体类 Person
@Component
public class Person {
@Autowired
private Car car;
}
2.3 创建配置类以及 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);
}
}
2.4 运行main方法

启动过程,抛出异常
2.5 解决方法
- 在 WhiteCar 或者 RedCar 所在的类上加 @Primary 注解
- 将 private Car car 改成 private Car redCar
- 使用 @Qualifier 注解
3. @Qualifier 注解源码

从源码中我们知道这个注解可以作用于属性、方法、参数、类、注解上面
3.1 作用于属性上
我们以 demo 演示代码为前提,使用 @Qualifier 注解
3.1.1 改造 Person 类
@Component
public class Person {
@Autowired
@Qualifier("redCar")
private Car car;
}
3.1.2 运行main方法,查看运行结果

3.2 作用于方法上
3.2.1 创建一个接口 Animal,以及两个实现类
public interface Animal {
}
public class Dog implemenjsts Animal {
}
public class Cat implements Animal {
}
3.2.2 创建配置类
@Configuration
public class AnimalConfig {
@Bean
@Qualifier("mimi")
public Cat cat(){
return new Cat();
}
@Bean
@Qualifier("wangcai")
public Dog dog(){
return new Dog();
}
}
3.2.3 改造 Person 类
@Component
public class Person {
@Autowired
@Qualifier("redCar")
private Car car;
@Autowired
@Qualifier("mimi")
private Animal animal;
}
3.2.4 运行main方法,查看运行结果

3.3 作用于类上
3.3.1 改造 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;
}
3.3.2 运行 main 方法,查看运行结果

3.4 作用于参数上
3.4.1 改造 Person 类
@Component
public class Person {
@Autowired
@Qualifier("car666")
private Car car;
private Animal animal;
public Person(@Qualifier("wangcai") Animal animal) {
this.animal = animal;
}
}
3.4.2 运行 main 方法,查看运行结果

3.5 作用于注解上
3.5.1 创建自定义注解 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() http://www.devze.comdefault "";
}
3.5.2 自定义注解的使用
3.5.2.1 改造 Person 类
@Component
public class Person {
@Autowired
@NestQualifier("redCar")
private Car car;
private Animal animal;
public Person(@Qualifier("wangcai") Animal animal) {
this.animal = animal;
}
}
3.5.2.2 改造 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 {
}
3.5.3 运行main方法,查看运行结果

3.6 小结
- 作用于方法上、作用于类上等于给 bean 添加了一个 alias
- 作用于属性上、作用于参数上时等于注入指定标识符的 bean,这个标识符既可以是 beanName,也可以是 alias
- 作用于注解上比较特殊,如果作用于方法上、作用于类上时用了包装注解,作用于属性上、作用于参数上也必须使用包装注解,否则标识符只能使用 beanName,使用 alias 会报错
4. 源码解析
4.1 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的AutowireCandidateQualifier
AutowireCandidateQualifier qualifier = bd.getQualifier(type.getName());
if (qualifier == null) {
// 判断是否存在名称为NestQualifier的AutowireCandidateQualifier
qualifier = bd.getQualifier(ClassUtils.getShortName(type));
}
if (qualifier == null) {
// 判断bd的qualifiedElement,是否存在@NestQualifier注解
Annotation targetAnnotation = getQualifiedElementAnnotation(bd, type);
// 判断bd的factoryMethod,是否存在@NestQualifier注解
//js 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 class
if (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值一致,则返回true
if (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 present
return false;
}
for (Map.Entry<String, Object> entry : attribute编程s.entrySet()) {
// 获取注解的属性和属性值
String attributeName = entry.getKey();
Object expectedValue = entry.getValue();
Object actualValue = null;
// 通过qualifier获取actualValue
if (qualifier != null) {
actualValue = qualifier.getAttribute(attributeName);
}
// 通过bd获取actualValue
if (actualValue == null) {
// Fall back on bean definition attribute
actualValue = bd.getAttribute(attributeName);
}
// 即属性注入的value值和beanName的值相等则,返回true
if (actualValue == null && attributeName.equals(AutowireCandidateQualifier.VALUE_KEY) &&
expectedValue instanceof String && bdHolder.matchesName((String) expectedValue)) {
// Fall back on bean name (or alias) match
continue;
}
// actualValue等于null,qualifier不等于null,获取value的默认值
if (actualValue == null && qualifier != null) {
// Fall back on default, but only if the qualifier is present
actualValue = AnnotationUtils.getDefaultValue(annotation, attributeName);
}
if (actualValue != null) {
actualValue = typeConverter.convertIfNecessary(actualValue, expectedValue.getClass());
}
// 判断@NestQualifier注解设置的值是否与默认值相等
if (!expectedValue.equals(actualValue)) {
return false;
}
}
return true;
}
4.2 通过 BeanFactoryPostProcessor 来设置上述源码中的一些值
@Component
public class FirstBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
ScannedGenericBeanDefinition scannedGenericBeanDefinition = (ScannedGenericBeanDefinition) registry.getBeanDefinition("redCar");
RootBeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClassName(scannedGenericBeanDefinition.getBeanClassName());
// 设置qualifiedElement
beanDefinition.setQualifiedElement(RedCar.class);
AutowireCandidateQualifier qualifier = new AutowireCandidateQualifier(NestQualifier.class);
// 通过qualifier设置actualValue
qualifier.setAttribute("value", "whiteCar");
beanDefinition.addQualifier(qualifier);
// 通过bd设置actualValue
beanDefinition.setAttribute("value", "redCar");
registry.registerBeanDefinition("redCar", beanDefinition);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
}
}
PS : 被 @ComponentScan 注解扫描的带有 @Component 注解的类会被解析成ScannedGenericBeanDefinition,但是 Spring 在实例化 bean 的时候会把所有 BeanDefinition 封装成 RootBeanDefinition 处理,如果不提前改造 BeanDefinition 的话,RootBeanDefinition 属性都是默认值
到此这篇关于Spring之@Qualifier注解的具体使用的文章就介绍到这了,更多相关Spring @Qualifier注解内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!
加载中,请稍侯......
精彩评论