“Spring 对于 NoUniqueBeanDefinition(不惟一的Bean)的解决”
NoUniqueBeanDefinitionException
Spring IoC
容器有两种实现依赖管制的办法,一种是依赖注入,一种是依赖查找,在默认状况下,当某个类型的Bean
存在多个的状况下,都可能会产生NoUniqueBeanDefinitionException
。
应用谬误倒推的办法看看依赖注入以及依赖查找对不惟一Bean
的解决。
依赖注入剖析
谬误点:在依赖注入解决多个Bean
时,当所有计划都没用(啥都没配置时),会应用DependencyDescriptor#resolveNotUnique
抛出异样
public class DependencyDescriptor extends InjectionPoint implements Serializable {...... @Nullable public Object resolveNotUnique(ResolvableType type, Map<String, Object> matchingBeans) throws BeansException { throw new NoUniqueBeanDefinitionException(type, matchingBeans.keySet()); }......}
剖析入口:DefaultListableBeanFactory#doResolveDependency
doResolveDependency
@Nullablepublic Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor); try { ...... //解析是否汇合类型(包含Map)类型的Bean,是返回Bean汇合 Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter); if (multipleBeans != null) { return multipleBeans; } //获取能够进行依赖注入符合条件的所有Bean Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor); if (matchingBeans.isEmpty()) { if (isRequired(descriptor)) { raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor); } return null; } String autowiredBeanName; Object instanceCandidate; if (matchingBeans.size() > 1) { //若不止一个Bean,从中决定一个Bean进行注入 autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor); if (autowiredBeanName == null) { if (isRequired(descriptor) || !indicatesMultipleBeans(type)) { //决定不了,且不容许为空,抛出异样 return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans); } else { // In case of an optional Collection/Map, silently ignore a non-unique case: // possibly it was meant to be an empty collection of multiple regular beans // (before 4.3 in particular when we didn't even look for collection beans). return null; } } instanceCandidate = matchingBeans.get(autowiredBeanName); } else { // We have exactly one match. Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next(); autowiredBeanName = entry.getKey(); instanceCandidate = entry.getValue(); } ...... return result; } finally { ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint); }}
findAutowireCandidates
查找适合的Bean
,会依据autowireCandidate
进行判断Bean
是否能用于主动拆卸。isAutowireCandidate
办法进过一系列调用会应用AutowireCandidateResolver
进行判断,默认应用ContextAnnotationAutowireCandidateResolver
,蕴含了autowireCandidate
判断,以及@Qualifier
判断。
protected Map<String, Object> findAutowireCandidates( @Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) { //先把合乎类型的Bean都查问进去 String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this, requiredType, true, descriptor.isEager()); Map<String, Object> result = CollectionUtils.newLinkedHashMap(candidateNames.length); ...... for (String candidate : candidateNames) { //isAutowireCandidate会先依据autowireCandidate判断,而后会应用@Qualifier判断,若容许则退出result中 if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) { addCandidateEntry(result, candidate, descriptor, requiredType); } } if (result.isEmpty()) { //后续保险匹配,可疏忽 boolean multiple = 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) && (!multiple || getAutowireCandidateResolver().hasQualifier(descriptor))) { addCandidateEntry(result, candidate, descriptor, requiredType); } } if (result.isEmpty() && !multiple) { // 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;}
AutowireCandidateResolver
默认依据BeanDefinition#autowireCandidate
判断,子类QualifierAnnotationAutowireCandidateResolver
提供@Qualifier
解决能力,默认ContextAnnotationAutowireCandidateResolver
继承QualifierAnnotationAutowireCandidateResolver
。
public interface AutowireCandidateResolver { /** * Determine whether the given bean definition qualifies as an * autowire candidate for the given dependency. * <p>The default implementation checks * {@link org.springframework.beans.factory.config.BeanDefinition#isAutowireCandidate()}. * @param bdHolder the bean definition including bean name and aliases * @param descriptor the descriptor for the target method parameter or field * @return whether the bean definition qualifies as autowire candidate * @see org.springframework.beans.factory.config.BeanDefinition#isAutowireCandidate() */ default boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) { return bdHolder.getBeanDefinition().isAutowireCandidate(); }}
determineAutowireCandidate
蕴含了Primary
、@Priority
、名称匹配三种策略
/** * Determine the autowire candidate in the given set of beans. * <p>Looks for {@code @Primary} and {@code @Priority} (in that order). * @param candidates 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 autowire candidate, or {@code null} if none found */@Nullableprotected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) { Class<?> requiredType = descriptor.getDependencyType(); //是否指定了Primary String primaryCandidate = determinePrimaryCandidate(candidates, requiredType); if (primaryCandidate != null) { return primaryCandidate; } //是否有应用@Priority指定优先级 String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType); if (priorityCandidate != null) { return priorityCandidate; } // Fallback for (Map.Entry<String, Object> entry : candidates.entrySet()) { String candidateName = entry.getKey(); Object beanInstance = entry.getValue(); //最初兜底,应用BeanName和注入属性的名称进行匹配 if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) || matchesBeanName(candidateName, descriptor.getDependencyName())) { return candidateName; } } return null;}
依赖查找剖析
依赖查找有两种,一种是指定Bean的名称查找,不存在不惟一的状况,另一种便是依据类型去查找,可能会呈现NoUniqueBeanDefinitionException
。
谬误点:resolveNamedBean
抛出异样
剖析入口:DefaultListableBeanFactory#resolveNamedBean
resolveNamedBean
依据类型getBean
,最终会调用resolveNamedBean
获取NamedBeanHolder
以取得对应的Bean
,当存在多个Bean时,此办法顺次依据BeanDefinition#autowireCandidate
、Primary
以及Priority
进行解决,在无奈决定的状况下会报错。
private <T> NamedBeanHolder<T> resolveNamedBean( ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) throws BeansException { Assert.notNull(requiredType, "Required type must not be null"); String[] candidateNames = getBeanNamesForType(requiredType); if (candidateNames.length > 1) { List<String> autowireCandidates = new ArrayList<>(candidateNames.length); for (String beanName : candidateNames) { //判断BeanDefinition#autowireCandidate if (!containsBeanDefinition(beanName) || getBeanDefinition(beanName).isAutowireCandidate()) { autowireCandidates.add(beanName); } } if (!autowireCandidates.isEmpty()) { candidateNames = StringUtils.toStringArray(autowireCandidates); } } if (candidateNames.length == 1) { return resolveNamedBean(candidateNames[0], requiredType, args); } else if (candidateNames.length > 1) { Map<String, Object> candidates = CollectionUtils.newLinkedHashMap(candidateNames.length); for (String beanName : candidateNames) { if (containsSingleton(beanName) && args == null) { Object beanInstance = getBean(beanName); candidates.put(beanName, (beanInstance instanceof NullBean ? null : beanInstance)); } else { candidates.put(beanName, getType(beanName)); } } //判断Primary String candidateName = determinePrimaryCandidate(candidates, requiredType.toClass()); if (candidateName == null) { //依据@Priority筛选优先级最高的 candidateName = determineHighestPriorityCandidate(candidates, requiredType.toClass()); } if (candidateName != null) { Object beanInstance = candidates.get(candidateName); if (beanInstance == null) { return null; } if (beanInstance instanceof Class) { return resolveNamedBean(candidateName, requiredType, args); } return new NamedBeanHolder<>(candidateName, (T) beanInstance); } if (!nonUniqueAsNull) { throw new NoUniqueBeanDefinitionException(requiredType, candidates.keySet()); } } return null;}
总结
对于NoUniqueBeanDefinitionException
,咱们有多种解决形式,按依赖注入&依赖查找来讲
依赖注入(按程序)
BeanDefinition#autowireCandidate
属性设置,只保留一个为true的@Qualifier
注解进行解决- 指定
Primary
的Bean
@Priority
注解指定程序- 兜底名称匹配
依赖查找-按类型(按程序)
BeanDefinition#autowireCandidate
属性设置,只保留一个为true的- 指定
Primary
的Bean
@Priority
注解指定程序
References
- https://tablesheep233.github....
原创不易,禁止未受权的转载。如果我的文章对您有帮忙,就请点赞/珍藏/关注激励反对一下吧