SpringBoot启动流程
SpringBoot的启动流程大致分为创建SpringApplication和运行SpringApplication两大步。
①创建SpringApplication。
- 启动程序执行SpringApplication.run方法,作用是创建了一个Springpplication实例并执行run方法,这里先分析创建一个Springpplication实例的过程,即调用new SpringApplication()。
1
2
3
4
5
6
public class HelloTestApplication {
public static void main(String[] args) {
SpringApplication.run(HelloTestApplication.class, args);
}
}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
27public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
//二、构造方法中的逻辑:
this.resourceLoader = resourceLoader;
// 1、一定要指定primarySources
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 2、deduce(推断)web类型(servlet、reactive、NoWeb)
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 3、从META-INF/spring.factories中获取
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 4、从META-INF/spring.factories中获取
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 5、推断执行的main方法的定义类
this.mainApplicationClass = deduceMainApplicationClass();
}①调用this.resourceLoader = resourceLoader初始化资源加载器为null。
②断言主要加载资源类不能为null,否则报错。
③初始化主要加载资源类集合primarySources并去重。primarySources即保存了主配置类的信息。
④调用WebApplicationType.deduceFromClasspath()推断当前WEB应用类型。
⑤调用setInitializers()设置应用上下文初始化器。其中重点调用了getSpringFactoriesInstances()方法,最终创建了7个初始化器。
①获取当前线程上下文类加载器。
②获取ApplicationContextInitializer的实例名称集合并去重。即找到类路径下所有META-INF/spring.factories文件解析并获取ApplicationContextInitializer所有配置的类路径名称。
③根据以上类路径创建初始化器实例列表。
④调用AnnotationAwareOrderComparator.sort(instances)初始化器实例列表排序后返回实例对象。
⑥调用setListeners()设置监听器。即找到类路径下所有META-INF/spring.factories文件解析并获取ApplicationListener所有配置的类路径名称。逻辑和上方的创建初始化器ApplicationContextInitializer实例列表一样,最终创建了11个监听器。
⑦推断主入口应用类。通过构造一个运行时异常,再遍历异常栈中的方法名,获取方法名为main的栈帧,从来得到入口类的名字再返回该类。
![]()
- 启动程序执行SpringApplication.run方法,作用是创建了一个Springpplication实例并执行run方法,这里先分析创建一个Springpplication实例的过程,即调用new SpringApplication()。
②运行SpringApplication。即执行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
57public ConfigurableApplicationContext run(String... args) {
// 1 StopWatch开启计时
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 这是设置系统属性java.awt.headless,请百度了解 java.awt.headless的用途。
configureHeadlessProperty();
// 2、获取到 META-INF/spring.factories中配置的SpringApplicationRunListener
// 疑问:又出一个Listener,这个SpringApplicationRunListener是监听什么的?
// 通过它的接口定义、注释了解它的用途
SpringApplicationRunListeners listeners = getRunListeners(args);
// 这里就调用它的starting()
listeners.starting();
try {
// 命令行参数包装为了ApplicationArguments
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 3、准备好了Environment,此刻Environment中都有哪些配置参数了?
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
// 4、打印springboot LOGo图标
Banner printedBanner = printBanner(environment);
// 5、创建ApplicationContext context = createApplicationContext();
context = createApplicationContext();
// 6、获取到 META-INF/spring.factories中配置的SpringBootExceptionReporter
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 7、准备ApplicationContext
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 8、刷新ApplicationContext
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// 9、发布started事件 listeners.started(context);
listeners.started(context);
// 10、执行所有的Runners
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
// 11、发布running中事件
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
// 12、返回ok的ConfigurableApplicationContext
return context;
}①创建一个StopWatch并执行start方法,用于记录任务的执行时间。
②配置Headless属性,让当前应用进入Headless模式,即给系统属性设置了java.awt.headless。Headless模式是在缺少显示屏、键盘或者鼠标时候的系统配置。
③获取到所有运行监听器。即在文件META-INF\spring.factories中获取SpringApplicationRunListener接口的实现类EventPublishingRunListener,主要发布SpringApplicationEvent。这里找到了一个运行监听器SpringApplicationRunListener。
④遍历所有的运行监听器并调用其starting()方法。相当于通知所有感兴趣系统正在启动过程的人,项目正在starting。
⑤保存命令行参数args,即把输入参数转成DefaultApplicationArguments类。
⑥创建Environment并设置比如环境信息,系统熟悉,输入参数和profile信息。
①由于这里是原生Servlet编程,所以创建了一个StandardServletEnvironment。
②调用configureEnvironment()配置环境信息。
①给容器中添加类型转换器conversionService,这里有136个。
②调用configurePropertySources()加载外部配置源,读取所有的配置源的配置属性值。
③调用configureProfiles()激活profile环境内容。
③调用上方创建的运行监听器SpringApplicationRunListener的environmentPrepared方法。
![]()
⑦打印Banner信息。即平常启动项目后打印的Spring标志图。
⑧根据项目类型(Servlet)创建容器AnnotationConfigServletWebServerApplicationContext。
![]()
⑨在文件META-INF\spring.factories中获取SpringBootExceptionReporter接口的实现类FailureAnalyzers。
⑩准备IOC容器的基本信息。
①往IOC容器中保存环境信息。
②调用postProcessApplicationContext()执行IOC容器的后置处理流程,即给容器中加入一些类型转换器等。
③调用applyInitializers()执行应用初始化器,即遍历之前创建的所有ApplicationContextInitializer实例(7个)并执行其initialize()方法,来对ioc容器进行初始化的扩展工作。
④调用listeners.contextPrepared()遍历之前创建的所有SpringApplicationRunListener实例(1个即EventPublishingRunListener)并执行其contextPrepared()方法。
⑤如果延迟加载,在上下文添加处理器LazyInitializationBeanFactoryPostProcessor。
⑥调用listeners.contextLoaded()遍历之前创建的所有SpringApplicationRunListener实例(1个即EventPublishingRunListener)并执行其contextLoaded()方法。
![]()
⑪刷新ioc容器。会来到熟悉的Spring容器初始化过程,在之前的文章中有详细分析到:https://perfectcode.top/2020/12/03/Spring%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/
⑫完成容器刷新后的工作,这里是个空方法。
⑬调用listeners.started()遍历之前创建的所有SpringApplicationRunListener实例(1个即EventPublishingRunListener)并执行其started()方法。
⑭调用callRunners()获取容器中的ApplicationRunner和CommandLineRunner组件并按照Order进行排序后遍历所有的runner调用run()方法。
⑮以上如果发生异常,会执行handleRunFailure()来遍历之前创建的所有SpringApplicationRunListener实例(1个即EventPublishingRunListener)并执行其failed()方法。
⑯如果没有发生异常,会执行listeners.running()来遍历之前创建的所有SpringApplicationRunListener实例(1个即EventPublishingRunListener)并执行其running()方法,如果在执行running()过程中有异常则调用handleRunFailure()–>reportFailure()来遍历所有的SpringBootExceptionReporter(1个即FailureAnalyzers)执行其reportException()方法。
⑰至此,ioc容器创建成功并返回。
总结如图:
可以看出SpringBoot启动过程中会在不同阶段运行初始化器、运行监听器以及runner的相关方法(图中粗体文字),所以我们可以自定义初始化器、运行监听器或者runner来实现自定义操作。
①自定义ApplicationContextInitializer。
1
2
3
4
5
6public class MyApplicationContextInitializer implements ApplicationContextInitializer {
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("MyApplicationContextInitializer ....initialize.... ");
}
}②自定义ApplicationListener。
1
2
3
4
5
6public class MyApplicationListener implements ApplicationListener {
public void onApplicationEvent(ApplicationEvent event) {
System.out.println("MyApplicationListener.....onApplicationEvent...");
}
}③自定义SpringApplicationRunListener。
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
42public class MySpringApplicationRunListener implements SpringApplicationRunListener {
private SpringApplication application;
public MySpringApplicationRunListener(SpringApplication application, String[] args){
this.application = application;
}
public void starting() {
System.out.println("MySpringApplicationRunListener....starting....");
}
public void environmentPrepared(ConfigurableEnvironment environment) {
System.out.println("MySpringApplicationRunListener....environmentPrepared....");
}
public void contextPrepared(ConfigurableApplicationContext context) {
System.out.println("MySpringApplicationRunListener....contextPrepared....");
}
public void contextLoaded(ConfigurableApplicationContext context) {
System.out.println("MySpringApplicationRunListener....contextLoaded....");
}
public void started(ConfigurableApplicationContext context) {
System.out.println("MySpringApplicationRunListener....started....");
}
public void running(ConfigurableApplicationContext context) {
System.out.println("MySpringApplicationRunListener....running....");
}
public void failed(ConfigurableApplicationContext context, Throwable exception) {
System.out.println("MySpringApplicationRunListener....failed....");
}
}④自定义ApplicationRunner。
1
2
3
4
5
6
7
8
public class MyApplicationRunner implements ApplicationRunner {
public void run(ApplicationArguments args) throws Exception {
System.out.println("MyApplicationRunner...run...");
}
}⑤自定义CommandLineRunner。
1
2
3
4
5
6
7
8
public class MyCommandLineRunner implements CommandLineRunner {
public void run(String... args) throws Exception {
System.out.println("MyCommandLineRunner....run....");
}
}⑥添加配置文件spring.factories。
1
2
3
4
5
6
7
8org.springframework.context.ApplicationContextInitializer=\
com.example.demo.listener.MyApplicationContextInitializer
org.springframework.context.ApplicationListener=\
com.example.demo.listener.MyApplicationListener
org.springframework.boot.SpringApplicationRunListener=\
com.example.demo.listener.MySpringApplicationRunListener⑦运行后控制台打印的主要代码如下。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19MyApplicationListener.....onApplicationEvent...
MySpringApplicationRunListener....starting....
MyApplicationListener.....onApplicationEvent...
MySpringApplicationRunListener....environmentPrepared....
MyApplicationContextInitializer ....initialize....
MyApplicationListener.....onApplicationEvent...
MySpringApplicationRunListener....contextPrepared....
MyApplicationListener.....onApplicationEvent...
MySpringApplicationRunListener....contextLoaded....
MyApplicationListener.....onApplicationEvent...
MyApplicationListener.....onApplicationEvent...
MyApplicationListener.....onApplicationEvent...
MyApplicationListener.....onApplicationEvent...
MySpringApplicationRunListener....started....
MyApplicationRunner...run...
MyCommandLineRunner....run....
MyApplicationListener.....onApplicationEvent...
MyApplicationListener.....onApplicationEvent...
MySpringApplicationRunListener....running....



