Spring Boot 之自动化配置和条件化配置(带源码解析)

Spring Boot 提供了一种条件化配置机制,通过 @Conditional 注解及其派生注解,可以根据特定条件来决定是否加载某些配置类或 Bean。这种机制在微服务架构中非常有用,可以根据环境、属性、类的存在性等条件来灵活地配置应用程序。

理解自动化配置对象

  • 每个自动化配置对象都需要使用 @AutoConfiguration 注解进行修饰,@AutoConfiguration 注解被 @Configuration 修饰,也就是说,@AutoConfiguration 注解是一个标准的 Configuration 类
  • **可以使用 @Conditional 注解限制自动化配置在何时被使用。**比如,使用 @ConditionalOnClass 和 @ConditionalOnMissingBean 注解,确保在找到相关的类和没有声明相关的类的时候加载自动化配置。

定位自动化配置

  • Spring Boot 会从 jar 的 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件中根据全类名加载自动化配置类。
com.mycorp.libx.autoconfigure.LibXAutoConfiguration
com.mycorp.libx.autoconfigure.LibXWebAutoConfiguration

你可以使用 # 字符在imports文件中添加注释。
自动配置类不应该通过 @ComponentScan 注解或其他方式让 Spring 自动扫描和加载,因为这可能会导致不必要或意外的对象被创建,从而干扰应用程序的正常运行
自动配置类中不应该包含 @ComponentScan 注解来扫描其他组件。如果你需要导入其他配置或类,应该使用 @Import 注解显式地导入。

  • 可以使用 @AutoConfiguration 注解中的 before、beforeName、after 和 afterName 属性,或者使用专门的 @AutoConfigureBefore 和 @AutoConfigureAfter 注解影响自动化配置对象的加载顺序。
  • 可以使用 @DependsOn 影响自动化配置对象的创建顺序。

Condition 注解

Class Conditions

  • @ConditionalOnClass 和 @ConditionalOnMissingClass 注解允许 @Configuration 类根据特定类的存在或不存在来决定是否包含这些配置类。由于注解元数据是通过 ASM 解析的,即使某个类在运行时的类路径上实际上不存在,你也可以通过 value 属性来引用该类。同时,你也可以使用 name 属性,通过字符串值指定类名。
  • 这种机制在 @Bean 方法上并不适用,因为 @Bean 方法通常依赖于方法的返回类型来进行条件判断:在应用条件之前,JVM 已经加载了该类,并可能处理了方法引用,如果该类不存在,则会导致处理失败。为了解决这个问题,可以使用一个单独的 @Configuration 类来隔离条件,如下例所示:
@AutoConfiguration
// Some conditions ...
public class MyAutoConfiguration {

    // Auto-configured beans ...

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(SomeService.class)
    public static class SomeServiceConfiguration {

        @Bean
        @ConditionalOnMissingBean
        public SomeService someService() {
            return new SomeService();
        }

    }

}

如果你将 @ConditionalOnClass 或 @ConditionalOnMissingClass 作为元注解的一部分来组合自定义的注解,在这种情况下,你必须使用 name 属性来引用类,因为使用 value 属性的方式不会被正确处理。

Bean Conditions

  • @ConditionalOnBean 和 @ConditionalOnMissingBean 注解允许根据特定 Bean 的存在或不存在来决定是否包含某个 Bean。你可以使用 value 属性按类型指定 Bean,或者使用 name 属性按名称指定 Bean。search 属性可以限制在搜索 Bean 时应考虑的 ApplicationContext 层次结构。
  • 当这些注解放置在 @Bean 方法上时,目标类型默认是方法的返回类型,如下例所示:
@AutoConfiguration
public class MyAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean
	public SomeService someService() {
		return new SomeService();
	}

}

