spring中的autowird流程思考

疑问:

今天在Spring的项目中遇到了NoUniqueBeanDefinitionException的问题。

这个已经是耳熟能详的问题,开发过spring的人都知道使用了@autowired的注解,如果xml中配置了实现类超过1个都会发生这个问题,如implA和implB。

翻阅了代码后发现果然是有多个实现类,咨询了开发人员后发现他们近期没有加过任何配置,反而删除过一个implC,那么问题来了?

为什么之前没有报错!!!autowired不是byType吗?

网上搜索了资料也都是描述autowired是按照类型自动装配的。

翻了源码后才恍然大悟,原来spring的autowired在处理处理多个实现的时候,会看默认名称,如果bean name 等于属性名称,则会被自动注入而不报错。

释疑:

Map matchingBeans = findAutowireCandidates(beanName, type, descriptor);
if (matchingBeans.isEmpty()) {
    if (descriptor.isRequired()) {
        raiseNoSuchBeanDefinitionException(type, "", descriptor);
    }
    return null;
}
if (matchingBeans.size() > 1) {
    String primaryBeanName = determinePrimaryCandidate(matchingBeans, descriptor);
    if (primaryBeanName == null) {
        throw new NoSuchBeanDefinitionException(type, "expected single matching bean but found " +
                matchingBeans.size() + ": " + matchingBeans.keySet());
    }
    if (autowiredBeanNames != null) {
        autowiredBeanNames.add(primaryBeanName);
    }
    return matchingBeans.get(primaryBeanName);
}
// We have exactly one match.
Map.Entry entry = matchingBeans.entrySet().iterator().next();
if (autowiredBeanNames != null) {
    autowiredBeanNames.add(entry.getKey());
}
return entry.getValue();


以上代码来自于类DefaultListableBeanFactory的doResolveDependency方法。

这是由类AutowiredAnnotationBeanPostProcessor类通过调用inject方法时,需要通过调用beanFactory.resolveDependency(descriptor, beanName, autowiredBeanNames, typeConverter)来解析引用信息。

分析流程如下:

  1. 首先根据类型找到所有可以满足条件的bean
  2. 判断bean是否为空,如果没有,则根据@autowired中的required属性进行判断是否抛出异常(默认为true)
  3. 如果多于一个,则尝试寻找最优的那一个,如果最优的未找到,则抛出异常(猫腻在这里,详见下方代码)
  4. 如果只有一个,则直接使用此bean
	/**
	 * Determine the primary autowire candidate in the given set of beans.
	 * @param candidateBeans a Map of candidate names and candidate instances
	 * that match the required type, as returned by {@link #findAutowireCandidates}
	 * @param descriptor the target dependency to match against
	 * @return the name of the primary candidate, or {@code null} if none found
	 */
	protected String determinePrimaryCandidate(Map candidateBeans, DependencyDescriptor descriptor) {
		String primaryBeanName = null;
		String fallbackBeanName = null;
		for (Map.Entry entry : candidateBeans.entrySet()) {
			String candidateBeanName = entry.getKey();
			Object beanInstance = entry.getValue();
			if (isPrimary(candidateBeanName, beanInstance)) {
				if (primaryBeanName != null) {
					boolean candidateLocal = containsBeanDefinition(candidateBeanName);
					boolean primaryLocal = containsBeanDefinition(primaryBeanName);
					if (candidateLocal == primaryLocal) {
						throw new NoUniqueBeanDefinitionException(descriptor.getDependencyType(), candidateBeans.size(),
								"more than one 'primary' bean found among candidates: " + candidateBeans.keySet());
					}
					else if (candidateLocal && !primaryLocal) {
						primaryBeanName = candidateBeanName;
					}
				}
				else {
					primaryBeanName = candidateBeanName;
				}
			}
			if (primaryBeanName == null &&
					(this.resolvableDependencies.values().contains(beanInstance) ||
							matchesBeanName(candidateBeanName, descriptor.getDependencyName()))) {
				fallbackBeanName = candidateBeanName;
			}
		}
		return (primaryBeanName != null ? primaryBeanName : fallbackBeanName);
	}

从最后的判断逻辑我们可以看到matchesBeanName方法,原来autowired不是单纯byType的,在impl有多个的情况下,代码会按照bean name进行匹配。


总结一下:
       @autowired这个注解的流程和xml配置中的default-autowire-byType不是一回事,有种被误导的感觉。
       虽然是一个常见错误,但是还有自己没有注意到的疑问,引以为鉴。


你可能感兴趣的:(spring)