初始化器与监听器的加载排序
概述
Spring Boot 启动时,SpringApplication 的构造函数会做一系列关键的初始化工作——其中最重要的是通过 SPI 机制加载 初始化器(ApplicationContextInitializer)和 监听器(ApplicationListener),并将它们按优先级排序。
这些初始化器和监听器在后续的 run() 流程中被回调,是 Spring Boot 扩展体系的基础设施之一。本文深入拆解它们的加载、排序、去重和合并细节。
本文基于 Spring Boot 3.x 源码分析。
1. SPI 加载全流程:getSpringFactoriesInstances()
SpringApplication 构造函数中通过统一的方法加载初始化器和监听器:
// SpringApplication.java
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
...
// 加载 ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// 加载 ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(
ApplicationListener.class));
...
}1.1 getSpringFactoriesInstances() 全流程
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// 1. 通过 SpringFactoriesLoader 加载全限定类名
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 2. 创建实例(按加载顺序)
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
// 3. 排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}第 1 步:SpringFactoriesLoader.loadFactoryNames()
// SpringFactoriesLoader.java
public static List<String> loadFactoryNames(Class<?> factoryType,
@Nullable ClassLoader classLoader) {
// 从缓存中获取或加载所有 spring.factories
Map<String, List<String>> result = loadSpringFactories(classLoader);
// 按 factoryType 取出对应的实现类列表
return result.getOrDefault(factoryType.getName(), Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(
ClassLoader classLoader) {
// 缓存检查
Map<String, List<String>> result = cache.get(classLoader);
if (result != null) return result;
result = new HashMap<>();
try {
// 扫描所有 jar 中的 META-INF/spring.factories
Enumeration<URL> urls = classLoader.getResources(
"META-INF/spring.factories");
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
Properties properties = PropertiesLoaderUtils
.loadProperties(new UrlResource(url));
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
String[] factoryImplNames = StringUtils
.commaDelimitedListToStringArray((String) entry.getValue());
for (String name : factoryImplNames) {
result.computeIfAbsent(factoryTypeName,
k -> new ArrayList<>()).add(name.trim());
}
}
}
cache.put(classLoader, Collections.unmodifiableMap(result));
} catch (IOException ex) { /* ... */ }
return result;
}第 2 步:createSpringFactoriesInstances() 反射创建
private <T> List<T> createSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
Set<String> names) {
List<T> instances = new ArrayList<>(names.size());
for (String name : names) {
try {
// 反射加载类
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
// 反射构造
Constructor<?> constructor = instanceClass
.getDeclaredConstructor(parameterTypes);
T instance = (T) BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
} catch (Throwable ex) {
throw new IllegalArgumentException(
"Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}第 3 步:AnnotationAwareOrderComparator.sort()
// AnnotationAwareOrderComparator 继承 OrderComparator
public class AnnotationAwareOrderComparator extends OrderComparator {
@Override
protected int getOrder(Object obj) {
// 先查 @Order 注解,再查 Ordered 接口
if (obj instanceof Ordered) return ((Ordered) obj).getOrder();
if (obj != null) {
Class<?> clazz = (obj instanceof Class ? (Class<?>) obj
: obj.getClass());
// 查找 @Order 注解
MergedAnnotations annotations = MergedAnnotations.from(clazz);
Integer order = annotations.get(Order.class
).getValue(MergedAnnotation.VALUE, Integer.class);
if (order != null) return order;
}
return Ordered.LOWEST_PRECEDENCE;
}
}排序规则:
@Order(0)>@Order(1)> ... >@Order(Integer.MAX_VALUE)Ordered接口的getOrder()返回值优先级相同- 无注解/接口的类默认
LOWEST_PRECEDENCE(Integer.MAX_VALUE)
1.2 完整执行流程图
getSpringFactoriesInstances(ApplicationContextInitializer.class)
│
├─ 1. loadFactoryNames(type, classLoader)
│ └─ loadSpringFactories(classLoader)
│ ├─ 检查缓存 (ConcurrentReferenceHashMap)
│ ├─ classLoader.getResources("META-INF/spring.factories")
│ │ ├─ spring-boot.jar!/META-INF/spring.factories
│ │ ├─ spring-boot-autoconfigure.jar!/META-INF/spring.factories
│ │ └─ 其他 jar 中的 spring.factories
│ ├─ 按 factoryType 分组合并
│ └─ 存入缓存
│
├─ 2. createSpringFactoriesInstances()
│ ├─ ClassUtils.forName() 加载类
│ ├─ BeanUtils.instantiateClass() 反射构造
│ └─ 存入 List<T>
│
└─ 3. AnnotationAwareOrderComparator.sort()
└─ 按 @Order / Ordered 排序2. ApplicationContextInitializer 的排序与去重
2.1 排序机制
ApplicationContextInitializer 的排序完全依赖 AnnotationAwareOrderComparator,支持两种方式:
// 方式一:@Order 注解
@Order(1)
public class MyInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext context) { ... }
}
// 方式二:实现 Ordered 接口
public class MyOrderedInitializer
implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
@Override
public int getOrder() { return 2; }
@Override
public void initialize(ConfigurableApplicationContext context) { ... }
}| 排序方式 | 优先级 | 示例 |
|---|---|---|
@Order(0) | 最高 | 框架内置的 SharedMetadataReaderFactoryContextInitializer |
Ordered.getOrder() | 次高 | 由 Ordered 接口返回的数值决定 |
| 无注解 | 最低 | 默认 Integer.MAX_VALUE |
2.2 setInitializers() vs addInitializers() 的去重差异
// SpringApplication.java
// 方式一:setInitializers —— 完全替换
public void setInitializers(
Collection<? extends ApplicationContextInitializer<?>> initializers) {
this.initializers = new ArrayList<>(initializers);
// 立即排序
AnnotationAwareOrderComparator.sort(this.initializers);
}
// 方式二:addInitializers —— 追加
public void addInitializers(
ApplicationContextInitializer<?>... initializers) {
this.initializers.addAll(Arrays.asList(initializers));
// 追加后排序
AnnotationAwareOrderComparator.sort(this.initializers);
}去重逻辑:
| 方法 | 去重行为 | 是否重新排序 |
|---|---|---|
setInitializers() | 完全替换,清空后重新设置 | ✅ |
addInitializers() | 追加,ArrayList.addAll() 不自动去重 | ✅ |
getSpringFactoriesInstances() | 使用 LinkedHashSet 去重(SPI 加载阶段) | ❌(排序在外部) |
SPI 加载时通过 LinkedHashSet 去重:
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));3. ApplicationListener 的去重与排序
3.1 加载与去重
ApplicationListener 的 SPI 加载与 ApplicationContextInitializer 走同一套 getSpringFactoriesInstances() 流程,同样使用 LinkedHashSet 去重。
// SpringApplication.java 构造函数
setListeners((Collection) getSpringFactoriesInstances(
ApplicationListener.class));3.2 排序细节
与 ApplicationContextInitializer 不同的是,ApplicationListener 除了 @Order 和 Ordered 外,框架还定义了优先级:
| 监听器 | 排序方式 | 典型顺序 |
|---|---|---|
LoggingApplicationListener | Ordered.HIGHEST_PRECEDENCE + 10 | 最先执行 |
BackgroundPreinitializer | Ordered.HIGHEST_PRECEDENCE + 20 | 次先 |
ClearCachesApplicationListener | Ordered.HIGHEST_PRECEDENCE + 30 | 第三 |
FileEncodingApplicationListener | Ordered.HIGHEST_PRECEDENCE + 40 | 第四 |
用户自定义 @Order | 由用户指定 | 可调 |
3.3 自定义监听器的两种注册方式
// 方式一:SPI 注册(自动)
// META-INF/spring.factories
// org.springframework.context.ApplicationListener=\
// com.example.MyApplicationListener
// 方式二:编程式注册(手动)
SpringApplication app = new SpringApplication(MyApp.class);
app.addListeners(new MyApplicationListener());4. BootstrapRegistryInitializer 的独立 SPI
BootstrapRegistryInitializer 是 Spring Boot 3.x 引入的新 SPI,与 ApplicationContextInitializer 独立加载。
4.1 独立 SPI 文件
// SpringApplication.java
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
...
// 从独立的 imports 文件加载
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
...
}在 Spring Boot 3.x 中,BootstrapRegistryInitializer 从独立的文件加载:
# META-INF/spring/org.springframework.boot.BootstrapRegistryInitializer.imports
com.example.MyBootstrapRegistryInitializer这是新的 ImportCandidates 机制(而不是旧的 spring.factories):
// SpringApplication.java
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, ...) {
...
// Spring Boot 3.x 使用 ImportCandidates 加载 BootstrapRegistryInitializer
if (type == BootstrapRegistryInitializer.class) {
List<String> names = ImportCandidates.load(type, classLoader)
.getCandidates();
return createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, new LinkedHashSet<>(names));
}
// 其余类型继续走 spring.factories
return ...;
}4.2 执行时机差异
| 初始化器类型 | 执行时机 | 可访问资源 |
|---|---|---|
BootstrapRegistryInitializer | BootstrapContext 创建后立即执行(run() 最早期) | 仅有 BootstrapRegistry,无 Environment、无 BeanFactory |
ApplicationContextInitializer | ApplicationContext 创建后、refresh() 前执行 | 可访问 ConfigurableApplicationContext |
5. SpringApplicationRunListener 的加载与事件桥接
5.1 加载机制
SpringApplicationRunListener 是特殊的运行监听器,在 run() 方法中按需加载:
// SpringApplication.java
private SpringApplicationRunListeners getRunListeners(String[] args) {
// 从 spring.factories 加载
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args),
this.applicationStartup);
}注意构造参数类型不同:SpringApplicationRunListener 的构造函数需要 (SpringApplication, String[])。
5.2 EventPublishingRunListener 的实现
写入唯一的实现类是 EventPublishingRunListener,它负责将 SpringApplicationRunListener 的生命周期事件桥接到 Spring 的 ApplicationListener:
// EventPublishingRunListener.java
public class EventPublishingRunListener
implements SpringApplicationRunListener {
private final SpringApplication application;
private final String[] args;
// 事件发布器 —— 用于桥接到 ApplicationListener
private final SimpleApplicationEventMulticaster multicaster;
public EventPublishingRunListener(SpringApplication application,
String[] args) {
this.application = application;
this.args = args;
this.multicaster = new SimpleApplicationEventMulticaster();
// 将所有 ApplicationListener 注册到 multicaster
for (ApplicationListener<?> listener : application.getListeners()) {
this.multicaster.addApplicationListener(listener);
}
}
@Override
public void starting(ConfigurableBootstrapContext bootstrapContext) {
// 发布 ApplicationStartingEvent → 触发所有匹配的 ApplicationListener
multicaster.multicastEvent(
new ApplicationStartingEvent(bootstrapContext, this.application, this.args));
}
@Override
public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,
ConfigurableEnvironment environment) {
multicaster.multicastEvent(
new ApplicationEnvironmentPreparedEvent(bootstrapContext,
this.application, this.args, environment));
}
// ... 其他回调方法类似:contextPrepared()、started()、ready()、failed()
}5.3 事件桥接关系
SpringApplicationRunListener 回调 Spring 事件 ApplicationListener
─────────────────────────────── ────────── ─────────────────
starting() ─────→ ApplicationStartingEvent ─────→ LoggingApplicationListener
environmentPrepared() ─────→ ApplicationEnvironmentPreparedEvent ─→ EnvironmentPostProcessorApplicationListener
contextPrepared() ─────→ ApplicationContextInitializedEvent ─→ BackgroundPreinitializer
contextLoaded() ─────→ ApplicationPreparedEvent
started() ─────→ ApplicationStartedEvent ─────→ ClearCachesApplicationListener
ready() ─────→ ApplicationReadyEvent
failed() ─────→ ApplicationFailedEvent6. 多个 jar 中同名 imports 文件的合并逻辑
6.1 spring.factories 的合并
当多个 jar 包中都包含 META-INF/spring.factories 时,PropertiesLoaderUtils.loadProperties() 会逐文件加载:
Enumeration<URL> urls = classLoader.getResources("META-INF/spring.factories");
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
Properties properties = PropertiesLoaderUtils
.loadProperties(new UrlResource(url));
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
// 使用 computeIfAbsent + ArrayList.add() 累加
result.computeIfAbsent(factoryTypeName,
k -> new ArrayList<>()).addAll(
Arrays.asList(StringUtils.commaDelimitedListToStringArray(
(String) entry.getValue())));
}
}合并策略:computeIfAbsent + ArrayList.addAll(),相同 key 的实现类合并到同一个 List 中。
6.2 ImportCandidates 的合并(3.x 新机制)
Spring Boot 3.x 中,BootstrapRegistryInitializer.imports 等文件使用 ImportCandidates 加载:
// ImportCandidates.java
private ImportCandidates(Enumeration<URL> urls) throws IOException {
List<String> candidates = new ArrayList<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(url.openStream(),
StandardCharsets.UTF_8))) {
// 逐行读取,每个 jar 的 imports 文件累加到同一个列表
reader.lines()
.map(String::trim)
.filter(line -> !line.isEmpty() && !line.startsWith("#"))
.forEach(candidates::add);
}
}
this.candidates = Collections.unmodifiableList(candidates);
}多个 jar 中同名 imports 文件的内容按 URL 遍历顺序合并到同一个 List<String> 中。
6.3 全量合并示意图
多个 jar 的 META-INF/spring.factories
│
├─ spring-boot.jar
│ └─ key = ApplicationContextInitializer → [A, B]
│
├─ spring-boot-autoconfigure.jar
│ └─ key = ApplicationContextInitializer → [C, D]
│
└─ my-custom.jar
└─ key = ApplicationContextInitializer → [E]
合并后: result["ApplicationContextInitializer"] = [A, B, C, D, E]7. 初始化器链的调用时机
所有 ApplicationContextInitializer 在 run() 的 prepareContext() 阶段被调用:
// SpringApplication.java
private void prepareContext(DefaultBootstrapContext bootstrapContext,
ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
// ... 设置环境、注册启动参数等 ...
// 执行所有 ApplicationContextInitializer(按已排序的顺序)
applyInitializers(context);
// ... 后续步骤 ...
}
protected void applyInitializers(ConfigurableApplicationContext context) {
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
initializer.getClass(), ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "...");
initializer.initialize(context); // ← 按 @Order 顺序调用
}
}调用链保证:已排序的 ApplicationContextInitializer 链,按 @Order 值从小到大依次执行。
8. 编程式注册推荐用法
// 创建 SpringApplication 实例后手动添加
SpringApplication app = new SpringApplication(MyApplication.class);
// 添加自定义初始化器
app.addInitializers(new MyInitializer());
app.addInitializers(new AnotherInitializer());
// 添加自定义监听器
app.addListeners(new MyListener());
// 运行
app.run(args);这种方式比修改 spring.factories 更直接,适合测试和自定义启动场景。
总结
| 细节点 | 核心要点 |
|---|---|
① getSpringFactoriesInstances() 流程 | loadFactoryNames() → createSpringFactoriesInstances() → AnnotationAwareOrderComparator.sort() |
② Initializer 排序机制 | @Order / Ordered → AnnotationAwareOrderComparator 排序 |
③ Listener 去重与排序 | LinkedHashSet 去重 + @Order 排序 |
④ BootstrapRegistryInitializer 独立加载 | 从 META-INF/spring/...imports 加载,比 ApplicationContextInitializer 更早执行 |
⑤ SpringApplicationRunListener 加载与桥接 | EventPublishingRunListener 将生命周期事件桥接到 ApplicationListener |
⑥ EventPublishingRunListener | SimpleApplicationEventMulticaster 持有所有 ApplicationListener |
| ⑦ 多个 jar 同名文件合并 | computeIfAbsent + ArrayList.addAll() 或 ImportCandidates 逐行累加 |
| ⑧ 去重时机 | SPI 加载时 LinkedHashSet 去重;setInitializers() 完全替换;addInitializers() 追加不自动去重 |