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

相关推荐
JELEE.3 小时前
Django登录注册完整代码(图片、邮箱验证、加密)
前端·javascript·后端·python·django·bootstrap·jquery
QX_hao5 小时前
【Go】--反射(reflect)的使用
开发语言·后端·golang
小坏讲微服务5 小时前
Docker-compose 搭建Maven私服部署
java·spring boot·后端·docker·微服务·容器·maven
yuuki2332336 小时前
【数据结构】用顺序表实现通讯录
c语言·数据结构·后端
你的人类朋友6 小时前
【Node】手动归还主线程控制权:解决 Node.js 阻塞的一个思路
前端·后端·node.js
史不了7 小时前
静态交叉编译rust程序
开发语言·后端·rust
码事漫谈8 小时前
C++中的多态:动态多态与静态多态详解
后端
码事漫谈8 小时前
单链表反转:从基础到进阶的完整指南
后端
与遨游于天地8 小时前
Spring解决循环依赖实际就是用了个递归
java·后端·spring