源码

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Qualifier {String value() default "";
}

@Inherited表示支持被继承;

简析

org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver实现了依赖注入候选解析器接口org.springframework.beans.factory.support.AutowireCandidateResolver,继承自org.springframework.beans.factory.support.GenericTypeAwareAutowireCandidateResolver,Spring默认使用它负责候选处理,专门用于解析
org.springframework.beans.factory.annotation.Qualifier注解;

构造函数

public QualifierAnnotationAutowireCandidateResolver() {// 默认支持@Qualifier和javax.inject.Qualifierthis.qualifierTypes.add(Qualifier.class);try {this.qualifierTypes.add((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Qualifier",QualifierAnnotationAutowireCandidateResolver.class.getClassLoader()));}catch (ClassNotFoundException ex) {// JSR-330 API not available - simply skip.}
}

核心方法

/*** bdHolder 候选Bean的定义信息* descriptor 待自动注入的描述信息*/
public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {// 先经过父类判断,是否允许依赖注入、泛型匹配boolean match = super.isAutowireCandidate(bdHolder, descriptor);if (match) {// 满足父类要求后,再进行判断@Qualifier注解是否匹配// 如果descriptor是Field,则获取Field上的注解// 如果descriptor是MethodParameter,则获取方法参数上的注解match = checkQualifiers(bdHolder, descriptor.getAnnotations());if (match) {// 若是MethodParameter,则需要进一步判断方法参数所在方法上的@Qualifier注解是否匹配MethodParameter methodParam = descriptor.getMethodParameter();if (methodParam != null) {Method method = methodParam.getMethod();if (method == null || void.class == method.getReturnType()) {// 进一步校验构造函数/void方法上@Qualifier注解是否匹配match = checkQualifiers(bdHolder, methodParam.getMethodAnnotations());}}}}return match;
}protected boolean checkQualifiers(BeanDefinitionHolder bdHolder, Annotation[] annotationsToSearch) {if (ObjectUtils.isEmpty(annotationsToSearch)) {// 若待校验注解列表为空,则认为匹配// 方法上可能不存在注解return true;}SimpleTypeConverter typeConverter = new SimpleTypeConverter();// 遍历待校验注解列表for (Annotation annotation : annotationsToSearch) {Class<? extends Annotation> type = annotation.annotationType();// 默认不需要校验元注解boolean checkMeta = true;boolean fallbackToMeta = false;// 判断当前注解是否是@Qualifier注解if (isQualifier(type)) {// 判断当前Bean是否匹配@Qualifier注解if (!checkQualifier(bdHolder, annotation, typeConverter)) {// 未匹配,则进一步尝试校验其元注解fallbackToMeta = true;}else {// 匹配,则无需校验元注解checkMeta = false;}}// 匹配失败,或者不是@Qualifier注解,则需要校验元注解if (checkMeta) {boolean foundMeta = false;// 遍历当前注解的元注解列表for (Annotation metaAnn : type.getAnnotations()) {Class<? extends Annotation> metaType = metaAnn.annotationType();// 校验当前元注解是否是@Qualifier注解if (isQualifier(metaType)) {// 存在@Qualifier元注解foundMeta = true;// 如果fallbackToMeta,说明当前@Qualifier注解匹配失败,且存在@Qualifier元注解// 那么@Qualifier元注解必须有值可以进行后续校验,否则认为不匹配// Only accept fallback match if @Qualifier annotation has a value...// 或者@A标注了@Qualifier,@B标注了@A,当annotation为@B时,fallbackToMeta为false,此时进一步判断当前Bean是否匹配@A注解// 只向上找一层,如果@C标注了@B,当annotation为@C时,则无法fallbackToMeta// Otherwise it is just a marker for a custom qualifier annotation.if ((fallbackToMeta && ObjectUtils.isEmpty(AnnotationUtils.getValue(metaAnn))) ||!checkQualifier(bdHolder, metaAnn, typeConverter)) {// 不满足以上条件,则认为不匹配return false;}}}if (fallbackToMeta && !foundMeta) {// 如果fallbackToMeta且未找到匹配的元注解,则认为不匹配return false;}}}// 成功遍历完待校验注解列表,则认为匹配成功return true;
}protected boolean isQualifier(Class<? extends Annotation> annotationType) {// 遍历已注册的Qualifier注解for (Class<? extends Annotation> qualifierType : this.qualifierTypes) {// 待判断的注解匹配当前注解类型或者标注了当前注解,则认为属于Qualifier注解if (annotationType.equals(qualifierType) || annotationType.isAnnotationPresent(qualifierType)) {return true;}}return false;
}protected boolean checkQualifier(BeanDefinitionHolder bdHolder, Annotation annotation, TypeConverter typeConverter) {Class<? extends Annotation> type = annotation.annotationType();RootBeanDefinition bd = (RootBeanDefinition) bdHolder.getBeanDefinition();// Bean定义信息的qualifiers字段一般都为空AutowireCandidateQualifier qualifier = bd.getQualifier(type.getName());if (qualifier == null) {qualifier = bd.getQualifier(ClassUtils.getShortName(type));}// 优先使用qualifier(xml配置),若qualifier为空则尝试获取targetAnnotation// <bean id="demoBean" class="Demo">//     <qualifier type="org.springframework.beans.factory.annotation.Qualifier" value="demo"/>// </bean>if (qualifier == null) {// 尝试从Bean定义信息获取该类型的注解信息// First, check annotation on qualified element, if anyAnnotation targetAnnotation = getQualifiedElementAnnotation(bd, type);// Then, check annotation on factory method, if applicableif (targetAnnotation == null) {// 若注解信息不存在,则尝试从Bean的工厂方法中获取该类型的注解信息targetAnnotation = getFactoryMethodAnnotation(bd, type);}if (targetAnnotation == null) {RootBeanDefinition dbd = getResolvedDecoratedDefinition(bd);if (dbd != null) {targetAnnotation = getFactoryMethodAnnotation(dbd, type);}}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...}}if (targetAnnotation == null && bd.hasBeanClass()) {targetAnnotation = AnnotationUtils.getAnnotation(ClassUtils.getUserClass(bd.getBeanClass()), type);}}// 注解存在,且匹配待校验注解,则认为匹配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 presentreturn false;}// 遍历待校验注解的属性// @Qualifier注解只有value属性// 但是标注了@Qualifier注解的自定义注解,可以拥有其它属性for (Map.Entry<String, Object> entry : attributes.entrySet()) {// 属性名称String attributeName = entry.getKey();// 期望的属性值Object expectedValue = entry.getValue();Object actualValue = null;// Check qualifier firstif (qualifier != null) {// 从qualifier中获取属性值,若存在的话actualValue = qualifier.getAttribute(attributeName);}if (actualValue == null) {// 从Bean定义信息中获取属性值// Fall back on bean definition attributeactualValue = bd.getAttribute(attributeName);}if (actualValue == null && attributeName.equals(AutowireCandidateQualifier.VALUE_KEY) &&expectedValue instanceof String && bdHolder.matchesName((String) expectedValue)) {// 若属性值不存在,则判断Bean的名称是否匹配属性value对应的值// Fall back on bean name (or alias) matchcontinue;}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());}if (!expectedValue.equals(actualValue)) {return false;}}// 成功遍历玩待校验注解的所有属性,则认为匹配成功return true;
}

应用

过滤多匹配值

@Configuration
public class DemoConfiguration {// 过滤demo类别@Qualifier(value = "demo")@Autowiredprivate Demo demo;// 默认demo类别@Beanpublic Demo demo() {return new Demo("demo");}// 默认demo1类别@Beanpublic Demo demo1() {return new Demo("demo1");}// 指定类别为为demo1@Qualifier(value = "demo1")@Beanpublic Demo demo2() {return new Demo("demo2");}static class Demo {private String name;public Demo(String name) {this.name = name;}@Overridepublic String toString() {return "Demo{" +"name='" + name + '\'' +'}';}}public static void main(String[] args) {ApplicationContext context = new AnnotationConfigApplicationContext(DemoConfiguration.class);DemoConfiguration bean = context.getBean(DemoConfiguration.class);System.out.println(bean.demo);}
}

指定类别过滤

@Configuration
public class DemoConfiguration {// 过滤demo类别@Qualifier(value = "demo")@Autowiredprivate List<Demo> demos;// 满足 (actualValue == null && // attributeName.equals(AutowireCandidateQualifier.VALUE_KEY) && // expectedValue instanceof String && // bdHolder.matchesName((String) expectedValue))@Beanpublic Demo demo() {return new Demo("demo");}// 满足 (targetAnnotation != null && targetAnnotation.equals(annotation))// 指定类别为demo@Qualifier(value = "demo")@Beanpublic Demo demo1() {return new Demo("demo1");}// 默认demo2类别@Beanpublic Demo demo2() {return new Demo("demo2");}static class Demo {private String name;public Demo(String name) {this.name = name;}@Overridepublic String toString() {return "Demo{" +"name='" + name + '\'' +'}';}}public static void main(String[] args) {ApplicationContext context = new AnnotationConfigApplicationContext(DemoConfiguration.class);DemoConfiguration bean = context.getBean(DemoConfiguration.class);System.out.println(bean.demos);}
}

自定义类别注解过滤

@Configuration
public class DemoConfiguration {// 过滤@DemoTag注解@DemoTag@Autowiredprivate List<Demo> demos;@Beanpublic Demo demo() {return new Demo("demo");}// 指定@DemoTag注解@DemoTag@Beanpublic Demo demo1() {return new Demo("demo1");}// 指定@DemoTag注解@DemoTag@Beanpublic Demo demo2() {return new Demo("demo2");}static class Demo {private String name;public Demo(String name) {this.name = name;}@Overridepublic String toString() {return "Demo{" +"name='" + name + '\'' +'}';}}@Target({ElementType.FIELD, ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Qualifier@interface DemoTag {}public static void main(String[] args) {ApplicationContext context = new AnnotationConfigApplicationContext(DemoConfiguration.class);DemoConfiguration bean = context.getBean(DemoConfiguration.class);System.out.println(bean.demos);}
}

Spring Boot源码简析 @Qualifier相关推荐

  1. Spring Boot源码简析 @EnableTransactionManagement

    相关阅读 Spring Boot源码简析 事务管理 Spring Boot源码简析 @EnableAspectJAutoProxy Spring Boot源码简析 @EnableAsync Sprin ...

  2. 【Golang源码分析】Go Web常用程序包gorilla/mux的使用与源码简析

    目录[阅读时间:约10分钟] 一.概述 二.对比: gorilla/mux与net/http DefaultServeMux 三.简单使用 四.源码简析 1.NewRouter函数 2.HandleF ...

  3. django源码简析——后台程序入口

    django源码简析--后台程序入口 这一年一直在用云笔记,平时记录一些tips或者问题很方便,所以也就不再用博客进行记录,还是想把最近学习到的一些东西和大家作以分享,也能够对自己做一个总结.工作中主 ...

  4. (Ajax)axios源码简析(三)——请求与取消请求

    传送门: axios源码简析(一)--axios入口文件 axios源码简析(二)--Axios类与拦截器 axios源码简析(三)--请求与取消请求 请求过程 在Axios.prototype.re ...

  5. java ArrayList 概述 与源码简析

    ArrayList 概述 与源码简析 1 ArrayList 创建 ArrayList<String> list = new ArrayList<>(); //构造一个初始容量 ...

  6. spring boot 源码解析23-actuate使用及EndPoint解析

    前言 spring boot 中有个很诱人的组件–actuator,可以对spring boot应用做监控,只需在pom文件中加入如下配置即可: <dependency><group ...

  7. 【细读Spring Boot源码】重中之重refresh()

    前言 版本:spring-boot-2.7.3 | spring-context-5.3.22 在Spring Boot启动过程中[细读Spring Boot源码]启动步骤 主流程详情7中applic ...

  8. ffmpeg实战教程(十三)iJKPlayer源码简析

    要使用封装优化ijk就必须先了解ffmpeg,然后看ijk对ffmpeg的C层封装! 这是我看ijk源码时候的笔记,比较散乱.不喜勿喷~ ijk源码简析: 1.ijkplayer_jni.c 封装的播 ...

  9. 【Android项目】本地FM收音机开发及源码简析

    [Android项目]本地FM收音机开发及源码简析 目录 1.概述 2.收音机的基本原理 3.收音机其他信息 RDS功能 4.Android开发FM收音机源码解析 5.App层如何设计本地FM应用 6 ...

最新文章

  1. python中的pandas怎么安装_如何优雅的安装Python的pandas?
  2. 支付系统整体设计:整体架构设计以及注意要点(一)
  3. Scrapy安装介绍
  4. redis数据库及与python交互
  5. 在数据库中如何查询表的创建时间?
  6. Kotlin — 适用于数据科学
  7. Android开发之在不同API上遇见的坑
  8. C#的目录与文件操作
  9. 心上莲花:佛教简介(上)
  10. python在线朗读-python朗读软件
  11. 如何快速填充表格公式
  12. 中考英语听说计算机考试成绩查询,中考英语听说考试成绩查询
  13. 对Java的展望_优秀技能经验及对java学习展望
  14. 上项线体表位置_LPL退役选手总结最难打的四个位置:TES辅助RNG上单在列
  15. 手势检测及手掌质心的运动轨迹(opencv)
  16. python的100道简单习题,祝你成为python大神的小老弟
  17. web自动化--python+selenium自动化
  18. android汽车手机互联!阿里P8面试官都说太详细了,赶紧收藏备战金三银四!
  19. SIKI学院:MySQL数据库从零到精通:二十三:课时 27 : 26-数据库的备份和恢复+课时 28 : 27-结语
  20. 极限运算法则——“高等数学”

热门文章

  1. lastpass安卓最新版_密码管家 LastPass
  2. Ce6-Hydrazide 二氢卟吩-酰基 Ce6-PEG-Hydrazide
  3. 世界计算机显示器排名,京东方电脑显示屏等装备市占率稳居全球第一
  4. 时间序列(time serie)分析系列之平均法(移动/指数)2
  5. farpoint支持python_FarPoint Spread for Windows Forms
  6. 深度访谈 001| Lighthouse的故事
  7. 【通古斯爆炸之谜新说】
  8. php安装和开启curl扩展,php开启curl扩展
  9. 要不要把IT主导权还给业务人员
  10. 排序算法——归并排序和桶排序