返回介绍

Spring 系列

MyBatis

Netty

Dubbo

Tomcat

Redis

Nacos

Sentinel

RocketMQ

番外篇(JDK 1.8)

学习心得

SpringBoot ConditionalOnBean

发布于 2024-05-19 21:34:34 字数 27008 浏览 0 评论 0 收藏 0

SpringBoot ConditionalOnBean

  • Author: HuiFer
  • 源码阅读仓库: SourceHot-spring-boot

  • 在 SpringBoot 中有下列当 XXX 存在或不存的时候执行初始化

    • ConditionalOnBean ConditionalOnClass ConditionalOnCloudPlatform ConditionalOnExpression ConditionalOnJava ConditionalOnJndi ConditionalOnMissingBean ConditionalOnMissingClass ConditionalOnNotWebApplication ConditionalOnProperty ConditionalOnResource ConditionalOnSingleCandidate ConditionalOnWebApplication

ConditionalOnBean

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnBean {

   /**
    * 需要匹配的 bean 类型
    */
   Class<?>[] value() default {};

   /**
    * 需要匹配的 bean 类型
    */
   String[] type() default {};

   /**
    * 匹配的 bean 注解
    */
   Class<? extends Annotation>[] annotation() default {};

   /**
    * 需要匹配的 beanName
    */
   String[] name() default {};

   /**
    * 搜索策略
    */
   SearchStrategy search() default SearchStrategy.ALL;

   /**
    */
   Class<?>[] parameterizedContainer() default {};

}

SearchStrategy

public enum SearchStrategy {

   /**
    * 当前 上下文
    */
   CURRENT,

   /**
    * 找所有的父容器
    */
   ANCESTORS,

   /**
    * 当前上下文+父容器
    */
   ALL

}

OnBeanCondition

  • org.springframework.boot.autoconfigure.condition.OnBeanCondition

  • 这个类是一个条件类,相关的还有

    org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
    org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
    org.springframework.boot.autoconfigure.condition.OnClassCondition,\
    org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
    
  • 类图

    image-20200824085726621

在看这部分源码之前需要先了解 ConditionalCondition的源码

  • 简单描述

    通过实现Condition 来确认是否初始化 bean

  • 从类图上我们可以看到 condition 的继承关系. 在这里需要去找到SpringBootCondition

  • org.springframework.boot.autoconfigure.condition.SpringBootCondition#matches(org.springframework.context.annotation.ConditionContext, org.springframework.core.type.AnnotatedTypeMetadata)

    @Override
    public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
       // 类名或者方法名标记
       String classOrMethodName = getClassOrMethodName(metadata);
       try {
          // 比较类,子类实现
          ConditionOutcome outcome = getMatchOutcome(context, metadata);
          // 日志输出
          logOutcome(classOrMethodName, outcome);
          // 报告记录
          recordEvaluation(context, classOrMethodName, outcome);
          // 返回匹配结果
          return outcome.isMatch();
       }
       catch (NoClassDefFoundError ex) {
          throw new IllegalStateException("Could not evaluate condition on " + classOrMethodName + " due to "
                + ex.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)", ex);
       }
       catch (RuntimeException ex) {
          throw new IllegalStateException("Error processing condition on " + getName(metadata), ex);
       }
    }
    
  • getOutcomes 子类实现

    org.springframework.boot.autoconfigure.condition.OnBeanCondition#getOutcomes

    String[] autoConfigurationClasses,
                AutoConfigurationMetadata autoConfigurationMetadata
    
    • 第一个参数: 需要自动配置的类
    • 配置注解信息

ConditionOutcome 和 ConditionMessage

public class ConditionOutcome {

   /**
    * 是否匹配
    */
   private final boolean match;
   /**
    * 条件信息
    */
   private final ConditionMessage message;
}


public final class ConditionMessage {

    private String message;
}
  • 造一个对象用来进行 debug

@Component
public class Beans {


    @Bean
    public A a() {
        return new A();
    }


    @Bean
    @ConditionalOnBean(value = A.class)
    public B b() {
        return new B();
    }
}

getMatchOutcome

