上下文创建与准备阶段
概述
环境准备完成后,SpringApplication.run() 进入上下文(ApplicationContext)创建与准备阶段。这个阶段负责创建 IoC 容器实例、配置容器的各项基础设施、注册启动类为 Bean、以及执行所有 ApplicationContextInitializer 回调。
本文逐个拆解其中的 8 个核心细节。
本文基于 Spring Boot 3.x 源码分析。
1. 整体流程
// SpringApplication.java
public ConfigurableApplicationContext run(String... args) {
// ...
// 环境准备已完成
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
// 打印 Banner
Banner printedBanner = printBanner(environment);
// ==== 上下文创建与准备阶段开始 ====
// 1. 创建 ApplicationContext 实例
context = createApplicationContext();
// 2. 设置 ApplicationStartup
context.setApplicationStartup(this.applicationStartup);
// 3. 准备上下文
prepareContext(bootstrapContext, context, environment, listeners,
applicationArguments, printedBanner);
// 4. 刷新上下文(核心——启动 IoC 容器)
refreshContext(context);
// 5. 刷新后回调
afterRefresh(context, applicationArguments);
// ...
}prepareContext() 内部包含多个子步骤:
// SpringApplication.java
private void prepareContext(DefaultBootstrapContext bootstrapContext,
ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
// ① 设置 Environment
context.setEnvironment(environment);
// ② 后处理 ApplicationContext(注册 BFPP、设置 ConversionService 等)
postProcessApplicationContext(context);
// ③ 执行所有 ApplicationContextInitializer
applyInitializers(context);
// ④ 发布 ApplicationContextInitializedEvent
listeners.contextPrepared(context);
// ⑤ 发布 ApplicationPreparedEvent
listeners.contextLoaded(context);
// ⑥ 加载启动类到 BeanFactory
load(context, sources.toArray(new Object[0]));
// ⑦ 注册 shutdown hook
registerShutdownHook();
}2. createApplicationContext() 的反射创建
2.1 源码
// SpringApplication.java
protected ConfigurableApplicationContext createApplicationContext() {
return this.applicationContextFactory.create(this.webApplicationType);
}applicationContextFactory 的默认值在 SpringApplication 构造函数中初始化:
// SpringApplication.java 构造函数
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// ...
this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
// ...
}2.2 ApplicationContextFactory.DEFAULT 的实现
// ApplicationContextFactory.java
ApplicationContextFactory DEFAULT = (webApplicationType) -> {
try {
switch (webApplicationType) {
case SERVLET:
return new AnnotationConfigServletWebServerApplicationContext();
case REACTIVE:
return new AnnotationConfigReactiveWebServerApplicationContext();
default:
return new AnnotationConfigApplicationContext();
}
} catch (Exception ex) {
throw new IllegalStateException("Unable to create a " + webApplicationType + " ApplicationContext, ", ex);
}
};2.3 三种 ApplicationContext 类型
WebApplicationType | ApplicationContext 实现 | 特点 |
|---|---|---|
SERVLET | AnnotationConfigServletWebServerApplicationContext | 内嵌 Tomcat/Jetty/Undertow,注册 ServletWebServerFactory |
REACTIVE | AnnotationConfigReactiveWebServerApplicationContext | 内嵌 Netty 的 Reactor 模式 |
NONE | AnnotationConfigApplicationContext | 纯 Spring 容器,无 Web 特性 |
2.4 反射创建 vs 直接 new
虽然 ApplicationContextFactory.DEFAULT 使用 new 关键字,但 createApplicationContext() 方法本身并未直接反射——反射发生在更早的 ApplicationContextFactory 选择阶段。如果需要完全自定义,可以实现 ApplicationContextFactory。
3. ApplicationContextFactory 的 SPI 扩展
3.1 SPI 加载
// SpringApplication.java 构造函数
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// ...
this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
// 从 spring.factories 加载自定义 ApplicationContextFactory
List<ApplicationContextFactory> factories = SpringFactoriesLoader
.loadFactories(ApplicationContextFactory.class, getClassLoader());
// 如果有 SPI 实现且不是 DEFAULT,则替换
if (!factories.isEmpty()) {
this.applicationContextFactory = factories.get(0);
}
// ...
}3.2 自定义 ApplicationContextFactory
// 自定义工厂
public class MyApplicationContextFactory implements ApplicationContextFactory {
@Override
public ConfigurableApplicationContext create(WebApplicationType type) {
if (type == WebApplicationType.SERVLET) {
return new CustomServletWebServerApplicationContext();
}
return ApplicationContextFactory.DEFAULT.create(type);
}
}注册方式:
# META-INF/spring.factories
org.springframework.boot.ApplicationContextFactory=\
com.example.MyApplicationContextFactory3.3 与 SpringApplication.setApplicationContextFactory() 的优先级
1. SpringApplication.setApplicationContextFactory(factory) ← 最高优先级(编程式覆盖)
2. spring.factories 中的 ApplicationContextFactory ← 中间优先级(SPI)
3. ApplicationContextFactory.DEFAULT ← 兜底4. context.setApplicationStartup() 设置
4.1 应用启动监控
// SpringApplication.java
context.setApplicationStartup(this.applicationStartup);applicationStartup 的默认值:
// SpringApplication 构造函数
this.applicationStartup = ApplicationStartup.DEFAULT;4.2 FlightRecorderApplicationStartup vs ApplicationStartup.DEFAULT
| 实现 | 类型 | 用途 | 性能影响 |
|---|---|---|---|
ApplicationStartup.DEFAULT | 空实现 | 不记录任何启动步骤信息 | 无开销 |
FlightRecorderApplicationStartup | JDK Flight Recorder 集成 | 记录 Bean 创建、事件发布等步骤到 JFR | 低开销 |
BufferingApplicationStartup | 内存缓冲 | 在内存中记录启动步骤,供 Actuator /startup 端点查询 | 有内存开销 |
4.3 如何启用 FlightRecorder
SpringApplication app = new SpringApplication(MyApp.class);
app.setApplicationStartup(new FlightRecorderApplicationStartup());
app.run(args);等价配置:
spring:
application:
startup: flight-recorder4.4 setApplicationStartup() 的内部效果
// AbstractApplicationContext.java
@Override
public void setApplicationStartup(ApplicationStartup applicationStartup) {
Assert.notNull(applicationStartup, "ApplicationStartup must not be null");
this.applicationStartup = applicationStartup;
// 将 ApplicationStartup 传播到 BeanFactory
if (this.beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) this.beanFactory)
.setApplicationStartup(applicationStartup);
}
}设置后,BeanFactory 在创建每个 Bean 时会调用 ApplicationStartup.start() 记录步骤信息,最终通过 FlightRecorder 持久化到 JFR 事件流中。
5. postProcessApplicationContext() 的 4 件事情
5.1 源码
// SpringApplication.java
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
// 1. 注册 BeanFactoryPostProcessor
if (this.beanFactoryPostProcessors != null) {
for (BeanFactoryPostProcessor bfpp : this.beanFactoryPostProcessors) {
context.addBeanFactoryPostProcessor(bfpp);
}
}
// 2. 设置 ConversionService
if (this.conversionService != null) {
context.getBeanFactory().setConversionService(this.conversionService);
}
// 3. 注册 AOT 生成的 Initializer(GraalVM native image 场景)
if (this.aotGeneratedInitializerClassName != null) {
context.addBeanFactoryPostProcessor(
new AotGeneratedInitializerPostProcessor(
this.aotGeneratedInitializerClassName));
}
// 4. 设置 BeanFactory 的 ClassLoader(用于后续 Bean 类加载)
context.setClassLoader(this.resourceLoader != null
? this.resourceLoader.getClassLoader()
: ClassUtils.getDefaultClassLoader());
}5.2 四件事拆解
| 序号 | 操作 | 作用 | 被谁填充 |
|---|---|---|---|
| ① | addBeanFactoryPostProcessor() | 注册编程式添加的 BFPP | SpringApplication.addBeanFactoryPostProcessor() |
| ② | setConversionService() | 设置类型转换服务 | SpringApplication.setConversionService() |
| ③ | addAotGeneratedInitializer() | AOT 编译阶段的初始化器 | GraalVM 编译时自动生成 |
| ④ | setClassLoader() | 设置 BeanFactory 的 ClassLoader | SpringApplication.resourceLoader 或默认 ClassLoader |
5.3 注册 BFPP 的传递链
SpringApplication.addBeanFactoryPostProcessor(bfpp)
→ this.beanFactoryPostProcessors.add(bfpp) // 存入构造函数传入列表
→ postProcessApplicationContext() // prepareContext 中调用
→ context.addBeanFactoryPostProcessor(bfpp) // 注册到 ApplicationContext
→ AbstractApplicationContext.beanFactoryPostProcessors
→ 在 refresh() 的 invokeBeanFactoryPostProcessors() 阶段执行6. applyInitializers() 遍历顺序
6.1 执行流程
// SpringApplication.java
protected void applyInitializers(ConfigurableApplicationContext context) {
// 遍历已排序的 initializers 列表
for (ApplicationContextInitializer initializer : getInitializers()) {
// 解析 initializer 的泛型参数,检查 context 类型是否匹配
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
initializer.getClass(), ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context,
"Unable to call initializer...");
// 按 @Order 顺序调用 initialize()
initializer.initialize(context);
}
}6.2 执行顺序保证
初始化器列表在 SpringApplication 构造函数中已被排序:
// SpringApplication.java 构造函数
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// setInitializers 内部:
public void setInitializers(
Collection<? extends ApplicationContextInitializer<?>> initializers) {
this.initializers = new ArrayList<>(initializers);
AnnotationAwareOrderComparator.sort(this.initializers); // ← 排序
}6.3 内置 Initializer
Spring Boot 内置了以下 ApplicationContextInitializer:
| Initializer | @Order | 作用 |
|---|---|---|
SharedMetadataReaderFactoryContextInitializer | Ordered.HIGHEST_PRECEDENCE | 创建 CachingMetadataReaderFactory 共享实例,避免 ASM 读取重复创建 |
ContextIdApplicationContextInitializer | Ordered.HIGHEST_PRECEDENCE + 5 | 设置 spring.application.name 到 ApplicationContext ID |
ConfigurationWarningsApplicationContextInitializer | Ordered.HIGHEST_PRECEDENCE + 10 | 报告常见的配置错误 |
EnableLoggingConfigurationApplicationContextInitializer | Ordered.HIGHEST_PRECEDENCE + 15 | 启用日志配置扫描 |
ServerPortInfoApplicationContextInitializer | Ordered.HIGHEST_PRECEDENCE + 20 | 将 WebServer 端口写入 Environment |
DelegatingApplicationContextInitializer | - | 代理执行 context.initializer.classes 属性指定的自定义 Initializer |
AutoConfigurationReportLoggingInitializer | - | 记录自动配置报告到日志 |
6.4 GenericTypeResolver 的类型匹配
applyInitializers() 中的 GenericTypeResolver 检查确保了 Initializer 的类型安全:
// 如果 Initializer 声明了泛型类型,确保传入的 context 是兼容类型
// 例如: class MyInitializer implements ApplicationContextInitializer<AnnotationConfigServletWebServerApplicationContext>
// 当传入的 context 为 AnnotationConfigApplicationContext 时会抛出异常7. listeners.contextPrepared() / contextLoaded() 事件
7.1 contextPrepared() — 发布 ApplicationContextInitializedEvent
// EventPublishingRunListener.java
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
// 在 Initializer 执行完毕后发布
this.multicaster.multicastEvent(
new ApplicationContextInitializedEvent(this.application, this.args, context));
}此时:ApplicationContextInitializer 已全部执行完毕,但 refresh() 尚未调用,容器中还没有 Bean。
7.2 contextLoaded() — 发布 ApplicationPreparedEvent
// EventPublishingRunListener.java
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
// 在 load() 方法执行后发布
this.multicaster.multicastEvent(
new ApplicationPreparedEvent(this.application, this.args, context));
}7.3 事件时间轴
prepareContext()
│
├─ context.setEnvironment(env)
├─ postProcessApplicationContext()
├─ applyInitializers() ← Initializer 按序执行
│
├─ listeners.contextPrepared() ← 发布 ApplicationContextInitializedEvent
│ 表明 Initializer 已全部执行
├─ load() ← 注册启动类
│
├─ listeners.contextLoaded() ← 发布 ApplicationPreparedEvent
│ 表明启动类已注册
└─ registerShutdownHook()7.4 ApplicationPreparedEvent 的用途
ApplicationPreparedEvent 是 refresh() 前的最后一个事件。此时 BeanFactory 已有启动类的 BeanDefinition,但尚未开始实例化。典型用途:
LoggingApplicationListener:在此事件中初始化日志系统(logback-spring.xml读取配置)- 自定义监听器:在容器刷新前对 BeanDefinition 做最后一次修改
8. load() 方法处理 @SpringBootApplication 类
8.1 load() 实现
// SpringApplication.java
protected void load(ApplicationContext context, Object... sources) {
if (sources != null && sources.length > 0) {
// 创建 BeanDefinitionLoader
BeanDefinitionLoader loader = createBeanDefinitionLoader(
getBeanDefinitionRegistry(context), sources);
// 设置 BeanNameGenerator
if (this.beanNameGenerator != null) {
loader.setBeanNameGenerator(this.beanNameGenerator);
}
// 设置 ResourceLoader
if (this.resourceLoader != null) {
loader.setResourceLoader(this.resourceLoader);
}
// 设置 Environment
if (this.environment != null) {
loader.setEnvironment(this.environment);
}
// 执行加载
loader.load();
}
}8.2 createBeanDefinitionLoader()
// SpringApplication.java
protected BeanDefinitionLoader createBeanDefinitionLoader(
BeanDefinitionRegistry registry, Object... sources) {
return new BeanDefinitionLoader(registry, sources);
}8.3 BeanDefinitionLoader.load() 的流程
// BeanDefinitionLoader.java
public int load() {
int count = 0;
for (Object source : this.sources) {
if (source instanceof Class) {
// 启动类是 Class 类型 —— 最常见路径
count += load((Class<?>) source);
} else if (source instanceof Resource) {
count += load((Resource) source);
} else if (source instanceof Package) {
count += load((Package) source);
} else if (source instanceof CharSequence) {
count += load((CharSequence) source);
}
}
return count;
}8.4 加载 Class 类型源
// BeanDefinitionLoader.java
private int load(Class<?> source) {
// 1. 检查是否有 @Component 注解(包括 @Configuration、@SpringBootApplication)
if (isComponent(source)) {
// 2. 使用 AnnotatedBeanDefinitionReader 注册为 BeanDefinition
this.annotatedReader.register(source);
return 1;
}
// 3. 如果 Groovy 在 classpath 上,尝试 GroovyBeanDefinitionReader
if (this.groovyReader != null && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
// ...
}
return 0;
}
private boolean isComponent(Class<?> type) {
// 判断是否有 @Component 注解(@SpringBootApplication 有 @Component 元注解)
return MergedAnnotations.from(type, SearchStrategy.TYPE_HIERARCHY)
.isPresent(Component.class);
}8.5 从启动类到 BeanDefinition 的完整链
SpringApplication 构造函数
└─ primarySources = [Application.class] ← 存储启动类
SpringApplication.run()
└─ prepareContext()
└─ load(context, sources.toArray(...))
└─ BeanDefinitionLoader.load()
└─ load(Application.class)
└─ isComponent(Application.class) = true
└─ AnnotatedBeanDefinitionReader.register(Application.class)
└─ 解析 @SpringBootApplication 组合注解
├─ @Configuration → 生成 FullConfigurationClass BeanDefinition
├─ @EnableAutoConfiguration → 注册 AutoConfigurationImportSelector
└─ @ComponentScan → 生成 ComponentScan BeanDefinition
└─ 注册到 BeanDefinitionRegistry9. registerShutdownHook() 的注册时机
9.1 源码
// SpringApplication.java
private void registerShutdownHook() {
if (this.registerShutdownHook) {
try {
// 获取 ApplicationContext 并注册 shutdown hook
ApplicationContext context = this.context;
if (context instanceof ConfigurableApplicationContext) {
((ConfigurableApplicationContext) context)
.registerShutdownHook();
}
} catch (Exception ex) {
// 忽略异常
}
}
}9.2 注册时机
registerShutdownHook() 在 prepareContext() 末尾调用,在 refresh() 之前,但实际 JVM Shutdown Hook 的注册不依赖于 refresh() 的状态——refresh() 是后续在 run() 中调用的。
// SpringApplication.run()
public ConfigurableApplicationContext run(String... args) {
// ...
// prepareContext 中调用 registerShutdownHook() ← 注册钩子
prepareContext(bootstrapContext, context, environment,
listeners, applicationArguments, printedBanner);
// refreshContext() ← 容器刷新
refreshContext(context);
// ...
}9.3 内部控制
// AbstractApplicationContext.java
@Override
public void registerShutdownHook() {
// 如果还没有 shutdown hook,注册一个新的
if (this.shutdownHook == null) {
this.shutdownHook = new Thread(this::doClose, "SpringContextShutdownHook");
Runtime.getRuntime().addShutdownHook(this.shutdownHook);
}
}doClose() 的执行顺序:
JVM 关闭信号
│
└─ Runtime.getRuntime().addShutdownHook(SpringContextShutdownHook)
└─ AbstractApplicationContext.doClose()
├─ 1. 发布 ContextClosedEvent
├─ 2. 销毁所有单例 Bean(@PreDestroy、DisposableBean)
├─ 3. 关闭 BeanFactory
├─ 4. 关闭 ServletWebServer(停止 Tomcat)
└─ 5. 关闭 LiveBeansView9.4 spring.main.register-shutdown-hook 配置
spring:
main:
register-shutdown-hook: false # 默认为 true当设置为 false 时,registerShutdownHook() 内部的 if (this.registerShutdownHook) 直接跳过,适用于自定义关闭逻辑的场景。
总结
| 细节点 | 核心要点 |
|---|---|
① createApplicationContext() 反射创建 | ApplicationContextFactory.DEFAULT 根据 WebApplicationType 返回三种不同的 ApplicationContext 实现 |
② ApplicationContextFactory SPI | DEFAULT 枚举 > spring.factories 自定义 > 编程式 setApplicationContextFactory() |
③ setApplicationStartup() | FlightRecorderApplicationStartup 记录到 JFR;BufferingApplicationStartup 供 Actuator 查询;DEFAULT 无操作 |
④ postProcessApplicationContext() 四件事 | 注册 BFPP → 设置 ConversionService → AOT Initializer → 设置 ClassLoader |
⑤ applyInitializers() 遍历顺序 | 按 @Order 排序后的链逐个调用,GenericTypeResolver 做类型安全匹配 |
⑥ listeners.contextPreloaded() | 发布 ApplicationPreparedEvent,LoggingApplicationListener 在此初始化日志系统 |
⑦ load() 注册启动类 | AnnotatedBeanDefinitionReader.register() 解析 @SpringBootApplication 组合注解,注册为 BeanDefinition |
⑧ registerShutdownHook() | 在 prepareContext() 末尾注册 JVM 关闭钩子,doClose() 顺序发布事件→销毁 Bean→停止容器 |