引言
Spring Boot3与其之前的版本发生了较大的变化,引入了多项重要的更新和改进。比如
- 强制要求JDK17
- 进一步支持WebFlux
- 转变了自动配置的加载方式
- ......
本文来介绍自动配置的加载方式是如何发生变化的
Spring Boot < 2.7
在Spring Boot < 2.7的情况下,自动配置主要依赖于 META-INF/spring.factories 文件来完成。这个文件是Spring Boot自动配置机制的核心部分,用于注册各种自动配置类、条件选择器以及其他重要的组件。以下是关于 spring.factories 在Spring 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为例
相比于上一章,该版本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加载自动配置类的部分源码,从中我们可以看到该版本
-
使用
SpringFactoriesLoader去加载对应的信息,该类由Spring包提供。 -
同时由使用
ImportCandidates去加载另外的信息,该类由Spring Boot包提供
从Assert.notEmpty(configurations, "......")可以看出,该版本加载的信息来自于META-INF/spring.factories文件与AutoConfiguration.imports文件。
以mybatis为例
相比于上一章,该版本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为例
相比于上一章,该版本mybatis自动配置类的加载仅使用AutoConfiguration.imports
总结
从mybatis的示例可以看出,在Spring Boot3下,如果想要实现自动配置类的加载,只需要在resources/META-INF/spring文件夹下创建固定的文件名org.springframework.boot.autoconfigure.AutoConfiguration.imports,然后把原有的org.**.EnableAutoConfiguration=...的右侧的value按行加进去就好。
笔者认为,Spring Boot3做出此项改动主要是为了解耦。
- 将专门用于
自动配置的类从spring.factories中解放出来。专门通过AutoConfiguration.imports文件管理自己的自动配置类。 - 同时,相比
spring.factories,AutoConfiguration.imports只列出所需的自动配置类,加快了文件的解析,从而提高了启动速度和性能。