@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
    // 条件信息
    ConditionMessage matchMessage = ConditionMessage.empty();
    // 获取注解求和
    MergedAnnotations annotations = metadata.getAnnotations();
    // 注解是否匹配
    if (annotations.isPresent(ConditionalOnBean.class)) {
        // 搜索 ConditionalOnBean 注解
        Spec<ConditionalOnBean> spec = new Spec<>(context, metadata, annotations,
                ConditionalOnBean.class);
        // 匹配结果
        MatchResult matchResult = getMatchingBeans(context, spec);
        if (!matchResult.isAllMatched()) {
            String reason = createOnBeanNoMatchReason(matchResult);
            return ConditionOutcome.noMatch(spec.message().because(reason));
        }
        // 把注解解析出来获得文本
        matchMessage = spec.message(matchMessage).found("bean", "beans").items(Style.QUOTE,
                matchResult.getNamesOfAllMatches());
    }
    if (metadata.isAnnotated(ConditionalOnSingleCandidate.class.getName())) {
        Spec<ConditionalOnSingleCandidate> spec = new SingleCandidateSpec(context, metadata, annotations);
        MatchResult matchResult = getMatchingBeans(context, spec);
        if (!matchResult.isAllMatched()) {
            return ConditionOutcome.noMatch(spec.message().didNotFind("any beans").atAll());
        }
        else if (!hasSingleAutowireCandidate(context.getBeanFactory(), matchResult.getNamesOfAllMatches(),
                spec.getStrategy() == SearchStrategy.ALL)) {
            return ConditionOutcome.noMatch(spec.message().didNotFind("a primary bean from beans")
                    .items(Style.QUOTE, matchResult.getNamesOfAllMatches()));
        }
        matchMessage = spec.message(matchMessage).found("a primary bean from beans").items(Style.QUOTE,
                matchResult.getNamesOfAllMatches());
    }
    if (metadata.isAnnotated(ConditionalOnMissingBean.class.getName())) {
        Spec<ConditionalOnMissingBean> spec = new Spec<>(context, metadata, annotations,
                ConditionalOnMissingBean.class);
        MatchResult matchResult = getMatchingBeans(context, spec);
        if (matchResult.isAnyMatched()) {
            String reason = createOnMissingBeanNoMatchReason(matchResult);
            return ConditionOutcome.noMatch(spec.message().because(reason));
        }
        matchMessage = spec.message(matchMessage).didNotFind("any beans").atAll();
    }
    return ConditionOutcome.match(matchMessage);
}
  • 开始方法分析

getMatchingBeans

  • org.springframework.boot.autoconfigure.condition.OnBeanCondition#getMatchingBeans
protected final MatchResult getMatchingBeans(ConditionContext context, Spec<?> spec) {
    // 获取上下文
       ClassLoader classLoader = context.getClassLoader();
       // 获取 IOC 容器
   ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
   // 扫描方式比较是否为当前上下文
       boolean considerHierarchy = spec.getStrategy() != SearchStrategy.CURRENT;

   Set<Class<?>> parameterizedContainers = spec.getParameterizedContainers();
   if (spec.getStrategy() == SearchStrategy.ANCESTORS) {
      BeanFactory parent = beanFactory.getParentBeanFactory();
      Assert.isInstanceOf(ConfigurableListableBeanFactory.class, parent,
            "Unable to use SearchStrategy.ANCESTORS");
      beanFactory = (ConfigurableListableBeanFactory) parent;
   }
   // 结果对象初始化
   MatchResult result = new MatchResult();
   Set<String> beansIgnoredByType = getNamesOfBeansIgnoredByType(classLoader, beanFactory, considerHierarchy,
         spec.getIgnoredTypes(), parameterizedContainers);
   for (String type : spec.getTypes()) {
      // 通过类型获取 beanName
      Collection<String> typeMatches = getBeanNamesForType(classLoader, considerHierarchy, beanFactory, type,
            parameterizedContainers);
      typeMatches.removeAll(beansIgnoredByType);
      if (typeMatches.isEmpty()) {
         result.recordUnmatchedType(type);
      }
      else {
         result.recordMatchedType(type, typeMatches);
      }
   }
   for (String annotation : spec.getAnnotations()) {
      Set<String> annotationMatches = getBeanNamesForAnnotation(classLoader, beanFactory, annotation,
            considerHierarchy);
      annotationMatches.removeAll(beansIgnoredByType);
      if (annotationMatches.isEmpty()) {
         result.recordUnmatchedAnnotation(annotation);
      }
      else {
         result.recordMatchedAnnotation(annotation, annotationMatches);
      }
   }
   for (String beanName : spec.getNames()) {
      if (!beansIgnoredByType.contains(beanName) && containsBean(beanFactory, beanName, considerHierarchy)) {
         result.recordMatchedName(beanName);
      }
      else {
         result.recordUnmatchedName(beanName);
      }
   }
   return result;
}
  • MatchResult result = new MatchResult() 之前的代码作用是确认 ioc 容器

