SpringBoot3自动装配 源码阅读 笔记

291 阅读7分钟

1、准备工作

启动一个简单的springboot应用

@SpringBootApplication
@RestController
public class Main {
    public static void main(String[] args) {
       SpringApplication.run(Main.class);
    }

    @GetMapping("/a")
    public String a(){
       return "111";
    }
}

2、分析

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
       @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration

@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage

SpringBootApplication组合注解,主要有SpringBootConfiguration、ComponentScan和EnableAutoConfiguration三个注解组成,ComponentScan我们都知道是往容器中添加指定的对象,SpringBootConfiguratio注解主要是标识的类可作为一个配置类,进一步分析EnableAutoConfiguration

进一步分析EnableAutoConfiguration又是由@AutoConfigurationPackage和@Import(AutoConfigurationImportSelector.class)组合,@Import注解中的AutoConfigurationImportSelector类实现了DeferredImportSelector接口,Spring会将ImportSelector接口中selectImports方法返回的迭代器遍历添加到spring容器中

image.png

AutoConfigurationImportSelector类
@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
    Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
          () -> String.format("Only %s implementations are supported, got %s",
                AutoConfigurationImportSelector.class.getSimpleName(),
                deferredImportSelector.getClass().getName()));
    // 获取自动装配的类
    AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
       .getAutoConfigurationEntry(annotationMetadata);
       //将自动装配的类添加到autoConfigurationEntries属性
    this.autoConfigurationEntries.add(autoConfigurationEntry);
    for (String importClassName : autoConfigurationEntry.getConfigurations()) {
       this.entries.putIfAbsent(importClassName, annotationMetadata);
    }
}

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
       return EMPTY_ENTRY;
    }
    // 获取@EnableAutoConfiguration的属性exclude,excludeName 自动装配要排除的类
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    // 获取所有需要自动装配的类
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    // 去重
    configurations = removeDuplicates(configurations);
    // 获取@EnableAutoConfiguration属性要排除的类
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    // 检查要排除的类,如果自动装配的类中没有要排除的类会抛异常
    checkExcludedClasses(configurations, exclusions);
    // 从自动装配的类中移除要排除的类
    configurations.removeAll(exclusions);
    // 对自动装配的类进行过滤,需要实现AutoConfigurationImportFilter接口,并且把该过滤类写进spring.factories文件中
    configurations = getConfigurationClassFilter().filter(configurations);
    // 发布自动装配事件,需要实现AutoConfigurationImportListener接口,并且把该监听类写进spring.factories文件中
    fireAutoConfigurationImportEvents(configurations, exclusions);
    return new AutoConfigurationEntry(configurations, exclusions);
}

// 进入getCandidateConfigurations方法
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    // 获取要自动装配的类
    List<String> configurations = ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader())
       .getCandidates();
    // 如果为空,抛出异常
    Assert.notEmpty(configurations,
          "No auto configuration classes found in "
                + "META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you "
                + "are using a custom packaging, make sure that file is correct.");
    return configurations;
}

public static ImportCandidates load(Class<?> annotation, ClassLoader classLoader) {
    Assert.notNull(annotation, "'annotation' must not be null");
    // 获取类加载器
    ClassLoader classLoaderToUse = decideClassloader(classLoader);
    // LOCATION = "META-INF/spring/%s.imports" 拿AutoConfiguration注解全路径名进行拼接,拼接之后location=META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
    String location = String.format(LOCATION, annotation.getName());
    // 找location下资源
    Enumeration<URL> urls = findUrlsInClasspath(classLoaderToUse, location);
    List<String> importCandidates = new ArrayList<>();
    while (urls.hasMoreElements()) {
       URL url = urls.nextElement();
       // 读取每一行的内容作为要自动装配的类
       importCandidates.addAll(readCandidateConfigurations(url));
    }
    return new ImportCandidates(importCandidates);
}

与springboot2的自动装配不同的是,springboot3的自动装配读取的要自动装配的类的文件是META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports,自动装配过程中也有使用spring.factories文件,获取一些自动装配过程中过滤、事件监听器等信息(本人没读过springboot2的自动装配源码,从网上了解到spirngboot2的自动装配的类是在spring.factories文件中)

那么,springboot在启动过程中是如何走到这一步的呢

1、SpringApplication.run方法