在前面的例子中,如果 ApplicationContext 中还没有包含 SomeService 类型的 Bean,那么将创建 SomeService Bean。
一般来说,自动化配置 Bean 的创建在用户应用 Bean 之后,建议只在自动化配置对象中使用 @ConditionalOnBean 和 @ConditionalOnMissingBean 注解。以避免自动配置 Bean 干扰用户的应用 Bean。
在声明 @Bean 方法时,尽可能提供详细的类型信息。例如,如果你的 Bean 的具体实现类实现了一个接口,那么 @Bean 方法的返回类型应该是具体的实现类,而不是接口。因为在使用 Bean 条件(如 @ConditionalOnBean 或 @ConditionalOnMissingBean)时,条件的评估只能依赖于方法签名中可用的类型信息。

@Configuration
public class AppConfig {

    @Bean
    public UserService userService() {
        return new UserServiceImpl(); // 实际返回的是 UserServiceImpl
    }
}
@Configuration
public class AppConfig {

    @Bean
    public UserServiceImpl userService() {
        return new UserServiceImpl(); // 返回具体的实现类
    }
}

Property Conditions

@ConditionalOnProperty 注解允许根据 Spring Environment 的属性来决定是否包含某些配置。使用 prefix 和 name 属性可以指定需要检查的属性。默认情况下,任何存在且不等于 false 的属性都会匹配。你还可以通过使用 havingValue 和 matchIfMissing 属性来创建更复杂的检查。

@Configuration
@ConditionalOnProperty(prefix = "app", name = {"prop1", "prop2"}, havingValue = "true")
public class MultiPropertyConfig {
    @Bean
    public MultiPropertyService multiPropertyService() {
        return new MultiPropertyService();
    }
}

只有当 app.prop1 和 app.prop2 都存在且其值为 “true” 时,MultiPropertyConfig 类才会被加载,并注册 multiPropertyService Bean。

Resource Conditions

@ConditionalOnResource 注解允许仅在特定资源存在时包含某些配置。你可以使用 Spring 的常规资源表示方法来指定这些资源。

@Configuration
@ConditionalOnResource(resources = "file:/home/user/test.dat")
public class ResourceConfig {
    @Bean
    public MyService myService() {
        return new MyService();
    }
}

@ConditionalOnResource 注解检查是否存在位于 /home/user/test.dat 的文件资源。如果该资源存在,ResourceConfig 配置类会被加载,并且 myService Bean 会被创建和注册。

Web Application Conditions

@ConditionalOnWebApplication 和 @ConditionalOnNotWebApplication 注解用于根据应用程序是否是一个 Web 应用程序来决定是否包含某些配置。@ConditionalOnWarDeployment 和 @ConditionalOnNotWarDeployment 注解用于根据应用程序是否是一个传统的 WAR 部署应用程序来决定是否包含某些配置。

SpEL Expression Conditions

@ConditionalOnExpression 注解允许根据 SpEL(Spring Expression Language)表达式的结果来决定是否包含某些配置。使用这个注解可以让你基于更复杂的条件来决定配置是否生效。

@Configuration
@ConditionalOnExpression("#{systemProperties['os.name'].contains('Windows') and environment['app.mode'] == 'production'}")
public class WindowsProductionConfig {
    @Bean
    public WindowsProductionService windowsProductionService() {
        return new WindowsProductionService();
    }
}

#{systemProperties[‘os.name’].contains(‘Windows’) and environment[‘app.mode’] == ‘production’} 用于检查系统属性 os.name 是否包含 ‘Windows’ 以及环境属性 app.mode 是否等于 ‘production’。只有当这两个条件都满足时,WindowsProductionConfig 配置类才会被加载,并且 windowsProductionService Bean 将被创建和注册。
在使用 @ConditionalOnExpression 注解时,如果 SpEL 表达式中引用了某个 Bean,这个 Bean 可能会在 Spring 上下文的刷新过程中被过早初始化。

源码解析(基于 SpringBoot 3.2.0 版本)

Spring Boot 的入口

  • 当你调用 SpringApplication.run() 方法时,Spring Boot 开始启动过程。这是整个应用程序的入口。
  • Spring Boot 创建一个 ApplicationContext 实例(通常是 AnnotationConfigApplicationContext 或 AnnotationConfigServletWebServerApplicationContext)。

加载 AutoConfigurationImportSelector 并执行方法获取自动化配置类