getNamesOfBeansIgnoredByType

    /**
     * 获取忽略的beans(返回对象是 beanName)
     * 循环,忽略的类型, 将类型从 beanFactory 获取,返回
     */
    private Set<String> getNamesOfBeansIgnoredByType(ClassLoader classLoader, ListableBeanFactory beanFactory,
            boolean considerHierarchy, Set<String> ignoredTypes, Set<Class<?>> parameterizedContainers) {
        Set<String> result = null;
        for (String ignoredType : ignoredTypes) {
            // 从 beanFactory 中获取忽略的beanNames
            Collection<String> ignoredNames = getBeanNamesForType(classLoader, considerHierarchy, beanFactory,
                    ignoredType, parameterizedContainers);
            result = addAll(result, ignoredNames);
        }
        return (result != null) ? result : Collections.emptySet();
    }

getBeanNamesForType

/**
 * 通过类型获取 beanName
 */
private Set<String> getBeanNamesForType(ClassLoader classLoader, boolean considerHierarchy,
      ListableBeanFactory beanFactory, String type, Set<Class<?>> parameterizedContainers) throws LinkageError {
   try {
       // 从beanFactory 中获取忽略的类 返回beanNanme
      return getBeanNamesForType(beanFactory, considerHierarchy, resolve(type, classLoader),
            parameterizedContainers);
   }
   catch (ClassNotFoundException | NoClassDefFoundError ex) {
      return Collections.emptySet();
   }
}

getBeanNamesForType

/**
 * 通过类型获取 beanName
 */
private Set<String> getBeanNamesForType(ListableBeanFactory beanFactory, boolean considerHierarchy, Class<?> type,
      Set<Class<?>> parameterizedContainers) {
    // 获取beanName
   Set<String> result = collectBeanNamesForType(beanFactory, considerHierarchy, type, parameterizedContainers,
         null);
   return (result != null) ? result : Collections.emptySet();
}

collectBeanNamesForType

  • 这里最终回到了 spring beanFactory 的方法 getBeanNamesForType
private Set<String> collectBeanNamesForType(ListableBeanFactory beanFactory, boolean considerHierarchy,
      Class<?> type, Set<Class<?>> parameterizedContainers, Set<String> result) {
   result = addAll(result, beanFactory.getBeanNamesForType(type, true, false));
   for (Class<?> container : parameterizedContainers) {
      ResolvableType generic = ResolvableType.forClassWithGenerics(container, type);
      result = addAll(result, beanFactory.getBeanNamesForType(generic, true, false));
   }
   if (considerHierarchy && beanFactory instanceof HierarchicalBeanFactory) {
      BeanFactory parent = ((HierarchicalBeanFactory) beanFactory).getParentBeanFactory();
      if (parent instanceof ListableBeanFactory) {
         result = collectBeanNamesForType((ListableBeanFactory) parent, considerHierarchy, type,
               parameterizedContainers, result);
      }
   }
   return result;
}

到这里需要忽略的 beanName 就全部找出来了

// 匹配类型在移除
for (String type : spec.getTypes()) {
    // 通过类型获取 beanName
    Collection<String> typeMatches = getBeanNamesForType(classLoader, considerHierarchy, beanFactory, type,
            parameterizedContainers);
    typeMatches.removeAll(beansIgnoredByType);
    if (typeMatches.isEmpty()) {
        result.recordUnmatchedType(type);
    }
    else {
        result.recordMatchedType(type, typeMatches);
    }
}


        // 注解匹配删除忽略的beanname
        for (String annotation : spec.getAnnotations()) {
            Set<String> annotationMatches = getBeanNamesForAnnotation(classLoader, beanFactory, annotation,
                    considerHierarchy);
            annotationMatches.removeAll(beansIgnoredByType);
            if (annotationMatches.isEmpty()) {
                result.recordUnmatchedAnnotation(annotation);
            }
            else {
                result.recordMatchedAnnotation(annotation, annotationMatches);
            }
        }
  • 在忽略 bean 找到之后做一个类型移除的操作.

image-20200825140750035

返回值

  • 在返回之前做一堆判断条件. 一旦符合条件这个地方会做一个 noMatch 的一个对象(ConditionOutcome) ,通过返回 match 对象ConditionOutcome
