SpringBoot ConditionalOnBean

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

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

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

ConditionalOnBean

  1. @Target({ ElementType.TYPE, ElementType.METHOD })
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Conditional(OnBeanCondition.class)
  5. public @interface ConditionalOnBean {
  6. /**
  7. * 需要匹配的 bean 类型
  8. */
  9. Class<?>[] value() default {};
  10. /**
  11. * 需要匹配的 bean 类型
  12. */
  13. String[] type() default {};
  14. /**
  15. * 匹配的 bean 注解
  16. */
  17. Class<? extends Annotation>[] annotation() default {};
  18. /**
  19. * 需要匹配的 beanName
  20. */
  21. String[] name() default {};
  22. /**
  23. * 搜索策略
  24. */
  25. SearchStrategy search() default SearchStrategy.ALL;
  26. /**
  27. */
  28. Class<?>[] parameterizedContainer() default {};
  29. }

SearchStrategy

  1. public enum SearchStrategy {
  2. /**
  3. * 当前 上下文
  4. */
  5. CURRENT,
  6. /**
  7. * 找所有的父容器
  8. */
  9. ANCESTORS,
  10. /**
  11. * 当前上下文+父容器
  12. */
  13. ALL
  14. }

OnBeanCondition

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

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

    1. org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
    2. org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
    3. org.springframework.boot.autoconfigure.condition.OnClassCondition,\
    4. 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)

    1. @Override
    2. public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    3. // 类名或者方法名标记
    4. String classOrMethodName = getClassOrMethodName(metadata);
    5. try {
    6. // 比较类,子类实现
    7. ConditionOutcome outcome = getMatchOutcome(context, metadata);
    8. // 日志输出
    9. logOutcome(classOrMethodName, outcome);
    10. // 报告记录
    11. recordEvaluation(context, classOrMethodName, outcome);
    12. // 返回匹配结果
    13. return outcome.isMatch();
    14. }
    15. catch (NoClassDefFoundError ex) {
    16. throw new IllegalStateException("Could not evaluate condition on " + classOrMethodName + " due to "
    17. + ex.getMessage() + " not found. Make sure your own configuration does not rely on "
    18. + "that class. This can also happen if you are "
    19. + "@ComponentScanning a springframework package (e.g. if you "
    20. + "put a @ComponentScan in the default package by mistake)", ex);
    21. }
    22. catch (RuntimeException ex) {
    23. throw new IllegalStateException("Error processing condition on " + getName(metadata), ex);
    24. }
    25. }
  • getOutcomes 子类实现

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

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

ConditionOutcome 和 ConditionMessage

  1. public class ConditionOutcome {
  2. /**
  3. * 是否匹配
  4. */
  5. private final boolean match;
  6. /**
  7. * 条件信息
  8. */
  9. private final ConditionMessage message;
  10. }
  11. public final class ConditionMessage {
  12. private String message;
  13. }
  • 造一个对象用来进行 debug
  1. @Component
  2. public class Beans {
  3. @Bean
  4. public A a() {
  5. return new A();
  6. }
  7. @Bean
  8. @ConditionalOnBean(value = A.class)
  9. public B b() {
  10. return new B();
  11. }
  12. }

