SpringBoot 完整启动过程
# 680.SpringBoot 完整启动过程
上一篇博客我们说了 SpringApplicationContext 创建的过程,接下来说说 run 的过程(会讲的比较简单,有个印象即可)
# args
可以看到,run 方法中传参了 args,其实就是命令行运行时,命令行中带的参数
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
2
3
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
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;
}
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
首先创建了 StopWatch,这是一个工具类,可以理解为是一个秒表,用来记录启动时间的(start 方法就是开启计时,下面有个 stop 方法就是停止计时)
然后执行方法 configureHeadlessProperty,简单来说就是让应用进入自力更生模式,感兴趣的同学可以自行搜索
private void configureHeadlessProperty() {
System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}
private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless";
2
3
4
5
java.awt.headless 模式_michaelgo 的博客-CSDN 博客 (opens new window)
- 什么是 java.awt.headless 模式:java.awt.headless 是 J2SE 的一种模式,用于在缺失显示屏、鼠标或者键盘时的系统配置。对于后端服务来讲,很多都是需要将这个属性设置为 true 的。
- 什么时候使用 java.awt.headless 模式:对于开发者来讲常常需要在该模式下工作。因为服务器(如提供 Web 服务的)往往可能缺少前述设备,但又需要使用他们提供的功能,生成相应的数据,以提供给客户端(如浏览器所在的配有相关的、键盘和的主机)。
下一步(第 7 行)就是执行 getRunListeners 方法,获取所有的运行监听器并执行:
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
2
看看 getRunListeners 方法:可以看到,和之前的代码一样,也是读取 spring.factories 里配置的信息。
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
2
3
4
目前仅配置了一个:pring-boot-2.3.4.RELEASE.jar!\META-INF\spring.factories
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
2
可以理解为,这个监听器是一个广播,会广而告之系统正在启动。
SpringBoot 应用启动的过程中,会有很多的事件(event),例如初始化完成、环境准备好了、运行完成、失败等,可以参考文档:
后续我们可以自定义监听器,并在项目初始化的过程中做些自定义的事情。
然后就是保存命令行参数,如果后续有用到就可以直接取值
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
然后就是准备环境(prepareEnvironment 方法)并配置:
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
2
下一步就是配置 Banner:
Banner printedBanner = printBanner(environment);
就是我们启动 SpringBoot 的时候,控制台打印的内容(也可以自己配置):
然后就是很重要的创建 IoC 容器:
context = createApplicationContext();
会根据当前项目类型来创建 IoC 容器,目前是 Servlet 应用,所以创建的是 AnnotationConfigServletWebServerApplicationContext
:
public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
有了容器后,下一步就是配置 IoC 容器,然后是刷新:
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
2
3
refreshContext 方法:刷新 IoC 容器,其内部就是调用 Spring 底层的容器,也就是创建所有 bean 并放到容器中,可以参考 Spring 源码的教程。
然后会调用所有监听器的 running 方法:
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
2
3
4
5
6
7
8
至此,结束,并返回 IoC 容器。
# 自定义 Listener 和 Initialzer
以 LearnSpringBoot-Web-Admin 项目为例,新建刚刚提到的一些组件
新建一个 ApplicationContextInitializer
:
package com.peterjxl.learnspringbootwebadmin.listener;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
public class MyApplicationContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("MyApplicationContextInitializer...initialize..." + applicationContext);
}
}
2
3
4
5
6
7
8
9
10
11
ApplicationListener
:
package com.peterjxl.learnspringbootwebadmin.listener;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
public class MyApplicationListener implements ApplicationListener {
@Override
public void onApplicationEvent(ApplicationEvent event) {
// event包含了事件的信息,可以根据事件的类型进行不同的处理
System.out.println("MyApplicationListener...onApplicationEvent..." + event);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
ApplicationRunner
:
package com.peterjxl.learnspringbootwebadmin.listener;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
@Component
public class MyApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("MyApplicationRunner...run...");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
CommandLineRunner
:
package com.peterjxl.learnspringbootwebadmin.listener;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class MyCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("MyCommandLineRunner...run...");
}
}
2
3
4
5
6
7
8
9
10
11
12
SpringApplicationRunListener
:
package com.peterjxl.learnspringbootwebadmin.listener;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
public class MySpringApplicationRunListener implements SpringApplicationRunListener {
private SpringApplication application;
// 必须有的构造器
public MySpringApplicationRunListener(SpringApplication application, String[] args) {
this.application = application;
}
@Override
public void starting() {
// SpringApplication.run()方法执行之前执行的代码
System.out.println("MySpringApplicationRunListener...starting...");
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
// 获取到环境变量
System.out.println("MySpringApplicationRunListener...environmentPrepared...");
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
// IoC容器已经创建完成,但是没有加载bean
System.out.println("MySpringApplicationRunListener...contextPrepared...");
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
// IoC容器已经创建完成,bean也已经加载完成
System.out.println("MySpringApplicationRunListener...contextLoaded...");
}
@Override
public void started(ConfigurableApplicationContext context) {
// SpringApplication.run()方法执行之后执行的代码
System.out.println("MySpringApplicationRunListener...started...");
}
@Override
public void running(ConfigurableApplicationContext context) {
// 应用正在运行
System.out.println("MySpringApplicationRunListener...running...");
}
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
// 应用启动失败
System.out.println("MySpringApplicationRunListener...failed...");
}
}
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
SpringApplicationRunListener
,ApplicationListener
,ApplicationContextInitializer
都是读取 spring.factories 文件的,因此我们新建 resources/META-INF/spring.factories 文件:
org.springframework.context.ApplicationContextInitializer=\
com.peterjxl.learnspringbootwebadmin.listener.MyApplicationContextInitializer
org.springframework.context.ApplicationListener=\
com.peterjxl.learnspringbootwebadmin.listener.MyApplicationListener
org.springframework.boot.SpringApplicationRunListener=\
com.peterjxl.learnspringbootwebadmin.listener.MySpringApplicationRunListener
2
3
4
5
6
7
8
运行结果:可以看到有输出内容
# 完结
已将本文源码上传到 Gitee (opens new window) 或 GitHub (opens new window) 的分支 demo25,读者可以通过切换分支来查看本文的示例代码
至此,SpringBoot 教程系列完结~ 至于后面的整合其他框架,响应式编程,就得去看尚硅谷的大厂学院了。