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 {@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属性都是默认值

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

荒野大嫖客2找不到emp.dll解决办法

首先我参考了几篇文章尝试来解决这个问题&#xff0c; s霍格沃兹emp.dll文件丢失要怎么处理&#xff1f;快速修复emp.dll的方法-CSDN博客 使用电脑自带的修复工具&#xff0c;直接按键盘的winr&#xff0c;然后输入cmd&#xff0c;打开小黑板&#xff0c;我们再在小黑板上输入…

MQTT 简介

MQTT 简介 MQTT 是非常简单的协议&#xff0c;最初由 IBM 的两位工程师 Andy Stanford-Clark 以及 Arlen Nipper 在 1999 年为监控输油管道设计的。它被设计的场景就是有限的带宽、轻量级以及很小的耗电量&#xff0c;在那个时候&#xff0c;卫星宽带就是那么小&#xff0c;且…

【理解机器学习算法】之Clustering算法(Agglomerative Clustering)

聚合聚类(Agglomerative Clustering)是一种层次聚类算法&#xff0c;通过逐步合并或“聚集”它们来构建嵌套聚类。这种方法采用自底向上的方式构建聚类层次&#xff1a;它从将每个数据点作为单个聚类开始&#xff0c;然后迭代合并最接近的聚类对&#xff0c;直到所有数据点合并…

【机器学习】一文搞懂算法模型之:Transformer

Transformer 1、引言2、Transformer2.1 定义2.2 原理2.3 算法公式2.3.1 自注意力机制2.3.1 多头自注意力机制2.3.1 位置编码 2.4 代码示例 3、总结 1、引言 小屌丝&#xff1a;鱼哥&#xff0c; 你说transformer是个啥&#xff1f; 小鱼&#xff1a;嗯… 啊… 嗯…就是… 小屌…

【Java Web基础】一些网页设计基础(四)

文章目录 1. 做Tab切换2. 下面的内容展示——Card样式3. 采供分类&#xff0c;分类用面包屑导航做4. 出名企业展示&#xff0c;就是普通的图片5. 用热门商品类似的panel做一个农博会展览 1. 做Tab切换 使用BootStrap提供的样式&#xff1a; <ul class"nav nav-tabs&q…

【Redis】缓存穿透

问题发生背景&#xff1a;客户端请求的数据再缓存中和数据库中都不存在。 导致的问题&#xff1a;缓存永远不会生效&#xff0c;这些请求都会去请求数据库—导致数据库压力增大。 解决方案&#xff1a; 1.缓存空对象 在Redis中缓存空对象&#xff0c;告诉客户端数据库中没有该值…

uni-app攻略:如何对接驰腾打印机

一.引言 在当前的移动开发生态中&#xff0c;跨平台框架如uni-app因其高效、灵活的特点受到了开发者们的青睐。同时&#xff0c;随着物联网技术的飞速发展&#xff0c;智能打印设备已成为许多业务场景中不可或缺的一环。今天&#xff0c;我们就来探讨如何使用uni-app轻松对接驰…

Codeforces Round 935 (Div. 3)A~E

A. Setting up Camp 题目分析: 有三种人&#xff0c;内向、外向、综合&#xff0c;内向必须独自一个帐篷&#xff0c;外向必须3个人一个帐篷&#xff0c;综合介于1~3人一个帐篷&#xff0c;我们发现非法情况只会存在外向的人凑不成3个人一个帐篷的情况&#xff0c;因外向不够可…

软件管理rpm与yum

源代码包下载 Compare, Download & Develop Open Source & Business Software - SourceForgehttps://sourceforge.net/ rpm包下载 Welcome to the RPM repository on fr2.rpmfind.nethttp://rpmfind.net/linux/RPM/ 软件包管理 1.rpm包管理: 1)查询: 安装…

微隔离有哪些作用

当前&#xff0c;在零信任安全渐渐被大家熟知的情况下&#xff0c;很多用户在网络安全方面有了更深的认知。在网络安全领域&#xff0c;许多企业用户认为零信任对于降低网络安全风险至关重要&#xff0c;有助于在复杂的网络环境中保护自身企业资源免受未经授权的访问和潜在的网…

HDFSRPC安全认证Token篇

本文主要阐述HDFSRPC安全认证相关的实现。主要介绍Token相关的实现。 写在前面 相关blog https://blog.csdn.net/hncscwc/article/details/124722784 https://blog.csdn.net/hncscwc/article/details/124958357 Token由来 在探究完Kerberos&#xff0c;我一直在想一个问题…

python基本概念和基本数据类型

一、基本概念 1.变量 变量是编程语言中最基本的概念&#xff0c;和字面意思一样&#xff0c;指的就是他们的值可变&#xff0c;和我们以前学习的方程类似&#xff0c;变量可以代入任何值。 命名规范&#xff1a;变量一般使用&#xff1a; 英文字母、下划线 和 数字组成 2.关键…

3.21 day2 QT

自由发挥登录窗口的应用场景&#xff0c;实现一个登录窗口界面 要求: 1.需要使用Ui界面文件进行界面设计 2.ui界面上的组件相关设置&#xff0c;通过代码实现 3需要添加适当的动图

模拟堆(详解+例题)

一、定义 维护一个数据集合&#xff0c;堆是一个完全二叉树。 那么什么是二叉树呢&#xff1f; 如图&#xff1a; 二、关于小根堆实现 性质&#xff1a;每个根节点都小于等于左右两边&#xff0c;所以树根为最小值。 2.1、堆存储&#xff08;用一维数组来存&#xff09; 记住…

GifGun2汉化版点击渲染失败,弹窗提示“lossless不是有效的模板名称”

总算解决了&#xff0c;记录一下方法&#xff1a; 1&#xff09;在AE顶部导航&#xff0c;点击“编辑 > 模板 > 输出模块” 2&#xff09;新建一个名为GifGun的输出模块&#xff0c;为后续GifGun引用做准备。&#xff08;取名随意&#xff09; 3&#xff09;在AE顶部导航…

软件测试教程 自动化测试之Junit框架

文章目录 1. 什么是 Junit &#xff1f;2. 常见的注解2.1 Test2.2 BeforeAll&#xff0c;AfterAll2.3 BeforeEach&#xff0c;AfterEach 3. 测试用例顺序指定4. 参数化4.1 单个参数4.2 多个参数4.3 通过方法生成 5. 测试套件6. 断言6.1 断言相等6.2 断言不相等6.3 断言为空6.4 …

山东省大数据局副局长禹金涛一行莅临聚合数据走访调研

3月19日&#xff0c;山东省大数据局党组成员、副局长禹金涛莅临聚合数据展开考察调研。山东省大数据局数据应用管理与安全处处长杨峰&#xff0c;副处长都海明参加调研&#xff0c;苏州市大数据局副局长汤晶陪同。聚合数据董事长左磊等人接待来访。 调研组一行参观了聚合数据展…

软件设计师笔记

计算机 运算器组成&#xff1a;算术逻辑单元(ALU)、累加寄存器(AC)、数据缓冲寄存器(DR)、状态条件寄存器()等组成。 控制器组成&#xff1a;指令寄存器(IR)、程序计数器(PC)、地址寄存器(AR)、指令译码器(ID)。 最小数据单位&#xff1a;bit 最小存储单位: byte n进制 转 1…

蓝桥杯单片机备战——关于573问题的填坑

一、遇到的问题 还记得我前面在封装继电器外设的时候遇到的这个问题嘛&#xff0c;当时我怀疑的是138译码器在切换通道的时候会出现其他暂态导致已经锁定的573解锁。 其实不然&#xff0c;之所以会这样还是因为代码问题&#xff0c;也可以说是573反应时间太快了。下面我就分析…

麒麟系统中使用nginx发布项目

1. 安装Nginx sudo apt-get update #进行所有安装操作前都要执行这一句 sudo apt install nginx #出现询问就Yes参考具体 Nginx—在linux的ubuntu系统上的安装使用 2. 修改发布文件 将打包好的dist文件夹中的所有文件覆盖下面这个文件夹中的所有文件 如果出现没有权限替…