getMatchOutcome

  1. @Override
  2. public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
  3. // 条件信息
  4. ConditionMessage matchMessage = ConditionMessage.empty();
  5. // 获取注解求和
  6. MergedAnnotations annotations = metadata.getAnnotations();
  7. // 注解是否匹配
  8. if (annotations.isPresent(ConditionalOnBean.class)) {
  9. // 搜索 ConditionalOnBean 注解
  10. Spec<ConditionalOnBean> spec = new Spec<>(context, metadata, annotations,
  11. ConditionalOnBean.class);
  12. // 匹配结果
  13. MatchResult matchResult = getMatchingBeans(context, spec);
  14. if (!matchResult.isAllMatched()) {
  15. String reason = createOnBeanNoMatchReason(matchResult);
  16. return ConditionOutcome.noMatch(spec.message().because(reason));
  17. }
  18. // 把注解解析出来获得文本
  19. matchMessage = spec.message(matchMessage).found("bean", "beans").items(Style.QUOTE,
  20. matchResult.getNamesOfAllMatches());
  21. }
  22. if (metadata.isAnnotated(ConditionalOnSingleCandidate.class.getName())) {
  23. Spec<ConditionalOnSingleCandidate> spec = new SingleCandidateSpec(context, metadata, annotations);
  24. MatchResult matchResult = getMatchingBeans(context, spec);
  25. if (!matchResult.isAllMatched()) {
  26. return ConditionOutcome.noMatch(spec.message().didNotFind("any beans").atAll());
  27. }
  28. else if (!hasSingleAutowireCandidate(context.getBeanFactory(), matchResult.getNamesOfAllMatches(),
  29. spec.getStrategy() == SearchStrategy.ALL)) {
  30. return ConditionOutcome.noMatch(spec.message().didNotFind("a primary bean from beans")
  31. .items(Style.QUOTE, matchResult.getNamesOfAllMatches()));
  32. }
  33. matchMessage = spec.message(matchMessage).found("a primary bean from beans").items(Style.QUOTE,
  34. matchResult.getNamesOfAllMatches());
  35. }
  36. if (metadata.isAnnotated(ConditionalOnMissingBean.class.getName())) {
  37. Spec<ConditionalOnMissingBean> spec = new Spec<>(context, metadata, annotations,
  38. ConditionalOnMissingBean.class);
  39. MatchResult matchResult = getMatchingBeans(context, spec);
  40. if (matchResult.isAnyMatched()) {
  41. String reason = createOnMissingBeanNoMatchReason(matchResult);
  42. return ConditionOutcome.noMatch(spec.message().because(reason));
  43. }
  44. matchMessage = spec.message(matchMessage).didNotFind("any beans").atAll();
  45. }
  46. return ConditionOutcome.match(matchMessage);
  47. }
  • 开始方法分析

getMatchingBeans

  • org.springframework.boot.autoconfigure.condition.OnBeanCondition#getMatchingBeans
  1. protected final MatchResult getMatchingBeans(ConditionContext context, Spec<?> spec) {
  2. // 获取上下文
  3. ClassLoader classLoader = context.getClassLoader();
  4. // 获取 IOC 容器
  5. ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
  6. // 扫描方式比较是否为当前上下文
  7. boolean considerHierarchy = spec.getStrategy() != SearchStrategy.CURRENT;
  8. Set<Class<?>> parameterizedContainers = spec.getParameterizedContainers();
  9. if (spec.getStrategy() == SearchStrategy.ANCESTORS) {
  10. BeanFactory parent = beanFactory.getParentBeanFactory();
  11. Assert.isInstanceOf(ConfigurableListableBeanFactory.class, parent,
  12. "Unable to use SearchStrategy.ANCESTORS");
  13. beanFactory = (ConfigurableListableBeanFactory) parent;
  14. }
  15. // 结果对象初始化
  16. MatchResult result = new MatchResult();
  17. Set<String> beansIgnoredByType = getNamesOfBeansIgnoredByType(classLoader, beanFactory, considerHierarchy,
  18. spec.getIgnoredTypes(), parameterizedContainers);
  19. for (String type : spec.getTypes()) {
  20. // 通过类型获取 beanName
  21. Collection<String> typeMatches = getBeanNamesForType(classLoader, considerHierarchy, beanFactory, type,
  22. parameterizedContainers);
  23. typeMatches.removeAll(beansIgnoredByType);
  24. if (typeMatches.isEmpty()) {
  25. result.recordUnmatchedType(type);
  26. }
  27. else {
  28. result.recordMatchedType(type, typeMatches);
  29. }
  30. }
  31. for (String annotation : spec.getAnnotations()) {
  32. Set<String> annotationMatches = getBeanNamesForAnnotation(classLoader, beanFactory, annotation,
  33. considerHierarchy);
  34. annotationMatches.removeAll(beansIgnoredByType);
  35. if (annotationMatches.isEmpty()) {
  36. result.recordUnmatchedAnnotation(annotation);
  37. }
  38. else {
  39. result.recordMatchedAnnotation(annotation, annotationMatches);
  40. }
  41. }
  42. for (String beanName : spec.getNames()) {
  43. if (!beansIgnoredByType.contains(beanName) && containsBean(beanFactory, beanName, considerHierarchy)) {
  44. result.recordMatchedName(beanName);
  45. }
  46. else {
  47. result.recordUnmatchedName(beanName);
  48. }
  49. }
  50. return result;
  51. }
  • MatchResult result = new MatchResult() 之前的代码作用是确认 ioc 容器

