Spring之@Qualifier注解

场景重现

当我们注入的依赖存在多个候选者,我们得使用一些方法来筛选出唯一候选者,否则就会抛出异常

demo演示

创建接口Car,以及两个实现其接口的bean

public interface Car {
}

@Component
public class RedCar implements Car {
}

@Component
public class WhiteCar implements Car {
}

创建类型为Person的bean

@Component
public class Person {

    @Autowired
    private 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的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注解
        // 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 : attributes.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;
}

 上述源码的一些值需要通过bfpp来设置

@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的时候会把所有bd封装成RootBeanDefinition处理,如果不提前改造bd的话,RootBeanDefinition属性都是默认值

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/478955.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【python】python葡萄酒数据集—分类建模与分析(源码+数据集)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

Git原理及使用

1、Git初识 Git是一种版本控制器: 对于同一份文件,做多次改动,Git会记录每一次改动前后的文件。 通俗的讲就是⼀个可以记录⼯程的每⼀次改动和版本迭代的⼀个管理系统,同时也⽅便多⼈协同作业。 注意: Git其实只能跟踪⽂本⽂件的改动,⽐如TXT⽂件,⽹⻚,所有的程序代码…

matlab实现机器学习svm

一、目的和要求 1.编程实现SVM训练函数和预测函数&#xff1b; 2.绘制线性和非线性边界&#xff1b; 3.编写线性核函数 二、算法 1.线性svm&#xff1a; 分离超平面&#xff1a;wxb0&#xff0c;对于线性可分的数据集来说&#xff0c;这样的超平面有无穷多个&#xff08;…

汽车ECU的虚拟化技术(五) -- 对MCU虚拟化实现难点的思考

目录 1.概述 2.虚拟化软件的难点 2.1 虚拟化中的中断处理 2.2 虚拟ECU的通信 3.小结 1.概述 在上面文章里汽车ECU的虚拟化技术(四) -- 对MCU虚拟化实现难点的思考-CSDN博客&#xff0c;解了OEM面临新的电子电气架构下的集成难点&#xff0c;引入了hypervisor以及VM调度机制…

VMD + CEEMDAN 二次分解,CNN-Transformer预测模型

往期精彩内容&#xff1a; 时序预测&#xff1a;LSTM、ARIMA、Holt-Winters、SARIMA模型的分析与比较-CSDN博客 风速预测&#xff08;一&#xff09;数据集介绍和预处理-CSDN博客 风速预测&#xff08;二&#xff09;基于Pytorch的EMD-LSTM模型-CSDN博客 风速预测&#xff…

研华工控机610L学习笔记1:基本了解与认识

今日开始学习一些工控机的基本知识&#xff1a; 目录 目录 1、工控机介绍&#xff1a; 2、研华610L说明书参数了解&#xff1a; 3、基本结构了解&#xff1a; 前面板&#xff1a; 后窗&#xff1a; 4.RS232串口&#xff1a; ​编辑 5、工控机分类&#xff1a; 6、工控上…

深度学习pytorch——激活函数损失函数(持续更新)

论生物神经元与神经网络中的神经元联系——为什么使用激活函数&#xff1f; 我们将生物体中的神经元与神经网络中的神经元共同分析。从下图可以看出神经网络中的神经元与生物体中的神经元有很多相似之处&#xff0c;由于只有刺激达到一定的程度人体才可以感受到刺激&#xff0c…

一、SpringBoot基础搭建

本教程主要给初学SpringBoot的开发者&#xff0c;通过idea搭建单体服务提供手把手教学例程&#xff0c;主要目的在于理解环境的搭建&#xff0c;以及maven模块之间的整合与调用 源码&#xff1a;jun/learn-springboot 以商城项目为搭建例子&#xff0c;首先计划建1个父模块&…

微服务高级篇(三):分布式缓存+Redis集群

文章目录 一、单点Redis的问题及解决方案二、Redis持久化2.1 单机安装Redis2.2 RDB持久化2.3 AOF持久化2.4 RDB和AOF对比 三、Redis主从3.1 搭建Redis主从架构3.1.1 集群结构3.1.2 准备实例和配置3.1.3 启动3.1.4 开启主从关系3.1.5 测试 3.2 数据同步3.2.1 全量同步【建立连接…

【嵌入式——QT】QWT应用

