引言
在日常开发中,我们经常使用
Spring Boot
来快速构建 Web 应用,但你是否深入思考过它的启动流程是如何工作的?当你在main
方法中调用SpringApplication.run()
时,Spring Boot 究竟经历了哪些关键步骤,最终成功启动了应用?本篇文章将深入 Spring Boot 源码,剖析其启动流程,帮助你理解它的内部原理,让你在排查问题、优化启动速度时更加得心应手。
1. 启动入口
Spring Boot 应用通常以 SpringApplication.run()
作为入口:
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
该方法会初始化 SpringApplication
并调用 run()
方法启动 Spring Boot 应用。
2. SpringApplication.run()
启动流程
public ConfigurableApplicationContext run(String... args) {
long startTime = System.nanoTime();
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this);
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
listeners.started(context, timeTakenToStartup);
callRunners(context, applicationArguments);
} catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
} catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
Spring Boot 的启动过程可以拆解为以下阶段:
1. 创建 SpringApplication
对象
public SpringApplication(Class<?>... primarySources) {
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
- 通过
SpringFactoriesLoader
加载ApplicationContextInitializer
和ApplicationListener
- 确定
WebApplicationType
2. 监听器通知 starting
事件
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this);
Spring Boot 触发 starting
事件,通知监听器应用正在启动。
3. 准备 Environment
(环境变量)
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
- 加载
application.properties
或application.yml
- 通知监听器
environmentPrepared
事件
4. 打印 Spring Boot Banner
Banner printedBanner = printBanner(environment);
- 可通过
spring.banner.location
自定义 Banner。
5. 创建 ApplicationContext
context = createApplicationContext();
- Web 应用创建
AnnotationConfigServletWebServerApplicationContext
- 普通应用创建
AnnotationConfigApplicationContext
6. 准备 Spring 容器
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
- 调用
ApplicationContextInitializer
- 通知监听器
contextPrepared
7. 加载 Bean 定义
load(context, sources.toArray(new Object[0]));
- 通过
AnnotatedBeanDefinitionReader
和ClassPathBeanDefinitionScanner
扫描 Bean
8. 刷新 ApplicationContext
refreshContext(context);
最终调用 AbstractApplicationContext.refresh()
,执行 BeanFactory 初始化流程。
9. 触发 started
事件
listeners.started(context, timeTakenToStartup);
并执行 ApplicationRunner
和 CommandLineRunner
:
callRunners(context, applicationArguments);
10. 通知 running
事件
listeners.running(context);
至此,Spring Boot 启动完成!
总结:Spring Boot 启动核心流程
- 创建
SpringApplication
,加载监听器、环境类型。 - 触发
starting
事件,通知监听器应用正在启动。 - 准备
Environment
,解析application.properties
配置文件。 - 打印 Banner。
- 创建
ApplicationContext
并注册环境。 - 调用
ApplicationContextInitializer
进行上下文初始化。 - 加载 Bean,注册
BeanDefinition
。 - 刷新
ApplicationContext
,完成 Bean 实例化、依赖注入。 - 触发
started
事件,执行ApplicationRunner
和CommandLineRunner
。 - 通知
running
事件,Spring Boot 启动完成。
整个启动流程以 SpringApplication.run()
作为起点,结合监听器、环境、应用上下文、BeanFactory 等组件,实现了 Spring Boot 的自动化启动。