主程序上一般加有 @SpringBootApplication 注解,@SpringBootApplication 注解带有 @EnableAutoConfiguration 注解,@EnableAutoConfiguration 注解带有 @Import(AutoConfigurationImportSelector.class) 注解,Spring Boot 会加载 AutoConfigurationImportSelector 类并执行其中的 selectImports 方法获得其中的自动化配置类。

ConfigurationClassPostProcessor
  • 代码进入到 ApplicationContext 的 refresh 方法中执行其中的 invokeBeanFactoryPostProcessors 方法,invokeBeanFactoryPostProcessors 会分批实例化并调用所有已注册的BeanDefinitionRegistryPostProcessor 对象的 postProcessBeanDefinitionRegistry 方法。 其中 BeanDefinitionRegistryPostProcessor 的实现类 ConfigurationClassPostProcessor 负责解析 Spring 容器中的所有配置类(包括通过 @Import 注解导入的类)
  protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(
        beanFactory, getBeanFactoryPostProcessors());
  }
  public static void invokeBeanFactoryPostProcessors(
      ConfigurableListableBeanFactory beanFactory,
      List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
    // 获取所有实现 BeanDefinitionRegistryPostProcessor 接口的 Bean 名称
    String[] postProcessorNames =
        beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
    // 对实现了 PriorityOrdered 的 BeanDefinitionRegistryPostProcessor 进行实例化并执行相应的方法
    // currentRegistryProcessors 保存当前需要执行的 BeanDefinitionRegistryPostProcessor 对象
    // processedBeans 所有的 BeanDefinitionRegistryPostProcessor 对象
    for (String ppName : postProcessorNames) {
      if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
        currentRegistryProcessors.add(
            beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
        processedBeans.add(ppName);
      }
    }
    sortPostProcessors(currentRegistryProcessors, beanFactory);
    // ConfigurationClassPostProcessor 会在这里执行
    invokeBeanDefinitionRegistryPostProcessors(
        currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
    // 清空 currentRegistryProcessors
    currentRegistryProcessors.clear();

    // 接下来,对实现了 Ordered 的 BeanDefinitionRegistryPostProcessor 进行实例化并执行相应的方法
    postProcessorNames =
        beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
    for (String ppName : postProcessorNames) {
      if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
        currentRegistryProcessors.add(
            beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
        processedBeans.add(ppName);
      }
    }
    sortPostProcessors(currentRegistryProcessors, beanFactory);
    registryProcessors.addAll(currentRegistryProcessors);
    invokeBeanDefinitionRegistryPostProcessors(
        currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
    currentRegistryProcessors.clear();

    // 最后,调用所有其他 BeanDefinitionRegistryPostProcessors,
    // 直到没有其他 BeanDefinitionRegistryPostProcessors 出现为止。
    boolean reiterate = true;
    while (reiterate) {
      reiterate = false;
      postProcessorNames =
          beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
      for (String ppName : postProcessorNames) {
        if (!processedBeans.contains(ppName)) {
          currentRegistryProcessors.add(
              beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
          processedBeans.add(ppName);
          reiterate = true;
        }
      }
      sortPostProcessors(currentRegistryProcessors, beanFactory);
      registryProcessors.addAll(currentRegistryProcessors);
      invokeBeanDefinitionRegistryPostProcessors(
          currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
      currentRegistryProcessors.clear();
    }

    // 后面还会对实现了 BeanFactoryPostProcessor 的对象进行实例化并执行相应的方法,这里省略
  }

  /** Invoke the given BeanDefinitionRegistryPostProcessor beans. */
  private static void invokeBeanDefinitionRegistryPostProcessors(
      Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors,
      BeanDefinitionRegistry registry,
      ApplicationStartup applicationStartup) {

    for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
      postProcessor.postProcessBeanDefinitionRegistry(registry);
    }
  }
在 ConfigurationClassPostProcessor 使用 ConfigurationClassParser 解析启动类,并将 AutoConfigurationImportSelector 加载进来
  • 在程序中,第一个被解析配置类的是被 @SpringBootApplication 注解修饰的启动类。@SpringBootApplication 带有 @SpringBootConfiguration 注解,@SpringBootConfiguration 注解中带有 @Configuration 注解。@Configuration 注解使启动类可以通过 ConfigurationClassPostProcessor 的校验并进行解析。在程序中执行 ConfigurationClassParserparse 方法进行解析。
  • 在调用的 ConfigurationClassParser 的** parse 方法中,会先调用内部重构的 parse** 方法,其中的 parse 会调用内部的 doProcessConfigurationClass 方法,在 doProcessConfigurationClass 中会调用 doProcessConfigurationClass。doProcessConfigurationClass 会分别对配置类的 @Component、@Import 等注解进行处理。对 @Import 的处理是在 processImports 方法中进行的。此时在 processImports 方法中,由于启动类上的 @Import(AutoConfigurationImportSelector.class) 注解,AutoConfigurationImportSelector 会被加载进来,且因为 AutoConfigurationImportSelector 继承了 DeferredImportSelector,被视为是一个延迟加载的类,会执行 this.deferredImportSelectorHandler.handle 方法将 AutoConfigurationImportSelector 添加到一个容器中以待后续调用。
  @Override
   void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
	processConfigBeanDefinitions(registry);
  }

  public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
    String[] candidateNames = registry.getBeanDefinitionNames();

    for (String beanName : candidateNames) {
      BeanDefinition beanDef = registry.getBeanDefinition(beanName);
      // checkConfigurationClassCandidate 用于检查一个 BeanDefinition 
      // 是否是一个配置类候选者(即是否具有 @Configuration、@Component、@Import 等注解)
      if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
        configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
      }
    }

    // 如果类被 @Order 修饰,进行排序
    configCandidates.sort((bd1, bd2) -> {
      int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
      int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
      return Integer.compare(i1, i2);
    });


    // 解析被 @Configuration 修饰的类,第一次进入此类只有被 @SpringBootApplication 修饰的主类
    ConfigurationClassParser parser = new ConfigurationClassParser(
        this.metadataReaderFactory, this.problemReporter, this.environment,
        this.resourceLoader, this.componentScanBeanNameGenerator, registry);

    Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
    Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
    do {
      StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");

      // 对配置类进行解析
      parser.parse(candidates);
      parser.validate();

      Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
      configClasses.removeAll(alreadyParsed);

      // 将解析后新增的配置类解析为 BeanDefinition 并添加到上下文中
      if (this.reader == null) {
        this.reader = new ConfigurationClassBeanDefinitionReader(
            registry, this.sourceExtractor, this.resourceLoader, this.environment,
            this.importBeanNameGenerator, parser.getImportRegistry());
      }
      this.reader.loadBeanDefinitions(configClasses);
      alreadyParsed.addAll(configClasses);
      candidates.clear();

      // 如果解析后新增了配置类,对新增的配置类进行解析
      if (registry.getBeanDefinitionCount() > candidateNames.length) {
        String[] newCandidateNames = registry.getBeanDefinitionNames();
        Set<String> oldCandidateNames = Set.of(candidateNames);
        Set<String> alreadyParsedClasses = new HashSet<>();
        for (ConfigurationClass configurationClass : alreadyParsed) {
          alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
        }
        for (String candidateName : newCandidateNames) {
          if (!oldCandidateNames.contains(candidateName)) {
            BeanDefinition bd = registry.getBeanDefinition(candidateName);
            if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
                !alreadyParsedClasses.contains(bd.getBeanClassName())) {
              candidates.add(new BeanDefinitionHolder(bd, candidateName));
            }
          }
        }
        candidateNames = newCandidateNames;
      }
    }
    while (!candidates.isEmpty());
  }
  public void parse(Set<BeanDefinitionHolder> configCandidates) {
    for (BeanDefinitionHolder holder : configCandidates) {
      BeanDefinition bd = holder.getBeanDefinition();
      try {
        if (bd instanceof AnnotatedBeanDefinition annotatedBeanDef) {
          parse(annotatedBeanDef.getMetadata(), holder.getBeanName());
        } else if (bd instanceof AbstractBeanDefinition abstractBeanDef
            && abstractBeanDef.hasBeanClass()) {
          parse(abstractBeanDef.getBeanClass(), holder.getBeanName());
        } else {
          parse(bd.getBeanClassName(), holder.getBeanName());
        }
      } catch (BeanDefinitionStoreException ex) {
        throw ex;
      } catch (Throwable ex) {
        throw new BeanDefinitionStoreException(
            "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
      }
    }

    // 这里处理延迟导入的配置类 (实现了 ImportSelector 和 DeferredImportSelector 接口的配置类)
    // 比如 AutoConfigurationImportSelector 类
    this.deferredImportSelectorHandler.process();
  }

  protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
    processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
  }

  protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter)
      throws IOException {
    // 递归处理配置类
    SourceClass sourceClass = null;
    try {
      sourceClass = asSourceClass(configClass, filter);
      do {
        sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
      } while (sourceClass != null);
    } catch (IOException ex) {
      throw new BeanDefinitionStoreException(
          "I/O failure while processing configuration class [" + sourceClass + "]", ex);
    }
  }

  @Nullable
  protected final SourceClass doProcessConfigurationClass(
      ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
      throws IOException {

    // 递归处理带有 @Component 注解的配置类
    if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
      // Recursively process any member (nested) classes first
      processMemberClasses(configClass, sourceClass, filter);
    }

    // 处理配置类上的 @PropertySource 注解 略
    // 处理配置类上的 @ComponentScan 注解,略

    // 处理配置类上的 @Import 注解
    processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

    // 处理配置类上的 @ImportResource 注解 略
    // 处理配置类上单个 @Bean 方法 略
    // 处理接口上的默认方法 略
    // 处理超类 略

    return null;
  }

  private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
      Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
      boolean checkForCircularImports) {

      try {
        for (SourceClass candidate : importCandidates) {
          if (candidate.isAssignable(ImportSelector.class)) {
            Class<?> candidateClass = candidate.loadClass();
            ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
                this.environment, this.resourceLoader, this.registry);

            // 如果实现了 DeferredImportSelector,添加该类到 deferredImportSelectorHandler 中
            // DeferredImportSelector 为延迟导入接口,该类在解析的最后会被执行
            if (selector instanceof DeferredImportSelector deferredImportSelector) {
              this.deferredImportSelectorHandler.handle(configClass, deferredImportSelector);
            }
            else {
              String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
              Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
              processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
            }
          }
        }
      }
      catch (BeanDefinitionStoreException ex) {
        throw ex;
      }
    }
  }


