@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属性指定的名称跟依赖名比较判断能否注入依赖,更多请关注微信公众号 葡萄开源

相关推荐
葫芦和十三2 小时前
图解 MongoDB 23|两地三中心:跨可用区部署怎么扛机房故障
后端·mongodb·agent
勇哥java实战分享4 小时前
PaddleOCR 太慢?我换成 RapidOCR 后,速度直接起飞
后端
苏三说技术8 小时前
LangChain4j 和 LangGraph4j,哪个更好?
后端
ServBay9 小时前
7 个AI开发中真正用得上的 MCP Server,配合Claude Code食用效果更佳
后端·claude·mcp
妙码生花9 小时前
从 PHP 到 AI + Golang,程序员自救转型手记(十五):优化细节、网络请求封装
前端·后端·ai编程
用户67570498850210 小时前
Go 语言里判断字符串为空,90% 的人都写错了!
后端·go
用户67570498850210 小时前
Go 进阶必修:90% 的人都没用对的“表驱动法”
后端·go
小兔崽子去哪了10 小时前
Java 生成二维码解决方案
java·后端
苍何10 小时前
懂事的 Agent 已经开始自己看屏幕干活了,效率起飞!
后端