getNamesOfBeansIgnoredByType

  1. /**
  2. * 获取忽略的beans(返回对象是 beanName)
  3. * 循环,忽略的类型, 将类型从 beanFactory 获取,返回
  4. */
  5. private Set<String> getNamesOfBeansIgnoredByType(ClassLoader classLoader, ListableBeanFactory beanFactory,
  6. boolean considerHierarchy, Set<String> ignoredTypes, Set<Class<?>> parameterizedContainers) {
  7. Set<String> result = null;
  8. for (String ignoredType : ignoredTypes) {
  9. // 从 beanFactory 中获取忽略的beanNames
  10. Collection<String> ignoredNames = getBeanNamesForType(classLoader, considerHierarchy, beanFactory,
  11. ignoredType, parameterizedContainers);
  12. result = addAll(result, ignoredNames);
  13. }
  14. return (result != null) ? result : Collections.emptySet();
  15. }

getBeanNamesForType

  1. /**
  2. * 通过类型获取 beanName
  3. */
  4. private Set<String> getBeanNamesForType(ClassLoader classLoader, boolean considerHierarchy,
  5. ListableBeanFactory beanFactory, String type, Set<Class<?>> parameterizedContainers) throws LinkageError {
  6. try {
  7. // 从beanFactory 中获取忽略的类 返回beanNanme
  8. return getBeanNamesForType(beanFactory, considerHierarchy, resolve(type, classLoader),
  9. parameterizedContainers);
  10. }
  11. catch (ClassNotFoundException | NoClassDefFoundError ex) {
  12. return Collections.emptySet();
  13. }
  14. }

getBeanNamesForType

  1. /**
  2. * 通过类型获取 beanName
  3. */
  4. private Set<String> getBeanNamesForType(ListableBeanFactory beanFactory, boolean considerHierarchy, Class<?> type,
  5. Set<Class<?>> parameterizedContainers) {
  6. // 获取beanName
  7. Set<String> result = collectBeanNamesForType(beanFactory, considerHierarchy, type, parameterizedContainers,
  8. null);
  9. return (result != null) ? result : Collections.emptySet();
  10. }

collectBeanNamesForType

  • 这里最终回到了 spring beanFactory 的方法 getBeanNamesForType
  1. private Set<String> collectBeanNamesForType(ListableBeanFactory beanFactory, boolean considerHierarchy,
  2. Class<?> type, Set<Class<?>> parameterizedContainers, Set<String> result) {
  3. result = addAll(result, beanFactory.getBeanNamesForType(type, true, false));
  4. for (Class<?> container : parameterizedContainers) {
  5. ResolvableType generic = ResolvableType.forClassWithGenerics(container, type);
  6. result = addAll(result, beanFactory.getBeanNamesForType(generic, true, false));
  7. }
  8. if (considerHierarchy && beanFactory instanceof HierarchicalBeanFactory) {
  9. BeanFactory parent = ((HierarchicalBeanFactory) beanFactory).getParentBeanFactory();
  10. if (parent instanceof ListableBeanFactory) {
  11. result = collectBeanNamesForType((ListableBeanFactory) parent, considerHierarchy, type,
  12. parameterizedContainers, result);
  13. }
  14. }
  15. return result;
  16. }

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

  1. // 匹配类型在移除
  2. for (String type : spec.getTypes()) {
  3. // 通过类型获取 beanName
  4. Collection<String> typeMatches = getBeanNamesForType(classLoader, considerHierarchy, beanFactory, type,
  5. parameterizedContainers);
  6. typeMatches.removeAll(beansIgnoredByType);
  7. if (typeMatches.isEmpty()) {
  8. result.recordUnmatchedType(type);
  9. }
  10. else {
  11. result.recordMatchedType(type, typeMatches);
  12. }
  13. }
  14. // 注解匹配删除忽略的beanname
  15. for (String annotation : spec.getAnnotations()) {
  16. Set<String> annotationMatches = getBeanNamesForAnnotation(classLoader, beanFactory, annotation,
  17. considerHierarchy);
  18. annotationMatches.removeAll(beansIgnoredByType);
  19. if (annotationMatches.isEmpty()) {
  20. result.recordUnmatchedAnnotation(annotation);
  21. }
  22. else {
  23. result.recordMatchedAnnotation(annotation, annotationMatches);
  24. }
  25. }
  • 在忽略 bean 找到之后做一个类型移除的操作.

image-20200825140750035

