@Qualifier依赖注入原理

spring中通过@Autowired注入的依赖对应多个实现时,需要手动区分,告诉spring我们想注入的依赖是哪个,常见的作法有:

  1. 配合@Qualifier指定依赖名,根据依赖名找到bean进行注入
  2. 在依赖上标记@Primary注解,spring会优先注入
  3. 使用@Priority注解指定依赖的优先级,越小优先级越高 也就是说可以通过@Qualifier+@Autowired自动注入依赖,能够避免依赖冲突,现在看spring如何处理的。根据依赖类型找到匹配的依赖名,需要判断能否将依赖注入到属性。
java 复制代码
	protected boolean isAutowireCandidate(
			String beanName, DependencyDescriptor descriptor, AutowireCandidateResolver resolver)
			throws NoSuchBeanDefinitionException {
        //根据bean定义判断能否注入
		String bdName = BeanFactoryUtils.transformedBeanName(beanName);
		if (containsBeanDefinition(bdName)) {
			return isAutowireCandidate(beanName, getMergedLocalBeanDefinition(bdName), descriptor, resolver);
		}
        //根据bena实例判断能否注入
		else if (containsSingleton(beanName)) {
			return isAutowireCandidate(beanName, new RootBeanDefinition(getType(beanName)), descriptor, resolver);
		}
......
	}

检查能否注入时,先调用父类isAutowireCandidate判断checkGenericTypeMatch(bdHolder, descriptor)类型是否相同,即依赖类型和字段类型是否相同,再检查字段上标注的@Qualifier注解。

java 复制代码
	public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
		boolean match = super.isAutowireCandidate(bdHolder, descriptor);
		if (match) {
			match = checkQualifiers(bdHolder, descriptor.getAnnotations());
			if (match) {
				MethodParameter methodParam = descriptor.getMethodParameter();
				if (methodParam != null) {
					Method method = methodParam.getMethod();
					if (method == null || void.class == method.getReturnType()) {
						match = checkQualifiers(bdHolder, methodParam.getMethodAnnotations());
					}
				}
			}
		}
		return match;
	}

先从bean定义中获取@Qualifier,能获取到,则说明当前依赖bean为指定的优先注入的依赖;无法获取到,则将@Qualifier注解定义的依赖名和实际依赖bean的名称进行比较,相同的话说明根据@Qualifier注解找到指定的依赖了,进行注入即可。

java 复制代码
	protected boolean checkQualifier(
			BeanDefinitionHolder bdHolder, Annotation annotation, TypeConverter typeConverter) {
        //获取字段的@Qualifier注解
		Class<? extends Annotation> type = annotation.annotationType();
		RootBeanDefinition bd = (RootBeanDefinition) bdHolder.getBeanDefinition();

		AutowireCandidateQualifier qualifier = bd.getQualifier(type.getName());
.....       //从bean定义上获取@Qualifier注解,如果能获取到,则优先注入
			if (targetAnnotation != null && targetAnnotation.equals(annotation)) {
				return true;
			}
        //依赖上没有@Qualifier注解,则通过依赖bean名是否和@Qualifier注解属性相同判断能否注入
		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;
			// Check qualifier first
			if (qualifier != null) {
				actualValue = qualifier.getAttribute(attributeName);
			}
			if (actualValue == null) {
				// Fall back on bean definition attribute
				actualValue = bd.getAttribute(attributeName);
			}
            //判断@Qualifier指定依赖名和实际依赖名是否相同
			if (actualValue == null && attributeName.equals(AutowireCandidateQualifier.VALUE_KEY) &&
					expectedValue instanceof String && bdHolder.matchesName((String) expectedValue)) {
				// Fall back on bean name (or alias) match
				continue;
			}
			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());
			}
			if (!expectedValue.equals(actualValue)) {
				return false;
			}
		}
		return true;
	}

总结下,spring处理通过@Autowired结合@Qualifier注入依赖时,先判断类型,再判断依赖上是否存在@Qualifier,不存在的话再通过@Qualifier属性指定的名称跟依赖名比较判断能否注入依赖,更多请关注微信公众号 葡萄开源

相关推荐
吃饱了得干活21 分钟前
缓存与数据库一致性:从理论到实战
java·后端·面试
520拼好饭被践踏29 分钟前
JAVA+Agent学习day22
java·开发语言·后端·学习
whi33 分钟前
V 编译器 v3 ownership 模式:编译与使用指南
后端·编译器
swipe41 分钟前
05|(前端转后全栈)不手写一堆 SQL,后端怎么操作数据库?MyBatis-Plus 入门
前端·后端·全栈
霸道流氓气质42 分钟前
SpringBoot中使用JasperReports 报表引擎 — 介绍、原理与使用实践
java·spring boot·后端
swipe1 小时前
04|(前端转后全栈)前端状态为什么不够用?从页面数据到 MySQL 持久化
前端·后端·全栈
苏三说技术1 小时前
为什么越来越多人使用WebFlux?
后端
武子康1 小时前
Ask/Allow 不是安全边界:企业 Coding Agent 必须建立四层治理(Policy / Scoped Credential / Sandbox / Provenance)
人工智能·后端·agent
Wang's Blog1 小时前
Go-Zero项目开发17: IM私聊功能实现与消息存储设计
开发语言·后端·golang