大多数情况下,我们都可以在 main() 方法中调用 SpringApplication.run() 引导启动一个SpringBoot应用。
1 |
|
默认情况下,SpringApplication 将执行以下步骤来引导应用程序:
- 根据 classpath 创建一个合适的
ApplicationContext实例 - 注册一个
CommandLinePropertySource以将命令行参数公开为 Spring 属性 - 刷新应用程序上下文,加载所有单例 Bean
- 触发所有
CommandLineRunner
当然,真实的应用场景会比默认情况复杂的多,下面我们通过分析 SpringApplication.run() 的实现,以对整个流程有一个更加详细的了解。
1 | public static ConfigurableApplicationContext run(Object[] sources, String[] args) { |
在 SpringApplication.run() 里面,启动流程主要分为如下两个步骤:
- 创建
SpringApplication对象,并完成初始化操作 - 调用
run()方法启动应用
1 | private void initialize(Object[] sources) { |
正式启动前的初始化流程主要包括如下几个步骤:
- 添加特定的 Bean 配置源,在启动过程中由
BeanDefinitionLoader加载并注册到BeanFactory,在创建SpringApplication的过程中进行指定; - 检测是否为Web环境,主要依据
javax.servlet.Servlet及org.springframework.web.context.ConfigurableWebApplicationContext是否可以成功被类加载器加载; - 利用SPI机制从
spring.factories加载ApplicationContextInitializer及ApplicationListener实现类,由SpringFactoriesLoader.loadFactoryNames()进行配置读取,配置的 key 为接口或抽象类的完全限定名; - 查找main方法所在类,通过模拟异常栈信息进行方法名匹配;
1 | public ConfigurableApplicationContext run(String... args) { |
忽略掉 StopWatch,正式的启动流程主要分为如下几个步骤:
- 配置 awt 的 headless 属性,默认设置为 true,因为SpringBoot应用一般不需要使用awt的重量级组件,如展示窗口或者对话框、接收鼠标与键盘的输入等;
- 初始化
SpringApplicationRunListeners,即一组SpringApplicationRunListener的集合,通过SPI加载spring.factories配置完成初始化,默认配置中只有一个实现类EventPublishingRunListener,该实现类会通过多播器SimpleApplicationEventMulticaster将事件封装为SpringApplicationEvent系列事件对象,并将其广播给所有已注册的ApplicationListener监听器。 - 通过调用
EventPublishingRunListener.starting()方法给ApplicationListener广播ApplicationStartedEvent事件 - 创建
Environment并配置PropertySource及profile,调用EventPublishingRunListener.environmentPrepared()方法广播ApplicationEnvironmentPreparedEvent事件 - 创建
ApplicationContext,web应用环境创建AnnotationConfigEmbeddedWebApplicationContext,其它环境创建AnnotationConfigApplicationContext - 创建
FailureAnalyzers,实为使用SPI初始化的一组FailureAnalyzer对象,主要用于分析系统启动失败的各种原因,并将其转化为可读性更强的文字描述,主要的实现类有NoSuchBeanDefinitionFailureAnalyzer,NoUniqueBeanDefinitionFailureAnalyzer等; - 准备
ApplicationContext,调用所有的ApplicationContextInitializer,调用所有EventPublishingRunListener.contextPrepared()方法,但并未广播任何事件,调用EventPublishingRunListener.contextLoaded()方法广播ApplicationPreparedEvent事件; - 刷新
ApplicationContext,完成Bean的加载及增强,并注册 shutdownHook 用于监听关机消息,以便应用可以优雅停服; - 调用注册好的
ApplicationRunner及CommandLineRunner; - 调用
EventPublishingRunListener.finished()广播ApplicationFailedEvent或ApplicationReadyEvent事件;
整个流程的文字描述可能很复杂,如果将事件广播的逻辑独立出来,你会发现整个流程就只有文章开头的那四个步骤。
为了更好的对流程进行说明,我们使用一个简单的流程图对整个流程进行拆解总结如下:

最后,补充一下 SpringApplicationRunListener 方法与广播事件对象的映射关系:
| 方法名 | 事件对象 |
|---|---|
| starting | ApplicationStartedEvent |
| environmentPrepared | ApplicationEnvironmentPreparedEvent |
| contextPrepared | 无 |
| contextLoaded | ApplicationPreparedEvent |
| finished | ApplicationFailedEvent/ApplicationReadyEvent |
参考资料:
- Selecting in AWT mode:介绍AWT的模式选择依据。