Spring Boot3源码阅读系列(附录)-spring.factories与AutoConfiguration.imports的区别

378 阅读5分钟

引言

Spring Boot3与其之前的版本发生了较大的变化,引入了多项重要的更新和改进。比如

  1. 强制要求JDK17
  2. 进一步支持WebFlux
  3. 转变了自动配置的加载方式
  4. ......

本文来介绍自动配置的加载方式是如何发生变化的

Spring Boot < 2.7

在Spring Boot < 2.7的情况下,自动配置主要依赖于 META-INF/spring.factories 文件来完成。这个文件是Spring Boot自动配置机制的核心部分,用于注册各种自动配置类条件选择器以及其他重要的组件。以下是关于 spring.factoriesSpring Boot < 2.7中的使用和结构的详细介绍。

位置与结构

  • 文件位置: spring.factories 位于每个包下的 src/main/resources/META-INF/目录下
  • 文件结构: 该文件采用键值对的形式,其中 key 为接口或注解的全限定类名value为是实现该接口或使用该注解的类的全限定名。多个值可以用逗号或换行符分隔。

代码与示例

/**
 *
 * 返回应考虑的自动配置类名称。默认情况下,
 * 该方法将使用 SpringFactoriesLoader 读取并通过
 * getSpringFactoriesLoaderFactoryClass() 来加载候选类。
 *
 * Return the auto-configuration class names that should be considered. By default
 * this method will load candidates using {SpringFactoriesLoader} with
 * {#getSpringFactoriesLoaderFactoryClass()}.
 */
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
                    getBeanClassLoader());
    Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
                    + "are using a custom packaging, make sure that file is correct.");
    return configurations;
}

上方是Spring Boot 2.6.x加载自动配置类的部分源码,从中我们可以看到该版本使用SpringFactoriesLoader去加载对应的信息,该类由Spring包提供。

同时从Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");可以看出,我们加载的信息来自于META-INF/spring.factories文件。

示例

META-INF/spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 
com.example.AutoConfig1,\ 
com.example.AutoConfig2

以mybatis的spring.factories为例

image.png

相比于上一章,该版本mybatis使用了spring.factories来处理自动配置类

Spring Boot == 2.7

在Spring Boot == 2.7的情况下,Spring Boot社区已经决定淘汰spring.factories,但为了保证兼容性,并没有立即淘汰。该版本同时支持了spring.factories与*.imports

代码与示例

/**
 * 返回所需自动配置类的类名。默认情况下,该方法使用 ImportCandidates 
 * 读取并使用getSpringFactoriesLoaderFactoryClass()加载候选类
 * 为了向后兼容,它也会考虑使用 SpringFactoriesLoader。
 *
 * Return the auto-configuration class names that should be considered. By default
 * this method will load candidates using {ImportCandidates} with
 * {#getSpringFactoriesLoaderFactoryClass()}. For backward compatible reasons it
 * will also consider {SpringFactoriesLoader} with
 * {#getSpringFactoriesLoaderFactoryClass()}.
 */
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    List<String> configurations = new ArrayList<>(
                    SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()));
    ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);
    Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories nor 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;
}

上方是Spring Boot 2.7.x加载自动配置类的部分源码,从中我们可以看到该版本

  1. 使用SpringFactoriesLoader去加载对应的信息,该类由Spring包提供。

  2. 同时由使用ImportCandidates去加载另外的信息,该类由Spring Boot包提供

Assert.notEmpty(configurations, "......")可以看出,该版本加载的信息来自于META-INF/spring.factories文件与AutoConfiguration.imports文件。

以mybatis为例

image.png

相比于上一章,该版本mybatis同时使用了spring.factories与AutoConfiguration.imports来处理自动配置类

Spring Boot 3

在Spring Boot 3的情况下,Spring Boot社区在自动配置方面抛弃了META-INF/spring.factories。转而使用了AutoConfiguration.imports。以下是关于该文件的使用和结构的详细介绍。

位置与结构

  • 文件位置: AutoConfiguration.imports 位于每个包下的 src/main/resources/META-INF/spring/目录下
  • 文件结构: 该文件采用纯文本的形式,其中每行为一个对应实现类的全限定类名,多个实现类则书写为多行。

代码与示例

/**
 * 返回所需的自动配置类名,
 * 默认情况下,该方式使用ImportCandidates加载候选类
 *
 * Return the auto-configuration class names that should be considered. By default,
 * this method will load candidates using {ImportCandidates}.
 */
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;
}
/**
 * 从classpath加载候选
 * 这些候选存classpath下名为
 * META-INF/spring/full-qualified-annotation-name.imports 的文件中。
 * 支持使用 # 来书写注释
 *
 * Loads the names of import candidates from the classpath. The names of the import
 * candidates are stored in files named
 * {@code META-INF/spring/full-qualified-annotation-name.imports} on the classpath.
 * Every line contains the full qualified name of the candidate class. 
 * Comments are supported using the # character.
 */
public static ImportCandidates load(Class<?> annotation, ClassLoader classLoader) {
    Assert.notNull(annotation, "'annotation' must not be null");
    ClassLoader classLoaderToUse = decideClassloader(classLoader);
    String location = String.format(LOCATION, annotation.getName());
    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);
}

上方是Spring Boot 3.0.x加载自动配置类的部分源码,从中我们可以看到该版本仅使用ImportCandidates去加载另外的信息,该类由Spring Boot包提供 从Assert.notEmpty(configurations, "......")可以看出,该版本加载的信息来自于AutoConfiguration.imports文件。

示例

org.springframework.boot.autoconfigure.AutoConfiguration.imports

com.example.feature.AutoConfig1
com.example.feature.AutoConfig2

以mybatis为例

image.png

相比于上一章,该版本mybatis自动配置类的加载仅使用AutoConfiguration.imports

总结

从mybatis的示例可以看出,在Spring Boot3下,如果想要实现自动配置类的加载,只需要在resources/META-INF/spring文件夹下创建固定的文件名org.springframework.boot.autoconfigure.AutoConfiguration.imports,然后把原有的org.**.EnableAutoConfiguration=...的右侧的value按行加进去就好。

笔者认为,Spring Boot3做出此项改动主要是为了解耦

  1. 将专门用于自动配置的类从spring.factories中解放出来。专门通过AutoConfiguration.imports 文件管理自己的自动配置类。
  2. 同时,相比spring.factoriesAutoConfiguration.imports只列出所需的自动配置类,加快了文件的解析,从而提高了启动速度和性能。