public ConfigurableApplicationContext run(String... args) {
    if (this.registerShutdownHook) {
       SpringApplication.shutdownHook.enableShutdowHookAddition();
    }
    long startTime = System.nanoTime();
    DefaultBootstrapContext bootstrapContext = createBootstrapContext();
    ConfigurableApplicationContext context = null;
    configureHeadlessProperty();
    // 获取SpringApplicationRunListeners
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // 发布事件 ApplicationStartingEvent 应用开启
    listeners.starting(bootstrapContext, this.mainApplicationClass);
    try {
       ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
       // 准备环境
       ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
       // 输出banner
       Banner printedBanner = printBanner(environment);
       // 创建spring容器
       context = createApplicationContext();
       context.setApplicationStartup(this.applicationStartup);
       // 准备上下文
       prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
       // 启动spring容器(tomcat启动),就是AbstractApplicationContext的refresh方法了
       refreshContext(context);
       afterRefresh(context, applicationArguments);
       Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
       if (this.logStartupInfo) {
          new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
       }
       listeners.started(context, timeTakenToStartup);
       callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
       if (ex instanceof AbandonedRunException) {
          throw ex;
       }
       handleRunFailure(context, ex, listeners);
       throw new IllegalStateException(ex);
    }
    try {
       if (context.isRunning()) {
          Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
          listeners.ready(context, timeTakenToReady);
       }
    }
    catch (Throwable ex) {
       if (ex instanceof AbandonedRunException) {
          throw ex;
       }
       handleRunFailure(context, ex, null);
       throw new IllegalStateException(ex);
    }
    return context;
}

2、ConfigurationClassPostProcessor

ConfigurationClassPostProcessor类实现了BeanDefinitionRegistryPostProcessor接口,可以在spring容器中添加bean定义
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    int registryId = System.identityHashCode(registry);
    if (this.registriesPostProcessed.contains(registryId)) {
       throw new IllegalStateException(
             "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
    }
    if (this.factoriesPostProcessed.contains(registryId)) {
       throw new IllegalStateException(
             "postProcessBeanFactory already called on this post-processor against " + registry);
    }
    this.registriesPostProcessed.add(registryId);
    // 自动装配
    processConfigBeanDefinitions(registry);
}

3、processConfigBeanDefinitions方法片段

// 创建了ConfigurationClassParser对象parser
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的属性
    parser.parse(candidates);
    parser.validate();

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

    // Read the model and create bean definitions based on its content
    if (this.reader == null) {
       this.reader = new ConfigurationClassBeanDefinitionReader(
             registry, this.sourceExtractor, this.resourceLoader, this.environment,
             this.importBeanNameGenerator, parser.getImportRegistry());
    }
    // 在spring容器中注册parser.getConfigurationClasses()方法返回的bean定义
    this.reader.loadBeanDefinitions(configClasses);

至此,已发现自动装配的类被封装在ConfigurationClassParser的configurationClasses属性中,继续寻找该属性在何时被赋值

4、ConfigurationClassParser

parser.parse(candidates)方法
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);
       }
    }

    this.deferredImportSelectorHandler.process();
}
    
DeferredImportSelectorGroupingHandler类processGroupImports方法
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;
          }
          catch (Throwable ex) {
             throw new BeanDefinitionStoreException(
                   "Failed to process import candidates for configuration class [" +
                         configurationClass.getMetadata().getClassName() + "]", ex);
          }
       });
    }
}

// 返回DeferredImportSelector接口getImports返回的迭代器
public Iterable<Group.Entry> getImports() {
    for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
// 此处调用的就是AutoConfigurationImportSelector类的process方法
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
             deferredImport.getImportSelector());
    }
    return this.group.selectImports();
}

@Override
public Iterable<Entry> selectImports() {
    if (this.autoConfigurationEntries.isEmpty()) {
       return Collections.emptyList();
    }
    // 要排除的类
    Set<String> allExclusions = this.autoConfigurationEntries.stream()
       .map(AutoConfigurationEntry::getExclusions)
       .flatMap(Collection::stream)
       .collect(Collectors.toSet());
    // autoConfigurationEntries属性
    Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
       .map(AutoConfigurationEntry::getConfigurations)
       .flatMap(Collection::stream)
       .collect(Collectors.toCollection(LinkedHashSet::new));
    processedConfigurations.removeAll(allExclusions);
    
    return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
       .map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
       .toList();
}

