spring在帮我们管理bean的时候,会帮我们完成自动注入,其中有一个比较特殊的类型:list
这篇笔记主要记录spring注入list集合的原理

应用

public interface Rest {

}

@Component
public class RestServiceImpl01 implements Rest{

}

@Component
public class RestServiceImpl02 implements Rest{

}

@Component
public class OrderService {
    @Autowired
    //@Qualifier
    private List<Rest> restList;

    public void test() {
        System.out.println("打印注入的集合的值,restList:" + restList);
    }
}

public class Test {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
        OrderService orderService = ac.getBean(OrderService.class);
        orderService.test();
    }
}

以上代码执行之后,打印的结果是:

打印注入的集合的值,restList:[com.spring.list.RestServiceImpl01@60611244, com.spring.list.RestServiceImpl02@3745e5c6]

spring中,在使用@Autowired注解注入list集合的时候,并不会根据List类型去容器中查找,而是根据list集合的元素类型,从spring容器中找到所有的实现类,放在list集合中,然后注入到bean中

那如果我们想要指定只注入部分bean怎么办呢?

@Component
public class OrderService {
    @Autowired
    @Qualifier
    private List<Rest> restList;

    public void test() {
        System.out.println("打印注入的集合的值,restList:" + restList);
    }
}

只需要把这的@Qualifier注解放开,然后在需要注入的bean上,加上这个注解

@Component
@Qualifier
public class RestServiceImpl02 implements Rest{

}

此时再运行代码:打印注入的集合的值,restList:[com.spring.list.RestServiceImpl02@d706f19]

所以这就是注入list集合bean的应用

原理

对于bean的注入,如果我们使用的是@Autowired注解,会被AutowiredAnnotationBeanPostProcessor处理

链路:

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessProperties
	org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject
			org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#resolveFieldValue
	org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency
		org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency		

在doResolveDependency方法中,有一个代码逻辑

Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
if (multipleBeans != null) {
	return multipleBeans;
}

这里就是来解析list类型的

org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveMultipleBeans
else if (Collection.class.isAssignableFrom(type) && type.isInterface()) {
	Class<?> elementType = descriptor.getResolvableType().asCollection().resolveGeneric();
	if (elementType == null) {
		return null;
	}
	// 在这里会取解析list集合中指定的接口所有的实现类
	Map<String, Object> matchingBeans = findAutowireCandidates(beanName, elementType,
			new MultiElementDescriptor(descriptor));
	if (matchingBeans.isEmpty()) {
		return null;
	}
	if (autowiredBeanNames != null) {
		autowiredBeanNames.addAll(matchingBeans.keySet());
	}
	TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
	Object result = converter.convertIfNecessary(matchingBeans.values(), type);
	if (getDependencyComparator() != null && result instanceof List) {
		((List<?>) result).sort(adaptDependencyComparator(matchingBeans));
	}
	return result;
}

在这个方法中,这段代码是来解析list集合的,所以只截取了这一部分代码,这一部分关键的代码是:findAutowireCandidates方法

protected Map<String, Object> findAutowireCandidates(
		@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {

	/**
	 * 根据类型,获取当前beanDefinitionMap中的beanName,注意:这里是从beanDefinitionMap中获取的,并不是直接从spring
	 * 容器中获取
	 * 获取到的是待注入bean的name
	 */
	String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
			this, requiredType, true, descriptor.isEager());
	Map<String, Object> result = new LinkedHashMap<>(candidateNames.length);
	/**
	 * resolvableDependencies:这里来遍历这个集合,判断要注入的bean是否是该类型的
	 * resolvableDependencies这个集合,默认有四个值
	 * interface org.springframework.context.ApplicationContext" -> {AnnotationConfigApplicationContext@1641}
	 * interface org.springframework.beans.factory.BeanFactory" -> {DefaultListableBeanFactory@1630}
	 * interface org.springframework.core.io.ResourceLoader" -> {AnnotationConfigApplicationContext@1641} 
	 * interface org.springframework.context.ApplicationEventPublisher -> {AnnotationConfigApplicationContext@1641}
	 */
	for (Class<?> autowiringType : this.resolvableDependencies.keySet()) {
		if (autowiringType.isAssignableFrom(requiredType)) {
			Object autowiringValue = this.resolvableDependencies.get(autowiringType);
			autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
			if (requiredType.isInstance(autowiringValue)) {
				result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
				break;
			}
		}
	}
	/**
	 * 确切的说,是在isAutowireCandidate里面对Qualifier注解进行了判断
	 *  org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver#isAutowireCandidate(org.springframework.beans.factory.config.BeanDefinitionHolder, org.springframework.beans.factory.config.DependencyDescriptor)
	 */
	for (String candidate : candidateNames) {
		if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
			addCandidateEntry(result, candidate, descriptor, requiredType);
		}
	}
	if (result.isEmpty() && !indicatesMultipleBeans(requiredType)) {
		// Consider fallback matches if the first pass failed to find anything...
		DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
		for (String candidate : candidateNames) {
			if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor)) {
				addCandidateEntry(result, candidate, descriptor, requiredType);
			}
		}
		if (result.isEmpty()) {
			// Consider self references as a final pass...
			// but in the case of a dependency collection, not the very same bean itself.
			for (String candidate : candidateNames) {
				if (isSelfReference(beanName, candidate) &&
						(!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate)) &&
						isAutowireCandidate(candidate, fallbackDescriptor)) {
					addCandidateEntry(result, candidate, descriptor, requiredType);
				}
			}
		}
	}
	return result;
}

在源码中,就是在isAutowireCandidate()这个方法中,对@Qualifier注解进行的过滤,也就是说,如果我们在注入list集合的时候,没有添加@Qualifier注解,那这个方法都会返回true,然后将所有的实现类都返回
如果加了@Qualifier注解,这里只有加了@Qualifier注解的实现类会返回TRUE,会被返回

这个方法的实现细节,待研究,debug看源码的时候,看到这样的结果

结论

所以,在spring中,我们在注入list集合的时候,如果只加了@Autowired注解,那就会把集合元素的所有实现类都注入进来,如果想只注入指定的类,那就使用@Qualifier注解

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