执行 AutoConfigurationImportSelector 的方法将自动化配置类加载进来
  • 在执行的 ConfigurationClassParser 的 parse 方法的最后,也就是将启动类加载完成后,会执行内部类 DeferredImportSelectorHandlerprocess 方法处理延迟导入的配置类。在 process 方法中,会新建一个内部类 DeferredImportSelectorHandler 并执行 processGroupImports 方法加载并处理从 AutoConfigurationImportSelector 导入的类,其中加载动作由内部类 DeferredImportSelectorGroupinggetAutoConfigurationEntry 完成。
  public void parse(Set<BeanDefinitionHolder> configCandidates) {
    for (BeanDefinitionHolder holder : configCandidates) {
    // 对配置类进行解析 略

    // 这里处理延迟导入的配置类 (实现了 ImportSelector 和 DeferredImportSelector 接口的配置类)
    // 比如 AutoConfigurationImportSelector 类
    this.deferredImportSelectorHandler.process();
  }

   private class DeferredImportSelectorHandler {

    @Nullable
    private List<DeferredImportSelectorHolder> deferredImportSelectors = new ArrayList<>();

    public void process() {
      List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
      if (deferredImports != null) {
        DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
        handler.processGroupImports();
      }
    }
  }

    private class DeferredImportSelectorGroupingHandler {

      private final Map<Object, DeferredImportSelectorGrouping> groupings = new LinkedHashMap<>();

      private final Map<AnnotationMetadata, ConfigurationClass> configurationClasses =
        new HashMap<>();

      public void processGroupImports() {
      for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
        Predicate<String> exclusionFilter = grouping.getCandidateFilter();
        grouping
            .getImports()
            .forEach(
                entry -> {
                  ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
                  try {
                    processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter), Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)), exclusionFilter, false);
                  } catch (BeanDefinitionStoreException ex) {
                    throw ex;
                  }
                });
      }
    }
  }

  private static class DeferredImportSelectorGrouping {

    private final DeferredImportSelector.Group group;

    private final List<DeferredImportSelectorHolder> deferredImports = new ArrayList<>();


    /**
     * Return the imports defined by the group.
     * @return each import with its associated configuration class
     */
    public Iterable<Group.Entry> getImports() {
      for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
        this.group.process(deferredImport.getConfigurationClass().getMetadata(),
            deferredImport.getImportSelector());
      }
      return this.group.selectImports();
    }
  }
