@ConditionalOnBean源码分析

@ConditionalOnBean注解使用了@Condition注解,@Condition注解中使用的条件类是OnBeanCondition类,那就看下这个类。

在此之前,顺便先提一句,@ConditionalOnBean 是匹配目前为止由应用程序上下文处理过的bean定义,而不是 bean实例。bean实例还早的很。网上很多文章都写的是bean实例,误人子弟,后面源码会分析到。源码有些多,还请耐心看下去,如果实在看不下去,可看下此注解的注释,也有简单的说明。

OnBeanCondition类继承FilteringSpringBootCondition。

FilteringSpringBootCondition又继承SpringBootCondition。

SpringBootCondition的matches方法。

//SpringBootCondition类中的matches方法
public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    //获取当前注解标记的类名或者方法名(由标注的位置决定)
    String classOrMethodName = getClassOrMethodName(metadata);
    try {
        //关键代码:这里就会判断出结果
        ConditionOutcome outcome = this.getMatchOutcome(context, metadata);
        //存入日志
        this.logOutcome(classOrMethodName, outcome);
        //存入记录
        this.recordEvaluation(context, classOrMethodName, outcome);
        //最后返回ConditionOutcome的isMatch就是返回boolean类型结果
        return outcome.isMatch();
    } catch (NoClassDefFoundError var5) {
        throw new IllegalStateException("Could not evaluate condition on " + classOrMethodName + " due to " + var5.getMessage() + " not found. Make sure your own configuration does not rely on that class. This can also happen if you are @ComponentScanning a springframework package (e.g. if you put a @ComponentScan in the default package by mistake)", var5);
    } catch (RuntimeException var6) {
        throw new IllegalStateException("Error processing condition on " + this.getName(metadata), var6);
    }
}

关键代码在OnBeanCondition的getMatchOutcome方法中。

/**
 * 获得判断结果的方法,ConditionOutcome类中存着boolean类型的结果
 */
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
   //返回一个新的ConditionMessage
   ConditionMessage matchMessage = ConditionMessage.empty();
   //这是metadata会调用isAnnotated方法判断当前标注的注解是不是ConditionalOnBean
   if (metadata.isAnnotated(ConditionalOnBean.class.getName())) {
       //返回一个spec(说明),这里的spec规定了搜索的内容,比如搜索策略、需要搜索的类名......
      BeanSearchSpec spec = new BeanSearchSpec(context, metadata, ConditionalOnBean.class);
      //主要的搜索实现在这个方法里
      MatchResult matchResult = getMatchingBeans(context, spec);
      //判断搜索出来的结果
      if (!matchResult.isAllMatched()) {
         String reason = createOnBeanNoMatchReason(matchResult);
         return ConditionOutcome
               .noMatch(ConditionMessage.forCondition(ConditionalOnBean.class, spec).because(reason));
      }
      matchMessage = matchMessage.andCondition(ConditionalOnBean.class, spec).found("bean", "beans")
            .items(Style.QUOTE, matchResult.getNamesOfAllMatches());
   }
   //这是metadata会调用isAnnotated方法判断当前标注的注解是不是ConditionalOnSingleCandidate
   if (metadata.isAnnotated(ConditionalOnSingleCandidate.class.getName())) {
      BeanSearchSpec spec = new SingleCandidateBeanSearchSpec(context, metadata,
            ConditionalOnSingleCandidate.class);
      MatchResult matchResult = getMatchingBeans(context, spec);
      if (!matchResult.isAllMatched()) {
         return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnSingleCandidate.class, spec)
               .didNotFind("any beans").atAll());
      }
      else if (!hasSingleAutowireCandidate(context.getBeanFactory(), matchResult.getNamesOfAllMatches(),
            spec.getStrategy() == SearchStrategy.ALL)) {
         return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnSingleCandidate.class, spec)
               .didNotFind("a primary bean from beans")
               .items(Style.QUOTE, matchResult.getNamesOfAllMatches()));
      }
      matchMessage = matchMessage.andCondition(ConditionalOnSingleCandidate.class, spec)
            .found("a primary bean from beans").items(Style.QUOTE, matchResult.getNamesOfAllMatches());
   }
   //这是metadata会调用isAnnotated方法判断当前标注的注解是不是ConditionalOnMissingBean
   if (metadata.isAnnotated(ConditionalOnMissingBean.class.getName())) {
      BeanSearchSpec spec = new BeanSearchSpec(context, metadata, ConditionalOnMissingBean.class);
      MatchResult matchResult = getMatchingBeans(context, spec);
      if (matchResult.isAnyMatched()) {
         String reason = createOnMissingBeanNoMatchReason(matchResult);
         return ConditionOutcome
               .noMatch(ConditionMessage.forCondition(ConditionalOnMissingBean.class, spec).because(reason));
      }
      matchMessage = matchMessage.andCondition(ConditionalOnMissingBean.class, spec).didNotFind("any beans")
            .atAll();
   }
   return ConditionOutcome.match(matchMessage);
}