// 遍历所有要自动装配的类,执行该方法
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
       Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
       boolean checkForCircularImports) {

    if (importCandidates.isEmpty()) {
       return;
    }

    if (checkForCircularImports && isChainedImportOnStack(configClass)) {
       this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
    }
    else {
       this.importStack.push(configClass);
       try {
          for (SourceClass candidate : importCandidates) {
          // 是否实现了ImportSelector接口
             if (candidate.isAssignable(ImportSelector.class)) {
                // Candidate class is an ImportSelector -> delegate to it to determine imports
                Class<?> candidateClass = candidate.loadClass();
                // 反射创建该实例
                ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
                      this.environment, this.resourceLoader, this.registry);
                Predicate<String> selectorFilter = selector.getExclusionFilter();
                if (selectorFilter != null) {
                   exclusionFilter = exclusionFilter.or(selectorFilter);
                }
                // 是否是DeferredImportSelector
                if (selector instanceof DeferredImportSelector deferredImportSelector) {
                   this.deferredImportSelectorHandler.handle(configClass, deferredImportSelector);
                }
                else {
                   // 调用selectImports方法,获取到导入的类
                   String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                   Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
                   // 循环调用processImports本方法
                   processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
                }
             }
             else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
                // Candidate class is an ImportBeanDefinitionRegistrar ->
                // delegate to it to register additional bean definitions
                Class<?> candidateClass = candidate.loadClass();
                ImportBeanDefinitionRegistrar registrar =
                      ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
                            this.environment, this.resourceLoader, this.registry);
                configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
             }
             else {
                // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
                // process it as an @Configuration class
                this.importStack.registerImport(
                      currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
                processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
             }
          }
       }
       catch (BeanDefinitionStoreException ex) {
          throw ex;
       }
       catch (Throwable ex) {
          throw new BeanDefinitionStoreException(
                "Failed to process import candidates for configuration class [" +
                configClass.getMetadata().getClassName() + "]: " + ex.getMessage(), ex);
       }
       finally {
          this.importStack.pop();
       }
    }
}


protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
    // 是否不满足自动装配条件 主要判断@Conditional
    if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
       return;
    }

    ConfigurationClass existingClass = this.configurationClasses.get(configClass);
    if (existingClass != null) {
       if (configClass.isImported()) {
          if (existingClass.isImported()) {
             existingClass.mergeImportedBy(configClass);
          }
          // Otherwise ignore new imported config class; existing non-imported class overrides it.
          return;
       }
       else {
          // Explicit bean definition found, probably replacing an import.
          // Let's remove the old one and go with the new one.
          this.configurationClasses.remove(configClass);
          this.knownSuperclasses.values().removeIf(configClass::equals);
       }
    }

    // Recursively process the configuration class and its superclass hierarchy.
    SourceClass sourceClass = asSourceClass(configClass, filter);
    do {
       sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
    }
    while (sourceClass != null);
    // configurationClasses属性中添加要自动装配的类 
    this.configurationClasses.put(configClass, configClass);
}

springboot自动装配先使用@Import注解导入的AutoConfigurationImportSelector类,利用AutoConfigurationImportSelector实现了DeferredImportSelector接口,进而要将自动装配的类bean定义注册到spring容器中

最后,为什么springBoot3的自动装配的类文件不再使用spirng.factories,使用META-INF/spring/%s.imports替代?下面是某AI工具的回复,也挺有道理,欢迎大家讨论指正

Spring Boot 3 放弃使用 spring.factories 而使用 org.springframework.boot.autoconfigure.AutoConfiguration.imports 进行自动装配的类文件,主要有以下几个原因:

  1. 简化配置:使用 spring.factories 需要维护一个中央的静态工厂文件,这在大型项目中可能会变得非常复杂。每个需要配置的类都需要在工厂文件中进行显式声明,这无疑增加了大量的维护成本。而使用 AutoConfiguration.imports 则可以将配置分散到各个独立的类中,使得项目结构更加清晰,也更容易进行版本控制。
  2. 提高可读性和可维护性:使用 Java 注解(如 @Configuration 和 @Bean)来定义 Bean,相比在 spring.factories 文件中使用静态工厂方法,使得代码更加直观和易于理解。这也使得开发者能够更清晰地看到哪些类被配置为 Bean,以及它们是如何被配置的。
  3. 错误检查和一致性:新的自动配置机制能够在启动时检查配置的一致性,例如检查是否存在两个相同类型的 Bean。这种检查机制在 spring.factories 中是不存在的。
  4. 更好的支持 Java 配置:Spring Boot 的新机制更好地支持使用 Java 配置(而不是 XML 或注解)。这种配置方式使得开发者能够更清晰地看到哪些类被配置为 Bean,以及它们是如何被配置的。

总的来说,Spring Boot 3 放弃使用 spring.factories 而使用 org.springframework.boot.autoconfigure.AutoConfiguration.imports 是为了提高项目的可维护性、可读性和一致性,以及更好地支持 Java 配置。