大多数情况下,我们都可以在 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的模式选择依据。