BeanSearchSpec spec = new OnBeanCondition.BeanSearchSpec(context, metadata, ConditionalOnBean.class);

相当于将标注@ConditionalOnMissingBean注解时的属性都取出来放到内部类BeanSearchSpec中。

public BeanSearchSpec(ConditionContext context, AnnotatedTypeMetadata metadata, Class<?> annotationType,
      Class<?> genericContainer) {
   this.annotationType = annotationType;
   MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(annotationType.getName(),
         true);
   //注解的name属性
   collect(attributes, "name", this.names);
   //注解的value属性
   collect(attributes, "value", this.types);
   //注解的type属性
   collect(attributes, "type", this.types);
   collect(attributes, "annotation", this.annotations);
   collect(attributes, "ignored", this.ignoredTypes);
   collect(attributes, "ignoredType", this.ignoredTypes);
   collect(attributes, "parameterizedContainer", this.parameterizedContainers);
   this.strategy = (SearchStrategy) attributes.getFirst("search");
   BeanTypeDeductionException deductionException = null;
   try {
      if (this.types.isEmpty() && this.names.isEmpty()) {
         addDeducedBeanType(context, metadata, this.types);
      }
   }
   catch (BeanTypeDeductionException ex) {
      deductionException = ex;
   }
   validate(deductionException);
}

看主要的搜索实现方法 getMatchingBeans。

protected final MatchResult getMatchingBeans(ConditionContext context, BeanSearchSpec beans) {
   //获得当前bean工厂
   ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
   //判断当前的搜索策略是否是PARENTS或者ANCESTORS,默认是ALL
   if (beans.getStrategy() == SearchStrategy.ANCESTORS) {
      BeanFactory parent = beanFactory.getParentBeanFactory();
      Assert.isInstanceOf(ConfigurableListableBeanFactory.class, parent,
            "Unable to use SearchStrategy.ANCESTORS");
      //如果是PARENTS或者ANCESTORS,当前bean工厂就用父工厂      
      beanFactory = (ConfigurableListableBeanFactory) parent;
   }
   MatchResult matchResult = new MatchResult();
   //如果当前搜索策略不等于CURRENT,为true
   boolean considerHierarchy = beans.getStrategy() != SearchStrategy.CURRENT;
   TypeExtractor typeExtractor = beans.getTypeExtractor(context.getClassLoader());
   List<String> beansIgnoredByType = getNamesOfBeansIgnoredByType(beans.getIgnoredTypes(), typeExtractor,
         beanFactory, context, considerHierarchy);
   //注解的value和types都在types里面      
   for (String type : beans.getTypes()) {
      //看 getBeanNamesForType 方法
      Collection<String> typeMatches = getBeanNamesForType(beanFactory, type, typeExtractor,
            context.getClassLoader(), considerHierarchy);
      typeMatches.removeAll(beansIgnoredByType);
      if (typeMatches.isEmpty()) {
         matchResult.recordUnmatchedType(type);
      }
      else {
         matchResult.recordMatchedType(type, typeMatches);
      }
   }
   for (String annotation : beans.getAnnotations()) {
      List<String> annotationMatches = Arrays.asList(
            getBeanNamesForAnnotation(beanFactory, annotation, context.getClassLoader(), considerHierarchy));
      annotationMatches.removeAll(beansIgnoredByType);
      if (annotationMatches.isEmpty()) {
         matchResult.recordUnmatchedAnnotation(annotation);
      }
      else {
         matchResult.recordMatchedAnnotation(annotation, annotationMatches);
      }
   }
   for (String beanName : beans.getNames()) {
      if (!beansIgnoredByType.contains(beanName) && containsBean(beanFactory, beanName, considerHierarchy)) {
         matchResult.recordMatchedName(beanName);
      }
      else {
         matchResult.recordUnmatchedName(beanName);
      }
   }
   return matchResult;
}

