前言
理解 Spring Boot 的启动流程和生命周期,对于掌握 Spring Boot 核心原理、解决启动问题、进行扩展开发都至关重要。本文将深入剖析 Spring Boot 的启动过程。
SpringApplication 核心类
SpringApplication 是 Spring Boot 启动的核心类:
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
run 方法是静态方法,实际创建了 SpringApplication 实例:
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
SpringApplication application = new SpringApplication(primarySources);
return application.run(args);
}
启动流程详解
1. SpringApplication 构造
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 推断应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 加载 ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 加载 ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 推断主应用类
this.mainApplicationClass = deduceMainApplicationClass();
}
2. run 方法执行
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
SpringApplicationRunException exception = null;
// 1. 获取监听器
Set<SpringApplicationRunListener> listeners = getSpringFactoriesInstances(
SpringApplicationRunListener.class,
new Class[] { SpringApplication.class, String[].class },
this, args
);
// 2. 准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, args);
// 3. 准备上下文
prepareContext(context, environment, listeners, args);
// 4. 刷新上下文
refreshContext(context);
// 5. 刷新后处理
afterRefresh(context, applicationArguments);
// 6. 发布启动完成事件
listeners.started(context);
// 7. 调用 Runner
callRunners(context, applicationArguments);
return context;
}
启动流程七步曲
Step 1: 获取监听器
加载 SpringApplicationRunListener,用于监听启动过程:
Set<SpringApplicationRunListener> listeners = getSpringFactoriesInstances(
SpringApplicationRunListener.class,
new Class[] { SpringApplication.class, String[].class },
this, args
);
主要监听器:
- EventPublishingRunListener - 发布启动事件
- LoggingApplicationListener - 初始化日志系统
- ConfigDataApplicationListener - 加载配置数据
Step 2: 准备环境
创建并配置 Environment:
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments
) {
// 创建 Environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置 PropertySources
configurePropertySources(environment, applicationArguments);
// 绑定环境到 Bean
bindToSpringApplication(environment);
// 发布环境准备事件
listeners.environmentPrepared(environment);
return environment;
}
Environment 包含:
- systemProperties - 系统属性
- systemEnvironment - 系统环境变量
- random - 随机值
- commandLineArgs - 命令行参数
- applicationConfig - 配置文件
- profile-specific - Profile 特定配置
Step 3: 准备上下文
创建 ApplicationContext 并准备:
private void prepareContext(
ConfigurableApplicationContext context,
ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments
) {
// 设置环境
context.setEnvironment(environment);
// 后处理上下文
postProcessApplicationContext(context);
// 应用初始化器
applyInitializers(context);
// 发布上下文准备事件
listeners.contextPrepared(context);
// 加载主配置类
load(context, sources.toArray(new Class[0]));
// 发布上下文加载事件
listeners.contextLoaded(context);
}
Step 4: 刷新上下文
核心步骤,刷新 ApplicationContext:
private void refreshContext(ConfigurableApplicationContext context) {
context.refresh();
// 注册关闭钩子
context.registerShutdownHook();
}
context.refresh() 是 Spring 容器刷新的核心方法,执行以下操作:
- prepareRefresh() - 准备刷新
- obtainFreshBeanFactory() - 获取 BeanFactory
- prepareBeanFactory() - 准备 BeanFactory
- postProcessBeanFactory() - 后处理 BeanFactory
- invokeBeanFactoryPostProcessors() - 调用 BeanFactory 后处理器
- registerBeanPostProcessors() - 注册 Bean 后处理器
- initMessageSource() - 初始化消息源
- initApplicationEventMulticaster() - 初始化事件广播器
- onRefresh() - 子类重写刷新
- registerListeners() - 注册监听器
- finishBeanFactoryInitialization() - 完成 BeanFactory 初始化
- finishRefresh() - 完成刷新
Step 5: 刷新后处理
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
// 空方法,子类可以重写
}
Step 6: 发布启动完成事件
listeners.started(context);
发布 ApplicationStartedEvent 事件。
Step 7: 调用 Runner
执行 CommandLineRunner 和 ApplicationRunner:
private void callRunners(ConfigurableApplicationContext context, ApplicationArguments args) {
// 调用 ApplicationRunner
context.getBeansOfType(ApplicationRunner.class)
.values()
.forEach(runner -> runner.run(args));
// 调用 CommandLineRunner
context.getBeansOfType(CommandLineRunner.class)
.values()
.forEach(runner -> runner.run(args));
}
生命周期事件
Spring Boot 启动过程发布以下事件:
1. ApplicationStartingEvent
最早的事件,在启动时立即发布:
@Component
public class StartingListener implements ApplicationListener<ApplicationStartingEvent> {
@Override
public void onApplicationEvent(ApplicationStartingEvent event) {
System.out.println("应用启动中...");
}
}
2. ApplicationEnvironmentPreparedEvent
环境准备完成后:
@Component
public class EnvironmentListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
ConfigurableEnvironment env = event.getEnvironment();
System.out.println("环境准备完成:" + env.getProperty("spring.application.name"));
}
}
3. ApplicationContextPreparedEvent
上下文准备完成后:
@Component
public class ContextPreparedListener implements ApplicationListener<ApplicationContextPreparedEvent> {
@Override
public void onApplicationEvent(ApplicationContextPreparedEvent event) {
System.out.println("上下文准备完成");
}
}
4. ApplicationStartedEvent
容器刷新完成后:
@Component
public class StartedListener implements ApplicationListener<ApplicationStartedEvent> {
@Override
public void onApplicationEvent(ApplicationStartedEvent event) {
System.out.println("应用已启动");
}
}
5. ApplicationReadyEvent
应用完全就绪,可以处理请求:
@Component
public class ReadyListener implements ApplicationListener<ApplicationReadyEvent> {
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
System.out.println("应用已就绪");
}
}
6. ApplicationFailedEvent
启动失败时:
@Component
public class FailedListener implements ApplicationListener<ApplicationFailedEvent> {
@Override
public void onApplicationEvent(ApplicationFailedEvent event) {
System.err.println("启动失败:" + event.getException().getMessage());
}
}
自定义启动流程
1. 使用 SpringApplicationRunListener
public class CustomRunListener implements SpringApplicationRunListener {
public CustomRunListener(SpringApplication application, String[] args) {
// 构造
}
@Override
public void starting() {
System.out.println("自定义:启动中");
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
System.out.println("自定义:环境准备完成");
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
System.out.println("自定义:上下文准备完成");
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
System.out.println("自定义:上下文加载完成");
}
@Override
public void started(ConfigurableApplicationContext context) {
System.out.println("自定义:启动完成");
}
@Override
public void ready(ConfigurableApplicationContext context, Duration timeTaken) {
System.out.println("自定义:就绪,耗时:" + timeTaken);
}
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
System.err.println("自定义:启动失败");
}
}
注册:
# META-INF/spring/org.springframework.boot.SpringApplicationRunListener.imports
com.example.demo.listener.CustomRunListener
2. 使用 ApplicationListener
@Component
public class CustomApplicationListener implements ApplicationListener<ApplicationEvent> {
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationReadyEvent) {
System.out.println("应用就绪,可以开始处理请求");
}
}
}
3. 使用 CommandLineRunner
@Component
public class StartupRunner implements CommandLineRunner {
private final Environment environment;
public StartupRunner(Environment environment) {
this.environment = environment;
}
@Override
public void run(String... args) {
String port = environment.getProperty("local.server.port");
System.out.println("应用启动完成,端口:" + port);
}
}
启动时间优化
1. 分析启动时间
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(DemoApplication.class);
app.addListeners(event -> {
if (event instanceof ApplicationReadyEvent) {
ApplicationReadyEvent readyEvent = (ApplicationReadyEvent) event;
System.out.println("启动耗时:" + readyEvent.getTimeTaken());
}
});
app.run(args);
}
}
2. 优化建议
- 减少@ComponentScan 范围 - 精确扫描包路径
- 延迟初始化 Bean -
spring.main.lazy-initialization=true - 减少自动配置 - 排除不需要的自动配置
- 使用 GraalVM Native - 编译为原生镜像
总结
Spring Boot 启动流程:
- 获取监听器 - SpringApplicationRunListener
- 准备环境 - Environment、PropertySources
- 准备上下文 - ApplicationContext、Initializer
- 刷新上下文 - 核心步骤,创建 Bean
- 刷新后处理 - 可扩展点
- 发布启动事件 - ApplicationStartedEvent
- 调用 Runner - CommandLineRunner、ApplicationRunner
生命周期事件:
- ApplicationStartingEvent
- ApplicationEnvironmentPreparedEvent
- ApplicationContextPreparedEvent
- ApplicationStartedEvent
- ApplicationReadyEvent
- ApplicationFailedEvent
理解启动流程和生命周期,能帮助你更好地掌握 Spring Boot 核心原理。