【嵌入式——QT】QWT应用 概述步骤一步骤二步骤三代码声明代码实现图示 概述 QWT&#xff0c;全称Qt Widgets for Technical Applications&#xff0c;是一个基于Qt开发的第三方库。它主要为具有技术背景的程序提供GUI组件和一组实用类&#xff0c;用于生成各种统计图。QWT的主…

【JDBC编程】Java连接MySQL的五个步骤

目录 JDBC编程 1.JDBC的使用 2.数据库连接Connection 3.Statement对象 4.ResultSet对象 JDBC编程 JDBC编程运用了MySQL提供的 Java 的驱动包 mysql-connector-java &#xff0c;需要基于 Java 操作 MySQL 即需要该驱动包。同样的&#xff0c; 要基于 Java 操作 Oracle 数据库…

【文本输入框】显示输入文本的字数,并且限制输入字数不能超过***个字符

需求 实现一个输入框显示文本的字数&#xff0c;并且设置字数限制&#xff0c;当文本中没有内容或字符串长度超出限制&#xff0c;则不能点击确定按钮。 <div class"input-content"><div class"pt-2 rounded-tl-xl rounded-tr-xl bg-blue-100"&…

vue3+threejs新手从零开发卡牌游戏(十):创建己方战域

首先在game目录下创建site文件夹&#xff0c;用来存放战域相关代码&#xff1a; 这里思考下如何创建战域&#xff0c;我的想法是添加一个平面&#xff0c;将己方战域和对方战域都添加进这个平面中&#xff0c;所以首先我们先添加一个战域plane&#xff0c;site/index.vue代码如…

C是用什么语言写出来的?

C是用什么语言写出来的? C语言的起源和发展是一个迭代过程&#xff1a; 1. 最初的C语言编译器的开发始于对B语言的改进。B语言是由Ken Thompson设计的&#xff0c;它是基于BCPL语言简化而来的。在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「 C语言的…

2024.3.20 使用maven打包jar文件和保存到本地仓库

2024.3.20 使用maven打包jar文件和保存到本地仓库 使用maven可以很方便地打包jar文件和导入jar文件&#xff0c;同时还可以将该文件保存在本地仓库重复调用。 使用maven打包jar文件和保存到本地仓库 package打包文件。 install导入本地仓库。 使用maven导入jar文件 点击“…

基于GIS、RS、VORS模型、CCDM模型、geodetecto、GWR模型集成的生态系统健康的耦合协调分析技术

集成多源数据&#xff0c;依托ArcGIS Pro和R语言环境&#xff0c;采用“活力-组织力-恢复力-贡献力”&#xff08;VORS&#xff09;模型定量测算生态系统健康指数&#xff08;EHI&#xff09;&#xff1b;如何从经济城镇化&#xff08;GDPD&#xff09;、人口城镇化&#xff08…

【算法杂货铺】分治

目录 &#x1f308;前言&#x1f308; &#x1f4c1; 快速排序 &#x1f4c2;75. 颜色分类 - 力扣&#xff08;LeetCode&#xff09; &#x1f4c2; 912. 排序数组 - 力扣&#xff08;LeetCode&#xff09; &#x1f4c2; 215. 数组中的第K个最大元素 - 力扣&#xff08;Lee…

以RISC-V架构的CLIC中断机制讲解:中断咬尾、中断抢占、中断晚到

1、中断的相关属性 中断所属特权模式&#xff08;M模式 > S模式 > U模式&#xff09;中断等级&#xff1a;决定是否能够抢占当前的中断中断优先级&#xff1a;影响中断的仲裁&#xff0c;优先级高时优先被响应中断编号&#xff1a;区分中断&#xff0c;影响中断的仲裁 …

操作系统面经-什么是操作系统?

通过以下四点可以概括操作系统到底是什么&#xff1a; 操作系统&#xff08;Operating System&#xff0c;简称 OS&#xff09;是管理计算机硬件与软件资源的程序&#xff0c;是计算机的基石。操作系统本质上是一个运行在计算机上的软件程序 &#xff0c;主要用于管理计算机硬…

视频素材库哪家好?我给大家来分享

视频素材库哪家好&#xff1f;这是很多短视频创作者都会遇到的问题。别着急&#xff0c;今天我就来给大家介绍几个视频素材库哪家好的推荐&#xff0c;让你的视频创作更加轻松有趣&#xff01; 视频素材库哪家好的首选当然是蛙学网啦&#xff01;这里有大量的高质量视频素材&am…