//根据类型获取bean的name
private Collection<String> getBeanNamesForType(ListableBeanFactory beanFactory, String type,
      TypeExtractor typeExtractor, ClassLoader classLoader, boolean considerHierarchy) throws LinkageError {
   try {
      //继续看 getBeanNamesForType 方法
      return getBeanNamesForType(beanFactory, considerHierarchy, ClassUtils.forName(type, classLoader),
            typeExtractor);
   }
   catch (ClassNotFoundException | NoClassDefFoundError ex) {
      return Collections.emptySet();
   }
}

private Collection<String> getBeanNamesForType(ListableBeanFactory beanFactory, boolean considerHierarchy,
      Class<?> type, TypeExtractor typeExtractor) {
   Set<String> result = new LinkedHashSet<>();
   //继续看 collectBeanNamesForType 方法
   collectBeanNamesForType(result, beanFactory, type, typeExtractor, considerHierarchy);
   return result;
}

private void collectBeanNamesForType(Set<String> result, ListableBeanFactory beanFactory, Class<?> type,
      TypeExtractor typeExtractor, boolean considerHierarchy) {
   //先看下 BeanTypeRegistry.get(beanFactory);
   BeanTypeRegistry registry = BeanTypeRegistry.get(beanFactory);
   //关键这里 registry.getNamesForType(type, typeExtractor)
   result.addAll(registry.getNamesForType(type, typeExtractor));
   if (considerHierarchy && beanFactory instanceof HierarchicalBeanFactory) {
      BeanFactory parent = ((HierarchicalBeanFactory) beanFactory).getParentBeanFactory();
      if (parent instanceof ListableBeanFactory) {
         collectBeanNamesForType(result, (ListableBeanFactory) parent, type, typeExtractor, considerHierarchy);
      }
   }
}

注册 BeanTypeRegistry bean定义,并获取实例。

这个就到了熟悉的getBean了,就不用往下看了,回到上面的 collectBeanNamesForType 方法。

继续看 registry.getNamesForType(type, typeExtractor)

可以看到,是与 beanTypes 去匹配查找,那么 beanTypes 的数据从哪里来的?看 updateTypesIfNecessary(); 方法。

先看下 RootBeanDefinition definition = getBeanDefinition(name);

到这里,就再明确不过了,获取的是bean定义,而不是网上那些说的实例。

private void addBeanTypeForNonAliasDefinition(String name) {
   //拿到bean定义 
   RootBeanDefinition definition = getBeanDefinition(name);
   if (definition != null) {
       //这里就是加到 beanTypes 中去。
      addBeanTypeForNonAliasDefinition(name, definition);
   }
}

继续看 addBeanTypeForNonAliasDefinition(name, definition); 方法。