调用 AutoConfigurationImportSelector 的方法获取自动化配置类
  • 在 DeferredImportSelectorGrouping 的 getImports 方法中,调用的 this.group.process 方法为 AutoConfigurationImportSelector.AutoConfigurationGroup 类的 process 方法,**process **会调用 getAutoConfigurationEntry 加载自动化配置类。在 getAutoConfigurationEntry 中,getCandidateConfigurations 方法负责从 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 中获取自动化配置类的全类名,getConfigurationClassFilter().filter(configurations) 执行的过滤器会过滤被 @Conditional 注解注解的配置类。
  protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    // 从 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 中获取自动化配置类的全类名
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    // 去除重复的类名
    configurations = removeDuplicates(configurations);
    // 获取明确排除的自动配置类,如在 @EnableAutoConfiguration 注解中使用 exclude 或者 excludeName 属性
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    checkExcludedClasses(configurations, exclusions);
    configurations.removeAll(exclusions);
    // 执行过滤器对自动化配置类进行进一步过滤,比如使用了 @ConditionalOnClass 的注解会在这里进行过滤
    configurations = getConfigurationClassFilter().filter(configurations);
    // 执行一些 AutoConfigurationImportListener 监听器的
    fireAutoConfigurationImportEvents(configurations, exclusions);
    return new AutoConfigurationEntry(configurations, exclusions);
  }

  protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    List<String> configurations = ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader())
        .getCandidates();
    return configurations;
  }


  private static class AutoConfigurationGroup implements DeferredImportSelector.Group, BeanClassLoaderAware, BeanFactoryAware,
          ResourceLoaderAware {

    @Override
    public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
      AutoConfigurationEntry autoConfigurationEntry =
          ((AutoConfigurationImportSelector) deferredImportSelector).getAutoConfigurationEntry(annotationMetadata);
      for (String importClassName : autoConfigurationEntry.getConfigurations()) {
        this.entries.putIfAbsent(importClassName, annotationMetadata);
      }
    }
  }
  private static final String LOCATION = "META-INF/spring/%s.imports";

  public static ImportCandidates load(Class<?> annotation, ClassLoader classLoader) {
    Assert.notNull(annotation, "'annotation' must not be null");
    ClassLoader classLoaderToUse = decideClassloader(classLoader);
    // 这里的 location = META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
    String location = String.format(LOCATION, annotation.getName());
    Enumeration<URL> urls = findUrlsInClasspath(classLoaderToUse, location);
    // importCandidates 保存读取到的自动化配置的全类名
    List<String> importCandidates = new ArrayList<>();
    while (urls.hasMoreElements()) {
      URL url = urls.nextElement();
      importCandidates.addAll(readCandidateConfigurations(url));
    }
    return new ImportCandidates(importCandidates);
  }

在 3.X 版本中已经不再支持将自动化配置类放置在 META-INF/spring.factories 文件中进行加载。取而代之的是,推荐使用 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件来注册自动化配置类。
如果你正在开发同时兼容 Spring Boot 2.x 和 3.x 的库,可以在两个文件中同时列出自动化配置类。Spring Boot 2.7 及以上版本会自动去重,从而避免重复加载。

  • 22
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值