0.概述
在前面第一篇、第二篇都已经简要的分析了下SpringBoot启动配置的原理。接下来在这篇中,我们会再次深入一点,回过头,继续看源码!废话不多说,开整把~ ~ ~
1.还是从程序入口点看起
为了直观感受和更好的理解代码逻辑,我们现在SpringBoot启动程序处下个断点。然后以debug模式跑起来。哦,对了。为了减少不必要的组件启动,这次我们只引入了web模块。
程序跑起来后,断下来,进入run方法进行分析。代码如下:
1 | public static ConfigurableApplicationContext run(Class<?>[] primarySources, |
2.接下来我们分别看看具体干了些什么
-
创建SpringApplication
-
源码如下
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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83public class SpringApplication {
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
+ "annotation.AnnotationConfigApplicationContext";
public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot."
+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
+ "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
private static final String REACTIVE_WEB_ENVIRONMENT_CLASS = "org.springframework."
+ "web.reactive.DispatcherHandler";
private static final String MVC_WEB_ENVIRONMENT_CLASS = "org.springframework."
+ "web.servlet.DispatcherServlet";
//构造方法
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
//断言启动类不为空,如果为空就报错
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//判断环境,代码在下方
this.webApplicationType = deduceWebApplicationType();
//设置Initializer。通过getSpringFactoriesInstances()得到需要设置的Initializer。方法如下
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
//设置ApplicationListener。通过getSpringFactoriesInstances()从/META-INF/spring.factories中读取所有的ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//从堆栈中找出main函数所在的类,方法体如下
this.mainApplicationClass = deduceMainApplicationClass();
}
//判断环境
private WebApplicationType deduceWebApplicationType() {
//REACTIVE类型。REACTIVE_WEB_ENVIRONMENT_CLASS和MVC_WEB_ENVIRONMENT_CLASS都是常量,定义在上头
if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
&& !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
//普通类型 WEB_ENVIRONMENT_CLASSES定义在上头
for (String className : WEB_ENVIRONMENT_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
//getSpringFactoriesInstances()
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
//使用SpringFactoriesLoader.loadFactoryNames()从/META-INF/spring.factories中读取所有的
//ApplicationContextInitializer。截图在下方
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
//找到拥有main函数的启动类
private Class<?> deduceMainApplicationClass() {
try {
//获取堆栈信息
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
//遍历堆栈,寻找有main方法的类,并且把它加载到内存
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
}-
/META-INF/spring.factories中所有的ApplicationContextInitializer。这里以SpringBoot的jar包为例
内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16\ =
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
\ =
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
-
-
总结
在SpringBoot启动时,会先创建
SpringApplication
实例。在SpringApplication构造方法中做了如下几件事:- 首先会判断当前环境
- 通过
getSpringFactoriesInstances()
方法,从类路径下/META-INF/spring.factories
文件中获取所有的ApplicationContextInitializer
,加入容器(setInitializers) - 和上面一样,读取类路径下
/META-INF/spring.factories
文件中获取所有的ApplicationListener
,加入到容器中(setListeners) - 从堆栈中找出main函数所在的类,加载到内存
以上就是
new SpringApplication()
的分析。接下来就差run方法了。 -
-
调用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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191public ConfigurableApplicationContext run(String... args) {
//好像是创建计时器,统计时间,这个我们不关心
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//这个看源码好像是设置系统属性。我们也不关心
configureHeadlessProperty();
//getRunListeners()方法在下面,大致意识是还是读取类路径/META-INF/spring.factories中所有SpringApplicationRunListener
//该类路径指的是SpringBoot的jar包目录下
SpringApplicationRunListeners listeners = getRunListeners(args);
//循环调用SpringApplicationRunListener的starting方法。代码如下
listeners.starting();
try {
//处理控制台传过来的参数。
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//字面意识是准备环境。在准备完成后,会调用Listener的environmentPrepared方法。代码如下
ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);
configureIgnoreBeanInfo(environment);
//打印banner图
Banner printedBanner = printBanner(environment);
//根据环境,创建Spring上下文。代码如下:
context = createApplicationContext();
//从类路径下/META-INF/spring.factories中获取所有SpringBootExceptionReporter
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//预备上下文环境,在初始化应用后,会调用所有Initializer的initializer()方法,再接着会调用Listener的contextPrepared方法。在准备环境完成后,调用Listener的contextLoaded方法(),告知Context已经加载完毕。代码以及介绍如下:
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//刷新容器,ioc容器初始化(如果是web应用还会创建嵌入式的Tomcat).这个就不多说了。如果有需要请看第一篇和第二篇文章
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
//调用所有listeners的started方法。具体方法如下:
listeners.started(context);
//获取所有ApplicationRunner和CommandLineRunner并循环调用,方法体如下:
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
//调用所有listener的running方法。方法体如下
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
//返回Spring的上下文
return context;
}
//--------------------------------------以下为某些方法的方法体(按出场顺序)---------------------------------------
//getRunListeners()方法。获取springBoot jar包下META-INF/spring.factories中的所有SpringApplicationRunListener
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
//listeners.starting()方法,循环调用starting()
public void starting() {
for (SpringApplicationRunListener listener : this.listeners) {
listener.starting();
}
}
//准备环境(prepareEnvironment).环境准备完成后,会调用Listener的environmentPrepared方法,告知环境初始化完成
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
//调用Listener的environmentPrepared方法,告知环境初始化完成
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (this.webApplicationType == WebApplicationType.NONE) {
environment = new EnvironmentConverter(getClassLoader())
.convertToStandardEnvironmentIfNecessary(environment);
}
ConfigurationPropertySources.attach(environment);
return environment;
}
//根据环境创建Spring上下文
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_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);
}
//预备上下文环境
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
//为上下文设置环境
context.setEnvironment(environment);
postProcessApplicationContext(context);
//翻译的字面意识是应用初始化,具体方法如下
applyInitializers(context);
//应用初始化后,调用Listener的contextPrepared方法
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
//在准备环境完成后,调用Listener的contextLoaded方法(),告知Context已经加载完毕
listeners.contextLoaded(context);
}
//应用初始化.初始化完毕后,会调用所有Initializer的initializer()方法
protected void applyInitializers(ConfigurableApplicationContext context) {
//拿到所有的Initializer遍历
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
initializer.getClass(), ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
//循环调用Initializer的initializer方法()
initializer.initialize(context);
}
}
//listeners.started(context);循环调用所有Listener的started方法
public void started(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.started(context);
}
}
//callRunners()
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<>();
//获取所有ApplicationRunner和CommandLineRunner
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
//循环调用
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
//running
public void running(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.running(context);
}
} -
总结
SpringBoot在run方法中主要做了如下几件事
- 从SpringBoot这个jar包下,获取/META-INFO/spring.factories中获取所有
SpringApplicationRunListeners
- 循环调用
SpringApplicationRunListener.starting()
方法 - 处理控制台传过来的参数。
- 准备环境。在准备完成后,会调用
Listener.environmentPrepared()
方法 - 打印banner图
- 根据环境,创建Spring上下文
- 从类路径下/META-INF/spring.factories中获取所有
SpringBootExceptionReporter
- 预备上下文环境,在初始化应用后,先获取所有的Initializer,然后会调用所有
Initializer.initializer()
方法,再接着会调用Listener.contextPrepared()
方法。在准备环境完成后,调用Listener.contextLoaded()
方法,告知Context已经加载完毕。 - 刷新容器,ioc容器初始化(如果是web应用还会创建嵌入式的Tomcat),初始化bean等操作
- 调用所有
listeners.started()
方法 - 获取所有
ApplicationRunner
和CommandLineRunner
并循环调用 - 调用所有
listener.running
方法 - 返回Spring上下文(SpringContext)
- 从SpringBoot这个jar包下,获取/META-INFO/spring.factories中获取所有
3.总结
以上就是SpringBoot启动配置原理了。再来个全文总结。以下类是我们在这一篇,需要注意的,以后再coding过程中也会用到的。
- 配置在META-INF/spring.factories
- ApplicationContextInitializer
- ApplicationListener
- SpringApplicationRunListener
好了,以上就是本篇的内容。感谢大家!有问题可以留言,不论是批评还是请教,都非常感谢大家~ ~ ~, 下一篇将带来自定义starter,敬请期待 ~ ~ ~
-
评论