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

相关推荐
大鸡腿同学17 小时前
【成长类】《只有偏执狂才能生存》读书笔记:程序员的偏执型成长地图
后端
0xDevNull17 小时前
MySQL数据冷热分离详解
后端·mysql
AI袋鼠帝18 小时前
OpenClaw(龙虾)最强开源对手!Github 40K Star了,又一个爆火的Agent..
后端
新知图书19 小时前
搭建Spring Boot开发环境
java·spring boot·后端
宸津-代码粉碎机19 小时前
Spring Boot 4.0虚拟线程实战调优技巧,最大化发挥并发优势
java·人工智能·spring boot·后端·python
小码哥_常20 小时前
一个Starter搞定六种防护,Spring Boot API的超强护盾来了
后端
小村儿21 小时前
连载04-最重要的Skill---一起吃透 Claude Code,告别 AI coding 迷茫
前端·后端·ai编程
IT_陈寒1 天前
Vite的alias配置把我整不会了,原来是这个坑
前端·人工智能·后端
gelald1 天前
Spring Boot - 自动配置原理
java·spring boot·后端
希望永不加班1 天前
SpringBoot 集成测试:@SpringBootTest 与 MockMvc
java·spring boot·后端·log4j·集成测试