private void addBeanTypeForNonAliasDefinition(String name, RootBeanDefinition definition) {
   try {
      if (!definition.isAbstract() && !requiresEagerInit(definition.getFactoryBeanName())) {
         //根据bean定义返回封装的 ResolvableType 
         ResolvableType factoryMethodReturnType = getFactoryMethodReturnType(definition);
         String factoryBeanName = BeanFactory.FACTORY_BEAN_PREFIX + name;
         //这里判断是不是 factoryBean,是的话需要特殊处理下。
         if (this.beanFactory.isFactoryBean(factoryBeanName)) {
            ResolvableType factoryBeanGeneric = getFactoryBeanGeneric(this.beanFactory, definition,
                  factoryMethodReturnType);
            this.beanTypes.put(name, factoryBeanGeneric);
            this.beanTypes.put(factoryBeanName, getType(factoryBeanName, factoryMethodReturnType));
         }
         // 其他的就直接放了。
         else {
            /* 
             * getType(name, factoryMethodReturnType) 返回的也是 ResolvableType,
             * ResolvableType 是封装了bean定义。
             * 这里相当于再去检查一下。
             */
            this.beanTypes.put(name, getType(name, factoryMethodReturnType));
         }
      }
      //并且 BeanTypeRegistry 中的还有 beanDefinitions 属性,存放bean定义。
      this.beanDefinitions.put(name, definition);
   }
   catch (CannotLoadBeanClassException ex) {
      // Probably contains a placeholder
      logIgnoredError("bean class loading failure for bean", name, ex);
   }
}

到这里,就把主要的源码分析完了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: @ConditionalOnBean 是 Spring 框架中的一个条件注解,用于指定在特定的 Bean 存在时才会生效。 通常情况下,我们使用 @ConditionalOnBean 注解来限制某个 Bean 的加载条件。当被注解的类或方法放置在 Bean 的定义上时,Spring 容器会根据指定的条件判断是否加载该 Bean。只有当条件满足时,才会将该 Bean 加入到 Spring 容器中。 @ConditionalOnBean 注解可以用于任何 Spring Bean 上,如 @Service、@Component、@Configuration 等等。它的参数是一个或多个 Class 类型的数组,指定了需要判断是否存在的 Bean 的类型。只有当指定的所有 Bean 都存在于 Spring 容器中,条件才会满足。 使用 @ConditionalOnBean 注解时,可以将多个条件连接起来以构成复杂的条件判断。通过在多个条件注解之间添加逻辑运算符,如 @ConditionalOnBean({BeanA.class, BeanB.class}) 表示只有当 BeanA 和 BeanB 同时存在时,条件才会满足。 这个注解的主要作用是根据特定的 Bean 的存在与否来决定特定的功能是否可用。比如,当我们的应用依赖于某个外部库的存在时,可以使用 @ConditionalOnBean 注解来限制某个组件的加载,在外部库存在时才加载该组件。 总之,@ConditionalOnBean 是 Spring 框架中用于条件化加载 Bean 的注解之一,可以根据特定 Bean 的存在来决定是否加载某个 Bean,从而实现更加灵活和可配置的应用开发。 ### 回答2: @ConditionalOnBean是Spring框架中的一个注解,用于指定一个组件的条件,只有当指定的Bean存在于Spring容器中时,才会创建被注解的组件。 使用@ConditionalOnBean注解可以在某些特定的条件下,动态地决定是否创建一个特定的组件。它的作用是根据指定的Bean是否存在来决定某个组件是否应该被创建。 @ConditionalOnBean的使用方法是将它放在一个类上面,这个类可以是任意可以被Spring容器扫描到的类,比如配置类、bean类等。注解中需要指定的参数是一个或多个被依赖的Bean的类名或类的全限定名。 当被注解的类被加载到Spring容器中时,会先检查指定的Bean是否存在于容器中。如果存在,则创建被注解的组件;如果不存在,则不创建该组件。 通过@ConditionalOnBean注解,我们可以根据应用条件来动态地控制组件的创建。比如,我们可以根据不同的数据源配置,优先选择某个特定的数据源作为默认数据源。 总结起来,@ConditionalOnBean是一个根据指定的Bean的存在与否来决定是否创建某个组件的注解。它提供了一种简单而灵活的方式来根据特定条件动态地选择组件的创建。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值