“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#autowireCandidatePrimary以及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,咱们有多种解决形式,按依赖注入&依赖查找来讲

  • 依赖注入(按程序)

    1. BeanDefinition#autowireCandidate属性设置,只保留一个为true的
    2. @Qualifier注解进行解决
    3. 指定PrimaryBean
    4. @Priority注解指定程序
    5. 兜底名称匹配
  • 依赖查找-按类型(按程序)

    1. BeanDefinition#autowireCandidate属性设置,只保留一个为true的
    2. 指定PrimaryBean
    3. @Priority注解指定程序

References

  • https://tablesheep233.github....
原创不易,禁止未受权的转载。如果我的文章对您有帮忙,就请点赞/珍藏/关注激励反对一下吧