返回值

  • 在返回之前做一堆判断条件. 一旦符合条件这个地方会做一个 noMatch 的一个对象(ConditionOutcome) ,通过返回 match 对象ConditionOutcome
  1. public static ConditionOutcome noMatch(ConditionMessage message) {
  2. return new ConditionOutcome(false, message);
  3. }
  1. if (!matchResult.isAllMatched()) {
  2. String reason = createOnBeanNoMatchReason(matchResult);
  3. return ConditionOutcome.noMatch(spec.message().because(reason));
  4. }
  5. // 把注解解析出来获得文本
  6. matchMessage = spec.message(matchMessage).found("bean", "beans").items(Style.QUOTE,
  7. matchResult.getNamesOfAllMatches());
  8. }
  9. if (metadata.isAnnotated(ConditionalOnSingleCandidate.class.getName())) {
  10. Spec<ConditionalOnSingleCandidate> spec = new SingleCandidateSpec(context, metadata, annotations);
  11. MatchResult matchResult = getMatchingBeans(context, spec);
  12. if (!matchResult.isAllMatched()) {
  13. return ConditionOutcome.noMatch(spec.message().didNotFind("any beans").atAll());
  14. }
  15. else if (!hasSingleAutowireCandidate(context.getBeanFactory(), matchResult.getNamesOfAllMatches(),
  16. spec.getStrategy() == SearchStrategy.ALL)) {
  17. return ConditionOutcome.noMatch(spec.message().didNotFind("a primary bean from beans")
  18. .items(Style.QUOTE, matchResult.getNamesOfAllMatches()));
  19. }
  20. matchMessage = spec.message(matchMessage).found("a primary bean from beans").items(Style.QUOTE,
  21. matchResult.getNamesOfAllMatches());
  22. }
  23. if (metadata.isAnnotated(ConditionalOnMissingBean.class.getName())) {
  24. Spec<ConditionalOnMissingBean> spec = new Spec<>(context, metadata, annotations,
  25. ConditionalOnMissingBean.class);
  26. MatchResult matchResult = getMatchingBeans(context, spec);
  27. if (matchResult.isAnyMatched()) {
  28. String reason = createOnMissingBeanNoMatchReason(matchResult);
  29. return ConditionOutcome.noMatch(spec.message().because(reason));
  30. }
  31. matchMessage = spec.message(matchMessage).didNotFind("any beans").atAll();
  32. }
  33. 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

    1. @Configuration(proxyBeanMethods = false)
    2. @ConditionalOnMissingBean(name = AbstractApplicationContext.MESSAGE_SOURCE_BEAN_NAME, search = SearchStrategy.CURRENT)
    3. @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
    4. @Conditional(ResourceBundleCondition.class)
    5. @EnableConfigurationProperties
    6. 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

    1. @Override
    2. public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
    3. // 从 容器中获取
    4. String basename = context.getEnvironment().getProperty("spring.messages.basename", "messages");
    5. // 从缓存中获取条件信息
    6. ConditionOutcome outcome = cache.get(basename);
    7. if (outcome == null) {
    8. // 生成条件信息对象
    9. outcome = getMatchOutcomeForBasename(context, basename);
    10. // 放入缓存
    11. cache.put(basename, outcome);
    12. }
    13. return outcome;
    14. }

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

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

Spring Boot 启动阶段的自动注入

  1. org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#filter
  1. private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {
  2. long startTime = System.nanoTime();
  3. String[] candidates = StringUtils.toStringArray(configurations);
  4. boolean[] skip = new boolean[candidates.length];
  5. boolean skipped = false;
  6. // 获取 AutoConfigurationImportFilter 相关配置
  7. for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
  8. // 执行 aware 相关接口
  9. invokeAwareMethods(filter);
  10. // 是否可以初始化的结果
  11. boolean[] match = filter.match(candidates, autoConfigurationMetadata);
  12. for (int i = 0; i < match.length; i++) {
  13. if (!match[i]) {
  14. // 是否跳过
  15. skip[i] = true;
  16. candidates[i] = null;
  17. skipped = true;
  18. }
  19. }
  20. }
  21. if (!skipped) {
  22. return configurations;
  23. }
  24. List<String> result = new ArrayList<>(candidates.length);
  25. // 处理最终需要的类
  26. for (int i = 0; i < candidates.length; i++) {
  27. if (!skip[i]) {
  28. result.add(candidates[i]);
  29. }
  30. }
  31. if (logger.isTraceEnabled()) {
  32. int numberFiltered = configurations.size() - result.size();
  33. logger.trace("Filtered " + numberFiltered + " auto configuration class in "
  34. + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
  35. }
  36. return new ArrayList<>(result);
  37. }
  • 在这里有一个关注点 循环方法getAutoConfigurationImportFilters()
  1. protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
  2. return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
  3. }

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

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

    image-20200825142332485

  • 最后来看整体类图

    image-20200825142418115