SpringBoot启动流程浅析

因为springboot的特性,使得它被广大开发者所采用,因此在网上出现了一系列关于springboot应用教程的文章,但只是应用远远还不够,因为在日常应用过程中总会遇到一些奇奇怪怪的异常问题或者需要一些自定义的特殊操作时,如果对springboot启动流程不够熟悉,往往会不知道从何入手。所以本文会根据源码大致讲下springboot(2.1.4)的启动流程

入口类,启动类

下面是SpringBoot启动类。众所周知启动应用需要一个入口类,也就是main方法,main方法内通过SpringApplication类调用run方法启动应用,所以接下来会根据run方法里的源码分析。当然该启动类需要注解@SpringBootApplication说明下该类是启动类。

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

根据run方法内,可以发现启动流程需要先创建SpringApplication实例,接着在通过实例调用run方法

1
2
3
4
5
SpringBootApplication : 1258
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
String[] args) {
return new SpringApplication(primarySources).run(args);
}

创建SpringApplication实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 判断是否为web环境
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 会将META-INF文件下的spring.factory中key为ApplicationContextInitializer的类实例化
// 设置到应用中Initializ属性中
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// 会将META-INF文件下的spring.factory中key为ApplicationListener的类实例化
// 设置为应用的监听器
// 注意这里可以设置应用监听事件(ApplicationEvent)有应用程序启动事件(ApplicationStartingEvent),环境准备事件(ApplicationEnvironmentPreparedEvent),准备事件(ApplicationPreparedEvent)等。
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 创建主方法即启动类的实例
this.mainApplicationClass = deduceMainApplicationClass();
}

这一步可以概括为配置source配置是否为web环境创建应用构造器创建应用监听器。这里会从META—INF文件夹下的spring.factories读取某些类,是属于SpringBoot一大特性自动配置的内容,这个在另外一篇文章会讲自动配置相关的知识。另外setListeners()方法会涉及到到应用监听事件Application Events and Listeners的内容,有兴趣可以看下官方文档介绍

run(),启动应用

当创建完SpringApplication实例后,接下来就是调用run方法启动。

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
public ConfigurableApplicationContext run(String... args) {
// 创建观察器,记录应用启动时间
StopWatch stopWatch = new StopWatch();
// 记录执行开始时间
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
// 获取spring.factories文件中key为SpringApplicationRunListener的类并实例化
// 例如配置环境,应用上下文监听
SpringApplicationRunListeners listeners = getRunListeners(args);
// 启动监听
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
// 配置一些环境信息。比如profile,命令行参数
// 广播出ApplicationEnvironmentPreparedEvent事件给相应的监听器执行
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);、
configureIgnoreBeanInfo(environment);
// 打印自定义banner图,就是启动应时控制台打印的那一大串图案
Banner printedBanner = printBanner(environment);
// 创建上下文信息
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 将environment环境, listeners监听器, applicationArguments应用参数,
// printedBanner信息与上下文Context相关联
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
// 刷新上下文,对自动配置依赖包进行实例化,例如spring-boot-starter-*,(redis,mybaits,mq)
// BeanFactoryPostProcessor接口的执行、BeanPostProcessor接口的执行
// 国际化信息的初始化、bean的实例化
refreshContext(context);
afterRefresh(context, applicationArguments);
// 记录执行结束时间并打印输出
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
// 执行ApplicationRunner接口的实现类和CommandLineRunner接口的实现类
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}

try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}

总结下run方法大致的如下:

  1. 启动监听器:配置环境、上下文applicationContext
  2. 配置环境模块,创建配置环境,加载资源文件
  3. banner彩蛋
  4. 创建上下文并prepareContext配置应用上下文,将监听器,环境配置,应用参数,banner装配进context中
  5. refreshContext更新应用上下文,创建应用beanFactory,再对所有非懒加载的bean进行实例化和初始化
  6. 应用启动,command-line runnerapplication runner

其中refreshContext更新应用上下文方法里包含了对bean的实例化,也是需要掌握的,这点掌握了可以更加清楚的知道bean是如何被实例化以及属性依赖如何被注入的,有兴趣的读者可以阅读另外一片文章spring IOC 容器初始化bean的流程。到此整个应用已经启动完成了,建议读者可以根据debug一步一步的跟踪流程,写一个例子来验证下启动流程分析。例如跟启动流程有关联的Application Events and Listeners

附录

Application Events and Listeners

SpringBoot定义了一些应用事件监听器,可供开发者在启动流程环节加上自定义的操作。下面解释下这6个监听事件发生在启动流程哪个节点中。其中前三个需要在META-INF文件夹的spring.factories添加key为ApplicationListener的实现类,下面举了个例子,MyListener是实现ApplicationListener的子类。

1
org.springframework.context.ApplicationListener=com.example.project.MyListener
  1. An ApplicationStartingEvent : 会在除了注册应用监听器和构造函数之外的其他进程启动之前触发事件
  2. An ApplicationEnvironmentPreparedEvent : 在配置环境完成后且在还没创建应用上下文的时候触发事件
  3. An ApplicationPreparedEvent : 会在refreshContext刷新应用上下文之前触发事件
  4. An ApplicationStartedEvent : 会在refreshContext实例化bean后且在调用任何应用启动或命令行启动前触发事件
  5. An ApplicationReadyEvent : 在调用任何应用启动或命令行启动后且应用已经准备好接受请求时触发事件
  6. An ApplicationFailedEvent : 应用在启动时发生异常触发事件

写了一些例子去验证应用事件监听器,有兴趣的读者可以自行下载运行,加深下印象。最后打下debug跟踪下流程。

github地址:

参考

https://www.jianshu.com/p/87f101d8ec41


评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×