SpringBoot启动流程

大多数情况下,我们都可以在 main() 方法中调用 SpringApplication.run() 引导启动一个SpringBoot应用。

1
2
3
4
5
6
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(MyApplication.class, args);
}
}

默认情况下,SpringApplication 将执行以下步骤来引导应用程序:

  • 根据 classpath 创建一个合适的 ApplicationContext 实例
  • 注册一个 CommandLinePropertySource 以将命令行参数公开为 Spring 属性
  • 刷新应用程序上下文,加载所有单例 Bean
  • 触发所有 CommandLineRunner

当然,真实的应用场景会比默认情况复杂的多,下面我们通过分析 SpringApplication.run() 的实现,以对整个流程有一个更加详细的了解。

1
2
3
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
return new SpringApplication(sources).run(args);
}

SpringApplication.run() 里面,启动流程主要分为如下两个步骤:

  1. 创建 SpringApplication 对象,并完成初始化操作
  2. 调用 run() 方法启动应用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
private void initialize(Object[] sources) {
if (sources != null && sources.length > 0) {
// 添加Bean的配置源,后面由 BeanDefinitionLoader 进行加载
this.sources.addAll(Arrays.asList(sources));
}
// 检测是否为Web应用环境
this.webEnvironment = deduceWebEnvironment();
// 加载 spring.factories 中配置的 ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 加载 spring.factories 中配置的 ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 通过线程栈信息查询 main 方法所在类
this.mainApplicationClass = deduceMainApplicationClass();
}

正式启动前的初始化流程主要包括如下几个步骤:

  1. 添加特定的 Bean 配置源,在启动过程中由 BeanDefinitionLoader 加载并注册到 BeanFactory,在创建 SpringApplication 的过程中进行指定;
  2. 检测是否为Web环境,主要依据 javax.servlet.Servletorg.springframework.web.context.ConfigurableWebApplicationContext 是否可以成功被类加载器加载;
  3. 利用SPI机制从 spring.factories 加载 ApplicationContextInitializerApplicationListener 实现类,由 SpringFactoriesLoader.loadFactoryNames() 进行配置读取,配置的 key 为接口或抽象类的完全限定名;
  4. 查找main方法所在类,通过模拟异常栈信息进行方法名匹配;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public ConfigurableApplicationContext run(String... args) {
// StopWatch 用来统计启动流程耗时
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
// 配置 awt 的 headless 属性
configureHeadlessProperty();
// 加载 SpringApplicationRunListeners
SpringApplicationRunListeners listeners = getRunListeners(args);
// 通过调用 SpringApplicationRunListener 的 starting 方法
// 给所有 ApplicationListener 广播 ApplicationStartedEvent 事件
listeners.starting();
try {
// 封装命令行参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 创建并准备 Environment
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// 打印Banner图
Banner printedBanner = printBanner(environment);
// 创建应用上下文
context = createApplicationContext();
// 创建失败分析器
analyzers = new FailureAnalyzers(context);
// 准备应用上下文
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 刷新应用上下文
refreshContext(context);
// 触发 ApplicationRunner 与 CommandLineRunner
afterRefresh(context, applicationArguments);
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}

忽略掉 StopWatch,正式的启动流程主要分为如下几个步骤:

  1. 配置 awt 的 headless 属性,默认设置为 true,因为SpringBoot应用一般不需要使用awt的重量级组件,如展示窗口或者对话框、接收鼠标与键盘的输入等;
  2. 初始化 SpringApplicationRunListeners,即一组 SpringApplicationRunListener 的集合,通过SPI加载 spring.factories 配置完成初始化,默认配置中只有一个实现类 EventPublishingRunListener,该实现类会通过多播器 SimpleApplicationEventMulticaster 将事件封装为 SpringApplicationEvent 系列事件对象,并将其广播给所有已注册的 ApplicationListener 监听器。
  3. 通过调用 EventPublishingRunListener.starting() 方法给 ApplicationListener 广播 ApplicationStartedEvent 事件
  4. 创建 Environment 并配置 PropertySourceprofile,调用 EventPublishingRunListener.environmentPrepared() 方法广播 ApplicationEnvironmentPreparedEvent 事件
  5. 创建 ApplicationContext,web应用环境创建 AnnotationConfigEmbeddedWebApplicationContext,其它环境创建 AnnotationConfigApplicationContext
  6. 创建 FailureAnalyzers,实为使用SPI初始化的一组 FailureAnalyzer 对象,主要用于分析系统启动失败的各种原因,并将其转化为可读性更强的文字描述,主要的实现类有 NoSuchBeanDefinitionFailureAnalyzerNoUniqueBeanDefinitionFailureAnalyzer 等;
  7. 准备 ApplicationContext,调用所有的 ApplicationContextInitializer,调用所有 EventPublishingRunListener.contextPrepared() 方法,但并未广播任何事件,调用 EventPublishingRunListener.contextLoaded() 方法广播 ApplicationPreparedEvent 事件;
  8. 刷新 ApplicationContext,完成Bean的加载及增强,并注册 shutdownHook 用于监听关机消息,以便应用可以优雅停服;
  9. 调用注册好的 ApplicationRunnerCommandLineRunner
  10. 调用 EventPublishingRunListener.finished() 广播 ApplicationFailedEventApplicationReadyEvent 事件;

整个流程的文字描述可能很复杂,如果将事件广播的逻辑独立出来,你会发现整个流程就只有文章开头的那四个步骤。

为了更好的对流程进行说明,我们使用一个简单的流程图对整个流程进行拆解总结如下:

SpringBoot启动流程

最后,补充一下 SpringApplicationRunListener 方法与广播事件对象的映射关系:

方法名 事件对象
starting ApplicationStartedEvent
environmentPrepared ApplicationEnvironmentPreparedEvent
contextPrepared
contextLoaded ApplicationPreparedEvent
finished ApplicationFailedEvent/ApplicationReadyEvent

参考资料:

qchery wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!