public static ConditionOutcome noMatch(ConditionMessage message) {
   return new ConditionOutcome(false, message);
}
        if (!matchResult.isAllMatched()) {
                String reason = createOnBeanNoMatchReason(matchResult);
                return ConditionOutcome.noMatch(spec.message().because(reason));
            }
            // 把注解解析出来获得文本
            matchMessage = spec.message(matchMessage).found("bean", "beans").items(Style.QUOTE,
                    matchResult.getNamesOfAllMatches());
        }
        if (metadata.isAnnotated(ConditionalOnSingleCandidate.class.getName())) {
            Spec<ConditionalOnSingleCandidate> spec = new SingleCandidateSpec(context, metadata, annotations);
            MatchResult matchResult = getMatchingBeans(context, spec);
            if (!matchResult.isAllMatched()) {
                return ConditionOutcome.noMatch(spec.message().didNotFind("any beans").atAll());
            }
            else if (!hasSingleAutowireCandidate(context.getBeanFactory(), matchResult.getNamesOfAllMatches(),
                    spec.getStrategy() == SearchStrategy.ALL)) {
                return ConditionOutcome.noMatch(spec.message().didNotFind("a primary bean from beans")
                        .items(Style.QUOTE, matchResult.getNamesOfAllMatches()));
            }
            matchMessage = spec.message(matchMessage).found("a primary bean from beans").items(Style.QUOTE,
                    matchResult.getNamesOfAllMatches());
        }
        if (metadata.isAnnotated(ConditionalOnMissingBean.class.getName())) {
            Spec<ConditionalOnMissingBean> spec = new Spec<>(context, metadata, annotations,
                    ConditionalOnMissingBean.class);
            MatchResult matchResult = getMatchingBeans(context, spec);
            if (matchResult.isAnyMatched()) {
                String reason = createOnMissingBeanNoMatchReason(matchResult);
                return ConditionOutcome.noMatch(spec.message().because(reason));
            }
            matchMessage = spec.message(matchMessage).didNotFind("any beans").atAll();
        }
        return ConditionOutcome.match(matchMessage);

image-20200825141506531

  • 到此结果封装完毕.回到方法org.springframework.boot.autoconfigure.condition.SpringBootCondition#matches(org.springframework.context.annotation.ConditionContext, org.springframework.core.type.AnnotatedTypeMetadata) 继续进行
    • 再往后就继续执行 spring 的 bean 初始化咯

MessageSourceAutoConfiguration

  • 启动阶段的一个类运行解读

  • org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnMissingBean(name = AbstractApplicationContext.MESSAGE_SOURCE_BEAN_NAME, search = SearchStrategy.CURRENT)
    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
    @Conditional(ResourceBundleCondition.class)
    @EnableConfigurationProperties
    public class MessageSourceAutoConfiguration {}
    
    • 根据类的注解信息我们可以找到有ResourceBundleCondition

      image-20200825092343271

  • 获取类名或者方法名的结果是MessageSourceAutoConfiguration全路径

  • 继续往下是一个比较的方法(是否符合 match)

    org.springframework.boot.autoconfigure.condition.SpringBootCondition#getMatchOutcome这个方法是一个抽象方法子类实现

  • 上图中红框内标注的类为org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration.ResourceBundleCondition

    同时继承org.springframework.boot.autoconfigure.condition.SpringBootCondition

    并且重写了方法getMatchOutcome

    @Override
    public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // 从 容器中获取
        String basename = context.getEnvironment().getProperty("spring.messages.basename", "messages");
        // 从缓存中获取条件信息
        ConditionOutcome outcome = cache.get(basename);
        if (outcome == null) {
            // 生成条件信息对象
            outcome = getMatchOutcomeForBasename(context, basename);
            // 放入缓存
            cache.put(basename, outcome);
        }
        return outcome;
    }
    

    这个方法主要将比较信息放入,

  • 后续的行为依然是判断是否匹配,匹配就创建.

Spring Boot 启动阶段的自动注入

org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#filter
private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {
   long startTime = System.nanoTime();
   String[] candidates = StringUtils.toStringArray(configurations);
   boolean[] skip = new boolean[candidates.length];
   boolean skipped = false;
   // 获取 AutoConfigurationImportFilter 相关配置
   for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
      // 执行 aware 相关接口
      invokeAwareMethods(filter);
      // 是否可以初始化的结果
      boolean[] match = filter.match(candidates, autoConfigurationMetadata);
      for (int i = 0; i < match.length; i++) {
         if (!match[i]) {
            // 是否跳过
            skip[i] = true;
            candidates[i] = null;
            skipped = true;
         }
      }
   }
   if (!skipped) {
      return configurations;
   }
   List<String> result = new ArrayList<>(candidates.length);
   // 处理最终需要的类
   for (int i = 0; i < candidates.length; i++) {
      if (!skip[i]) {
         result.add(candidates[i]);
      }
   }
   if (logger.isTraceEnabled()) {
      int numberFiltered = configurations.size() - result.size();
      logger.trace("Filtered " + numberFiltered + " auto configuration class in "
            + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
   }
   return new ArrayList<>(result);
}
  • 在这里有一个关注点 循环方法getAutoConfigurationImportFilters()
protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
   return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
}

spring.factories文件中找到AutoConfigurationImportFilter后面的值

org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
  • 此时我们可以和前文的源码分析连接起来有一个完整的认识了

    image-20200825142332485

  • 最后来看整体类图

    image-20200825142418115

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文