有道无术,术可求;有术无道,止于术
1、前言
学习过springboot的都知道,在Springboot的main入口函数中调用SpringApplication.run(DemoApplication.class,args)函数便可以启用SpringBoot应用程序,跟踪一下SpringApplication源码可以发现,最终还是调用了SpringApplication的动态run函数。
下面以SpringBoot2.2.1.RELEASE为例简单分析一下运行过程。
我们在编写一个spring boot应用时通常启动的方式是通过SpringApplication.run(xxx.class, args)
来启动的
1 |
|
2、分析 SpringApplication构造函数
SpringApplication
源码:
1 | public class SpringApplication { |
可知这个构造器类的初始化包括以下 7 个过程,下面逐一分析每个步骤的源码:
2.1、赋值资源加载器
1 | this.resourceLoader = resourceLoader; |
2.2、断言主要加载资源类不能为 null,否则报错
1 | Assert.notNull(primarySources, "PrimarySources must not be null"); |
2.3、初始化主要加载资源类集合并去重
1 | this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); |
2.4、推断当前 WEB 应用类型
1 | this.webApplicationType = deduceWebApplicationType(); |
来看下 deduceWebApplicationType 方法和相关的源码:
1 | // 应用类型 |
2.5、设置ApplicationContextInitializer初始化器
1 | setInitializers((Collection)getSpringFactoriesInstances(ApplicationContextInitializer.class)); |
初始化initializers属性,加载classpath下META-INF/spring.factories
中配置的ApplicationContextInitializer。
ApplicationContextInitializer
的作用是什么?源码如下。
1 | public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> { |
用来初始化指定的 Spring 应用上下文,如注册属性资源、激活 Profiles 等。
来看下 setInitializers 方法源码,其实就是初始化一个 ApplicationContextInitializer 应用上下文初始化器实例的集合。
1 | public void setInitializers(Collection> initializers) { |
再来看下这个初始化 getSpringFactoriesInstances
方法和相关的源码:
1 | private |
这个方法会尝试从类路径的META-INF/spring.factories处读取相应配置文件,然后进行遍历,读取配置文件中Key 为:org.springframework.context.ApplicationContextInitializer
的value。以spring-boot-autoconfigure这个包为例,它的META-INF/spring.factories
部分定义如下所示:
1 | # Initializers |
其中上面代码用到的SpringFactoriesLoader.loadFactoryNames(xx.class)
的具体实现见另一篇文章springboot2.2自动注入文件spring.factories如何加载详解
2.6、设置ApplicationListener监听器
1 | setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); |
setListeners 初始化属性listeners,加载classpath下META-INF/spring.factories
中配置的ApplicationListener
,此处入参为getSpringFactoriesInstances
方法入参type= ApplicationListener.class
ApplicationListener 的作用是什么?源码如下。
1 |
|
看源码,这个接口继承了 JDK 的java.util.EventListener
接口,实现了观察者模式,它一般用来定义感兴趣的事件类型,事件类型限定于 ApplicationEvent 的子类,这同样继承了 JDK 的 java.util.EventObject
接口。
设置监听器和设置初始化器调用的方法是一样的,只是传入的类型不一样,设置监听器的接口类型为:getSpringFactoriesInstances
,对应的spring-boot-autoconfigure-2.2.1.RELEASE.jar!/META-INF/spring.factories
文件配置内容请见下方。
1 | # Application Listeners |
可以看出目前只有一个 BackgroundPreinitializer
监听器。
2.7、推断主入口应用类
1 | deduceMainApplicationClass() |
deduceMainApplicationClass()方法的源码实现如下:
1 | private Class deduceMainApplicationClass() { |
这个推断入口应用类的方式有点特别,通过构造一个运行时异常,再遍历异常栈中的方法名,获取方法名为 main 的栈帧,从来得到入口类的名字再返回该类。
3、 分析 SpringApplication中 run方法
SpringApplication的run方法代码如下:
1 | public ConfigurableApplicationContext run(String... args) { |
其实这个方法我们可以简单的总结下步骤为 :
- 配置属性
- 获取监听器,发布应用开始启动事件
- 初始化输入参数
- 配置环境,输出banner
- 创建上下文
- 预处理上下文
- 刷新上下文
- 刷新上下文的后处理,空实现
- 发布应用已经启动事件
- 发布应用启动完成事件
所以,我们可以按以下几步来分解 run 方法的启动过程。
3.1、创建并启动StopWatch
创建并启动计时监控类
1 | StopWatch stopWatch = new StopWatch(); |
来看下这个计时监控类 StopWatch 的相关源码:
1 | public class StopWatch { |
首先记录了当前任务的名称,默认为空字符串,然后记录当前 Spring Boot 应用启动的开始时间(单位纳秒)。
3.2、初始化SpringBootExceptionReporter
初始化应用上下文和异常报告集合
1 | ConfigurableApplicationContext context = null; |
3.3、设置系统属性 java.awt.headless
的值
1 | configureHeadlessProperty(); |
设置该默认值为:true,Java.awt.headless = true 有什么作用?
对于一个 Java 服务器来说经常要处理一些图形元素,例如地图的创建或者图形和图表等。这些API基本上总是需要运行一个X-server以便能使用AWT(Abstract Window Toolkit,抽象窗口工具集)。然而运行一个不必要的 X-server 并不是一种好的管理方式。有时你甚至不能运行 X-server,因此最好的方案是运行 headless 服务器,来进行简单的图像处理。
3.4、创建SpringApplicationRunListeners并发布ApplicationStartingEvent
3.4.1、getRunListeners()方法
1 | SpringApplicationRunListeners listeners = getRunListeners(args); |
来看下创建 SpringApplicationRunListeners运行监听器相关的源码:
1 | private SpringApplicationRunListeners getRunListeners(String[] args) { |
可以看到返回了SpringApplicationRunListeners对象,
3.4.2、创建SpringApplicationRunListeners
返回的SpringApplicationRunListeners这个类的源码如下:
1 | class SpringApplicationRunListeners { |
创建逻辑和之前实例化初始化器和监听器的一样,一样调用的是 getSpringFactoriesInstances
方法来获取配置的监听器名称并实例化所有的类。
SpringApplicationRunListener 所有监听器配置在 spring-boot-2.2.1.RELEASE.jar!/META-INF/spring.factories
这个配置文件里面。
1 | # Run Listeners |
可以看到SpringApplicationRunListener的值是org.springframework.boot.context.event.EventPublishingRunListener
这个类,所以创建SpringApplicationRunListeners这个对象时构造方法传递的2个参数就是这个EventPublishingRunListener的实例。
3.4.3、listeners.starting()
所以listeners.starting();
这行代码的实现SpringApplicationRunListeners.starting()方法中遍历的属性this.listeners
实际就只有1个EventPublishingRunListener
1 | class SpringApplicationRunListeners{ |
接下来看EventPublishingRunListener
类的starting方法的源码实现。
3.4.4、EventPublishingRunListener.starting()
1 | /** |
需要注意的是这里是一个典型的观察者模式的应用,SpringApplication
是被观察者,ApplicationListener
接口的实现是观察者,spring启动时从spring.factories
文件中根据找出所有的key为org.springframework.context.ApplicationListener
的ApplicationListener
接口的实现(观察者),然后在EventPublishingRunListener
类的构造方法将所有的ApplicationListener接口的实现(观察者)注册到类型为SimpleApplicationEventMulticaster
的initialMulticaster属性中,而SpringApplication
的run方法中又持有了EventPublishingRunListener这个类的引用(SpringApplicationRunListeners listeners = getRunListeners(args);
),所以在SpringApplication
是中可以通过listeners.starting()方法调用委托给真正的SimpleApplicationEventMulticaster
进行事件发布。
3.4.5、SimpleApplicationEventMulticaster
SimpleApplicationEventMulticaster类的作用时真正的发布事件(ApplicationEvent event)。
SimpleApplicationEventMulticaster类的源码如下:
1 | /** |
3.5、初始化默认应用参数类
1 | ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); |
DefaultApplicationArguments类的源码如下:
1 | public class DefaultApplicationArguments implements ApplicationArguments { |
从构造方法可以看到最主要的操作就是this.source = new Source(args);
这一行代码,而Souce类是一个内部内,Source的构造方法中直接调用了父类SimpleCommandLinePropertySource的构造方法,下面看SimpleCommandLinePropertySource
类构造方法的实现细节:
1 | public class SimpleCommandLinePropertySource extends CommandLinePropertySource<CommandLineArgs> { |
可以看到创建了SimpleCommandLineArgsParser类的实例,调用了它的parse(String… args)方法,从类名和方法命名可以就应该能猜到这个类是用来解析命令行参数的。
SimpleCommandLineArgsParser类源码如下:
1 | class SimpleCommandLineArgsParser { |
到这里ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
这一行代码的作用就分析完了,可以看到做的事情就是解析命令行参数。
3.6、创建Environment并发布ApplicationEnvironmentPreparedEvent
1 | ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); |
下面我们主要来看下准备环境的 prepareEnvironment
源码:
1 | private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, |
上面的代码需要重点关注,和spring cloud的整合在这里开始。
3.6.1、获取(或者创建)ConfigurableEnvironment
1 | ConfigurableEnvironment environment = getOrCreateEnvironment(); |
getOrCreateEnvironment()方法实现如下:
1 | private ConfigurableEnvironment getOrCreateEnvironment() { |
这里分为标准 Servlet 环境和标准环境。
3.6.2、 配置ConfigurableEnvironment
1 | configureEnvironment(environment, applicationArguments.getSourceArgs()); |
configureEnvironment()实现如下:
1 | protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) { |
3.6.2.1、创建ConversionService并存到Environment中
- ApplicationConversionService.getSharedInstance()源码如下:
1 | /** |
可以看到getSharedInstance()方法就是一个典型的双重判断单例模式的实现,只是需要注意这里sharedInstance变量是成员变量使用volatile进行了修饰,禁止指令重排序及保证内存可见性,ApplicationConversionService的构造方法中最终调用了configure(FormatterRegistry registry)
方法
从方法实现看默认注册了一些Convert、Formater接口的实现。
- DefaultConversionService.addDefaultConverters(registry);源码如下:
1 | public class DefaultConversionService extends GenericConversionService { |
- DefaultFormattingConversionService.addDefaultFormatters(registry);实现源码如下:
1 | public class DefaultFormattingConversionService extends FormattingConversionService { |
3.6.2.2、将命令行的属性添加到Environment中
configurePropertySources
方法源码如下:
1 | public class SpringApplication { |
接下来继续分析configurePropertySources(environment, args);
这一行后面的代码configureProfiles(environment, args);
3.6.2.3、configureProfiles(environment, args);
配置additionalProfiles到Environment中,设置激活的profile
1 | public class SpringApplication { |
prepareEnvironment()方法的源码分析完了,现在让我们回到public ConfigurableApplicationContext run(String... args)
方法中,继续看prepareEnvironment()方法之后的方法configureIgnoreBeanInfo(environment)
。
3.6.3、启动参数绑定到ConfigurableEnvironment中
1 | ConfigurationPropertySources.attach(environment); |
ConfigurationPropertySources
类的静态方法attach(Environment environment)
源码如下:
1 | public final class ConfigurationPropertySources { |
3.6.4、发布ApplicationEnvironmentPreparedEvent事件
1 | // 内部会发布ApplicationEnvironmentPreparedEvent事件,依次执行如下的listener: |
SpringApplicationRunListeners
类的environmentPrepared
方法源码如下:
1 | class SpringApplicationRunListeners { |
SpringApplicationRunListener
的实现类只有EventPublishingRunListener
,EventPublishingRunListener类的environmentPrepared
方法的源码如下:
1 | public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered { |
3.6.4.1、SimpleApplicationEventMulticaster
SimpleApplicationEventMulticaster类的作用时真正的发布事件(ApplicationEvent event)。
SimpleApplicationEventMulticaster类的源码如下:
1 | /** |
从上面第47行代码可以看到这是一个for循环,for循环的对象是getApplications()方法的返回值,这里我们不关注是怎么找到对ApplicationEnvironmentPreparedEvent事件感兴趣的ApplicationListener的,重点看getApplicaitons()方法的返回值有哪些,有哪些ApplicationListener的实现类对ApplicationEnvironmentPreparedEvent事件感兴趣,下面具体分析这些监听器的实现。
3.6.4.2、接收ApplicationEnvironmentPreparedEvent事件的ApplicationListener
使用idea调试时getApplications()方法的返回值(spring cloud环境):
按照执行顺序这些监听器的实现出处如下(注意下面文件的内容不是实际的文件内容顺序,是按照实际执行顺序列出):
spring-cloud-context-2.2.0.RELEASE.jar!\META-INF\spring.factories
共2个properties1
2
3
4
5## Application Listeners
\ =
org.springframework.cloud.bootstrap.BootstrapApplicationListener,\
org.springframework.cloud.bootstrap.LoggingSystemShutdownListener,\
...省略不相关的
spring-boot-2.2.1.RELEASE.jar!\META-INF\spring.factories
properties1
2
3
4
5
6## Application Listeners
\ =
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
...省略不相关的spring-boot-autoconfigure-2.2.1.RELEASE.jar!\META-INF\spring.factories
共1个properties1
2
3# Application Listeners
\ =
org.springframework.boot.autoconfigure.BackgroundPreinitializerspring-boot-2.2.1.RELEASE.jar!\META-INF\spring.factories
properties1
2
3
4
5
6## Application Listeners
\ =
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
...省略不相关的
根据getApplications()
方法的返回值看到第一个监听了ApplicationEnvironmentPreparedEvent
事件的ApplicationListener
就是spring cloud的org.springframework.cloud.bootstrap.BootstrapApplicationListener
, 下面开始分析BootstrapApplicationListener`类的源码
3.6.4.3、spring cloud的BootstrapApplicationListener
1 | public class BootstrapApplicationListener |
创建spring cloud的ApplicationContext
context = bootstrapServiceContext(environment, event.getSpringApplication(), configName);
这行代码的实现如下:
1 | private ConfigurableApplicationContext bootstrapServiceContext( |
1 | org.springframework.cloud.bootstrap.BootstrapApplicationListener |
3.6.4.4、ConfigFileApplicationListener
该类的作用是加载配置文件
1 | public class ConfigFileApplicationListener implements EnvironmentPostProcessor, SmartApplicationListener, Ordered { |
SystemEnvironmentPropertySourceEnvironmentPostProcessor
java1
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
33public class SystemEnvironmentPropertySourceEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered {
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
// sourceName的值为"systemEnvironment"
String sourceName = StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME;
// 获取name为"systemEnvironment"的系统环境变量属性源
PropertySource propertySource = environment.getPropertySources().get(sourceName);
// 一般都不会为null
if (propertySource != null) {
// 会执行到这里,调用当前类的replacePropertySource方法
replacePropertySource(environment, sourceName, propertySource);
}
}
/**
*
* @param environment spring boot/cloud的环境对象,servlet环境是StandardServletEnvironment
* @param sourceName 值为"systemEnvironment"
* @parm propertySource 名称为"systemEnvironment"的系统环境变量属性源
*/
private void replacePropertySource(ConfigurableEnvironment environment, String sourceName,
PropertySource propertySource) {
// 获取所有系统环境变量
MaporiginalSource = (Map ) propertySource.getSource();
SystemEnvironmentPropertySource source = new OriginAwareSystemEnvironmentPropertySource(sourceName,
originalSource);
// 把name为"systemEnvironment"的系统环境变量属性源替换为OriginAwareSystemEnvironmentPropertySource
// 有什么作用??目前还不清楚为什么要这样做。
environment.getPropertySources().replace(sourceName, source);
}
}
SpringApplicationJsonEnvironmentPostProcessor
java1
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/**
*
* 找到environment中Key为"spring.application.json"的属性,将其值解析为Map对象,作为一个属性源,
* 向environment中添加1个name为"spring.application.json"的JsonPropertySource属性源,数据源是之前描述的解析得到的Map对象
*/
public class SpringApplicationJsonEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered {
public static final String SPRING_APPLICATION_JSON_PROPERTY = "spring.application.json";
public static final String SPRING_APPLICATION_JSON_ENVIRONMENT_VARIABLE = "SPRING_APPLICATION_JSON";
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
// 获取可变属性源,包括springApplicationCommandLineArgs、configurationProperties、bootstrap、systemProperties、systemEnvironment属性源
MutablePropertySources propertySources = environment.getPropertySources();
propertySources.stream()
// 获取"spring.application.json" 或 "SPRING_APPLICATION_JSON"属性的实际值对应的JsonPropertyValue对象
// 如果当前属性源不存在该属性则返回null
// 返回的数据形式如[null, JsonPropertyValue, null, JsonPropertyValue, JsonPropertyValue, null...]
.map(JsonPropertyValue::get)
// 找出第一个非null的JsonPropertyValue
.filter(Objects::nonNull).findFirst()
// 如果存在"spring.application.json" 或 "SPRING_APPLICATION_JSON"属性的实际值对应的JsonPropertyValue对象则进行处理
.ifPresent((v) -> processJson(environment, v));
}
// 处理spring boot/cloud环境中"spring.application.json" 或 "SPRING_APPLICATION_JSON"属性的实际值对应的JsonPropertyValue对象
private void processJson(ConfigurableEnvironment environment, JsonPropertyValue propertyValue) {
// 根据classpath类路径是否存在com.fasterxml.jackson.databind.ObjectMapper、com.google.gson.Gson、org.yaml.snakeyaml.Yaml
// 类返回对应的解析器JacksonJsonParser、GsonJsonParser、YamlJsonParser、BasicJsonParser
JsonParser parser = JsonParserFactory.getJsonParser();
// propertyValue.getJson()的值为environment中"spring.application.json" 或 "SPRING_APPLICATION_JSON"属性的值
// 使用jackson、gson、yaml将字符串解析为Map类型对象
Mapmap = parser.parseMap(propertyValue.getJson());
if (!map.isEmpty()) {
// 添加name为"spring.application.json"的属性源
addJsonPropertySource(environment, new JsonPropertySource(propertyValue, flatten(map)));
}
}
private static class JsonPropertyValue {
// 实际值为 ["spring.application.json", "SPRING_APPLICATION_JSON"]
private static final String[] CANDIDATES = { SPRING_APPLICATION_JSON_PROPERTY,
SPRING_APPLICATION_JSON_ENVIRONMENT_VARIABLE };
private final PropertySource propertySource;
private final String propertyName;
private final String json;
/**
* @param propertySource "spring.application.json" 或 "SPRING_APPLICATION_JSON"属性所在的属性源
* @param propertyName 表示根据哪个属性名找到的属性值的,值为字符串"spring.application.json" 或 "SPRING_APPLICATION_JSON"其中之一
* @param json 在envorpnment中"spring.application.json" 或 "SPRING_APPLICATION_JSON"属性对应的值
*/
JsonPropertyValue(PropertySource propertySource, String propertyName, String json) {
this.propertySource = propertySource;
this.propertyName = propertyName;
this.json = json;
}
/**
*
* @param propertySource 属性源
*/
static JsonPropertyValue get(PropertySource propertySource) {
for (String candidate : CANDIDATES) {
// 获取"spring.application.json" 或 "SPRING_APPLICATION_JSON"属性的实际值
Object value = propertySource.getProperty(candidate);
// 判断属性的值是否是字符串并且有内容
if (value instanceof String && StringUtils.hasLength((String) value)) {
// 返回当前类的实例
return new JsonPropertyValue(propertySource, candidate, (String) value);
}
}
return null;
}
}
private static class JsonPropertySource extends MapPropertySource implements OriginLookup<String> {
private final JsonPropertyValue propertyValue;
JsonPropertySource(JsonPropertyValue propertyValue, Mapsource) {
// 属性源的name为"spring.application.json"
super(SPRING_APPLICATION_JSON_PROPERTY, source);
this.propertyValue = propertyValue;
}
public Origin getOrigin(String key) {
return this.propertyValue.getOrigin();
}
}
}
HostInfoEnvironmentPostProcessor
java1
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 class HostInfoEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered {
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
// 找到第1个非回环地址(非127.xxx.xxx.xxx)信息
InetUtils.HostInfo hostInfo = getFirstNonLoopbackHostInfo(environment);
LinkedHashMapmap = new LinkedHashMap<>();
// 计算机名,例如我的电脑是calebzhao,win10系统在桌面图标“此电脑”右键菜单点击属性即可看到计算机名
map.put("spring.cloud.client.hostname", hostInfo.getHostname());
// 电脑ip地址,不是本地回环地址,一般个人计算机是192.168.xxx.xxx
map.put("spring.cloud.client.ip-address", hostInfo.getIpAddress());
MapPropertySource propertySource = new MapPropertySource(
"springCloudClientHostInfo", map);
// 添加了一个名称为springCloudClientHostInfo的属性源
environment.getPropertySources().addLast(propertySource);
}
private HostInfo getFirstNonLoopbackHostInfo(ConfigurableEnvironment environment) {
InetUtilsProperties target = new InetUtilsProperties();
ConfigurationPropertySources.attach(environment);
Binder.get(environment).bind(InetUtilsProperties.PREFIX,
Bindable.ofInstance(target));
try (InetUtils utils = new InetUtils(target)) {
return utils.findFirstNonLoopbackHostInfo();
}
}
}
ConfigFileApplicationListener
默认从
["file:./config/", "file:./", "classpath:/config/", "classpath:/"]
目录下加载配置文件java1
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
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341public class ConfigFileApplicationListener implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {
private static final String DEFAULT_PROPERTIES = "defaultProperties";
public static final String ACTIVE_PROFILES_PROPERTY = "spring.profiles.active";
public static final String INCLUDE_PROFILES_PROPERTY = "spring.profiles.include";
static {
SetfilteredProperties = new HashSet<>();
filteredProperties.add("spring.profiles.active");
filteredProperties.add("spring.profiles.include");
LOAD_FILTERED_PROPERTY = Collections.unmodifiableSet(filteredProperties);
}
// 入口
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
addPropertySources(environment, application.getResourceLoader());
}
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
// 在systemEnvironment属性源之后添加能够获取随机数的属性源,属性源的名称为"random"
RandomValuePropertySource.addToEnvironment(environment);
// 加载
new Loader(environment, resourceLoader).load();
}
private class Loader {
private final ConfigurableEnvironment environment;
private final PropertySourcesPlaceholdersResolver placeholdersResolver;
private final ResourceLoader resourceLoader;
private final ListpropertySourceLoaders;
private Dequeprofiles;
Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
this.environment = environment;
this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(this.environment);
this.resourceLoader = (resourceLoader != null) ? resourceLoader : new DefaultResourceLoader();
// 这里非常关键,到spring.factories中找key为org.springframework.boot.env.PropertySourceLoader的属性源加载器,
// 括号中的内容代表该PropertySourceLoader所在的spring.factories文件所在的jar文件,按照执行顺序找到的实现如下:
// org.springframework.boot.env.PropertiesPropertySourceLoader (spring-boot-2.2.1.RELEASE.jar)
// org.springframework.boot.env.YamlPropertySourceLoader (spring-boot-2.2.1.RELEASE.jar)
this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,
getClass().getClassLoader());
}
// 实际处理逻辑:加载项目下的application.yml,,默认从["file:./config/", "file:./", "classpath:/config/", "classpath:/"]加载配置文件
void load() {
// environment包括springApplicationCommandLineArgs、configurationProperties、【bootstrap】、systemProperties、systemEnvironment属性源
FilteredPropertySource.apply(this.environment,
DEFAULT_PROPERTIES, // defaultProperties
LOAD_FILTERED_PROPERTY, // ["spring.profiles.active", "spring.profiles.include"]
(defaultProperties) -> {
this.profiles = new LinkedList<>();
this.processedProfiles = new LinkedList<>();
this.activatedProfiles = false;
this.loaded = new LinkedHashMap<>();
// 1.把环境中的profiles取出来,默认会增加1个为null的profile
// 如果用户如果自己通过命令行参数、jvm系统属性、系统环境变量指定了"spring.profiles.active"
// 或"spring.profiles.include"属性值则不使用默认的profile,
// 否则表明用户没有明确指定启用哪些profile,那么就使用spring默认的profile,
// 用户设置可以覆盖spring默认设置
initializeProfiles();
// 2.循环处理profiles,查找文件位置然后去加载文件
while (!this.profiles.isEmpty()) {
Profile profile = this.profiles.poll();
// 判断是否是默认profile
if (isDefaultProfile(profile)) {
addProfileToEnvironment(profile.getName());
}
// 重点:加载该环境的配置文件
load(profile, this::getPositiveProfileFilter,
addToLoaded(MutablePropertySources::addLast, false));
this.processedProfiles.add(profile);
}
load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true));
addLoadedPropertySources();
applyActiveProfiles(defaultProperties);
}
);
}
private void initializeProfiles() {
// The default profile for these purposes is represented as null. We add it
// first so that it is processed first and has lowest priority.
this.profiles.add(null);
// ACTIVE_PROFILES_PROPERTY常量的值为"spring.profiles.active"
// 从environment中取key为"spring.profiles.active"的值,若未找到返回空集合
SetactivatedViaProperty = getProfilesFromProperty(ACTIVE_PROFILES_PROPERTY);
// INCLUDE_PROFILES_PROPERTY常量的值为"spring.profiles.include"
// 从environment中取key为"spring.profiles.include"的值,若未找到返回空集合
SetincludedViaProperty = getProfilesFromProperty(INCLUDE_PROFILES_PROPERTY);
// 从environment中找到profile名称不在activatedViaProperty及includedViaProperty集合内的已激活的profile的名称
ListotherActiveProfiles = getOtherActiveProfiles(activatedViaProperty, includedViaProperty);
this.profiles.addAll(otherActiveProfiles);
// Any pre-existing active profiles set via property sources (e.g.
// System properties) take precedence over those added in config files.
this.profiles.addAll(includedViaProperty);
addActiveProfiles(activatedViaProperty);
// 如果用户如果自己通过命令行参数、jvm系统属性、系统环境变量指定了"spring.profiles.active"或"spring.profiles.include"属性值
// 则不使用默认的profile,否则表明用户没有明确指定启用哪些profile,那么就使用spring默认的profile,用户设置可以覆盖spring默认设置
if (this.profiles.size() == 1) { // profiles集合中只包含1个null元素
// 获取默认profile的名称,this.environment.getDefaultProfiles()方法默认返回字符串"default"
for (String defaultProfileName : this.environment.getDefaultProfiles()) {
// 创建name为"default"的Profile对象
Profile defaultProfile = new Profile(defaultProfileName, true);
// 将默认profile加到profiles中,此时profiles集合变成 [ null, 名称为"default"的Profile对象 ]
this.profiles.add(defaultProfile);
}
}
}
private SetgetProfilesFromProperty(String profilesProperty) {
if (!this.environment.containsProperty(profilesProperty)) {
return Collections.emptySet();
}
Binder binder = Binder.get(this.environment);
Setprofiles = getProfiles(binder, profilesProperty);
return new LinkedHashSet<>(profiles);
}
private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
// 确定从哪些位置搜索配置文件,默认从["file:./config/", "file:./", "classpath:/config/", "classpath:/"]搜索配置文件
getSearchLocations()
// 遍历每个搜索路径
.forEach((location) -> {
// 判断搜索路径是否是以"/"结尾的,如果是说明指定的目录,否则说明指定的搜索路径精确到具体文件名了
boolean isFolder = location.endsWith("/");
// NO_SEARCH_NAMES集合默认只有1个null元素
// 如果location路径是文件夹,在用户没有明确指定"spring.config.name"属性的情况下,
// 对于spring boot返回的是["application"],对于spring cloud返回的是["bootstrap"]
Setnames = isFolder ? getSearchNames() : NO_SEARCH_NAMES;
// 重点:根据names循环加载可能的配置文件
names.forEach((name) -> load(location, name, profile, filterFactory, consumer));
});
}
private void load(String location, String name, Profile profile, DocumentFilterFactory filterFactory,
DocumentConsumer consumer) {
// 判断name是否有值
if (!StringUtils.hasText(name)) {
for (PropertySourceLoader loader : this.propertySourceLoaders) {
if (canLoadFileExtension(loader, location)) {
load(loader, location, profile, filterFactory.getDocumentFilter(profile), consumer);
return;
}
}
throw new IllegalStateException("File extension of config file location '" + location
+ "' is not known to any PropertySourceLoader. If the location is meant to reference "
+ "a directory, it must end in '/'");
}
Setprocessed = new HashSet<>();
// this.propertySourceLoaders的值是在Loader类的构造方法中通过
// SpringFactoriesLoader.loadFactories(PropertySourceLoader.class, getClass().getClassLoader())初始化的。
// this.propertySourceLoaders集合属性值按照顺序包括:
// org.springframework.boot.env.PropertiesPropertySourceLoader
// org.springframework.boot.env.YamlPropertySourceLoader
for (PropertySourceLoader loader : this.propertySourceLoaders) {
// loader.getFileExtensions()方法返回值分如下2种情况:
// 对于org.springframework.boot.env.PropertiesPropertySourceLoader返回 ["properties", "xml"]
// 对于org.springframework.boot.env.YamlPropertySourceLoader返回 ["yml", "yaml"]
for (String fileExtension : loader.getFileExtensions()) {
if (processed.add(fileExtension)) {
// 核心方法:加载spring boot、spring cloud的配置文件
loadForFileExtension(loader, location + name, "." + fileExtension, profile, filterFactory,
consumer);
}
}
}
}
/**
*
* @param loader 属性加载器,PropertiesPropertySourceLoader或YamlPropertySourceLoader
* @param prefix 不包括扩展名的路径,例如"file:./config/bootstrap"、"classpath:./config/application"、"classpath:/application"等
* @param fileExtension 文件扩展名,例如".yml"、".yaml"、".properties"、".xml"
* @param profile 启用的环境
*
*/
private void loadForFileExtension(PropertySourceLoader loader, String prefix, String fileExtension,
Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
DocumentFilter defaultFilter = filterFactory.getDocumentFilter(null);
DocumentFilter profileFilter = filterFactory.getDocumentFilter(profile);
if (profile != null) {
// 加载特定环境的配置文件
// 例如"file:./config/bootstrap-default.yml" 、"classpath:/application-default.yml"
String profileSpecificFile = prefix + "-" + profile + fileExtension;
load(loader, profileSpecificFile, profile, defaultFilter, consumer);
load(loader, profileSpecificFile, profile, profileFilter, consumer);
// Try profile specific sections in files we've already processed
for (Profile processedProfile : this.processedProfiles) {
if (processedProfile != null) {
String previouslyLoaded = prefix + "-" + processedProfile + fileExtension;
load(loader, previouslyLoaded, profile, profileFilter, consumer);
}
}
}
// 加载常规配置文件
// 例如"file:./config/bootstrap.yml" 、"classpath:/application.yml"
load(loader, prefix + fileExtension, profile, profileFilter, consumer);
}
private void load(PropertySourceLoader loader, String location, Profile profile, DocumentFilter filter,
DocumentConsumer consumer) {
try {
// 真正加载配置文件了
Resource resource = this.resourceLoader.getResource(location);
if (resource == null || !resource.exists()) {
if (this.logger.isTraceEnabled()) {
StringBuilder description = getDescription("Skipped missing config ", location, resource,
profile);
this.logger.trace(description);
}
return;
}
if (!StringUtils.hasText(StringUtils.getFilenameExtension(resource.getFilename()))) {
if (this.logger.isTraceEnabled()) {
StringBuilder description = getDescription("Skipped empty config extension ", location,
resource, profile);
this.logger.trace(description);
}
return;
}
String name = "applicationConfig: [" + location + "]";
Listdocuments = loadDocuments(loader, name, resource);
if (CollectionUtils.isEmpty(documents)) {
if (this.logger.isTraceEnabled()) {
StringBuilder description = getDescription("Skipped unloaded config ", location, resource,
profile);
this.logger.trace(description);
}
return;
}
Listloaded = new ArrayList<>();
for (Document document : documents) {
if (filter.match(document)) {
addActiveProfiles(document.getActiveProfiles());
addIncludedProfiles(document.getIncludeProfiles());
loaded.add(document);
}
}
Collections.reverse(loaded);
if (!loaded.isEmpty()) {
loaded.forEach((document) -> consumer.accept(profile, document));
if (this.logger.isDebugEnabled()) {
StringBuilder description = getDescription("Loaded config file ", location, resource, profile);
this.logger.debug(description);
}
}
}
catch (Exception ex) {
throw new IllegalStateException("Failed to load property source from location '" + location + "'", ex);
}
}
// 确定从哪些位置搜索配置文件
private SetgetSearchLocations() {
// CONFIG_LOCATION_PROPERTY常量值为"spring.config.location"
// 这里的意图是判断用户是否通过命令行参数、系统属性、系统环境变量指定了"spring.config.location"属性值
// 如果指定了"spring.config.location"属性值则使用用户指定的配置文件路径
if (this.environment.containsProperty(CONFIG_LOCATION_PROPERTY)) {
return getSearchLocations(CONFIG_LOCATION_PROPERTY);
}
// CONFIG_ADDITIONAL_LOCATION_PROPERTY常量值为"spring.config.additional-location"
// 这里的意图是判断命令行参数、系统属性、系统环境变量是否指定了"spring.config.additional-location"属性值
// 如果指定了说明除了要解析默认的配置文件路径外,还要额外解析指定位置的配置文件,这就是additional-location所表达的意思
Setlocations = getSearchLocations(CONFIG_ADDITIONAL_LOCATION_PROPERTY);
// DEFAULT_SEARCH_LOCATIONS常量值为"classpath:/,classpath:/config/,file:./,file:./config/"
locations.addAll(
// ConfigFileApplicationListener.this.searchLocations默认为null
// asResolvedSet返回值为集合["file:./config/", "file:./", "classpath:/config/", "classpath:/"]
asResolvedSet(ConfigFileApplicationListener.this.searchLocations, DEFAULT_SEARCH_LOCATIONS));
return locations;
}
private SetgetSearchLocations(String propertyName) {
Setlocations = new LinkedHashSet<>();
// 判断environment环境中是否包含指定属性
if (this.environment.containsProperty(propertyName)) {
for (String path : asResolvedSet(this.environment.getProperty(propertyName), null)) {
if (!path.contains("$")) {
path = StringUtils.cleanPath(path);
if (!ResourceUtils.isUrl(path)) {
// ResourceUtils.FILE_URL_PREFIX常量值为"file:"
path = ResourceUtils.FILE_URL_PREFIX + path;
}
}
locations.add(path);
}
}
return locations;
}
// 确定配置文件的名称
private SetgetSearchNames() {
// CONFIG_NAME_PROPERTY常量值为"spring.config.name"
// 这里的意图是判断用户是否通过命令行参数、系统属性、系统环境变量指定了"spring.config.name"属性
// 特别注意spring cloud的BootstrapApplicationListener中的bootstrapServiceContext()方法中,
// 创建的environment添加了1个名称为bootstrap的属性源,该属性源中就指定了"spring.config.name"属性值为"bootstrap"
if (this.environment.containsProperty(CONFIG_NAME_PROPERTY)) {
// 用户指定了"spring.config.name"属性,获取属性值
String property = this.environment.getProperty(CONFIG_NAME_PROPERTY);
// 对属性值以逗号分隔,返回倒序集合,例如用户通过命令行参数指定属性-D"spring.config.name=application.yml,config.yml"
// 则返回["config.yml", "application.yml"]
return asResolvedSet(property, null);
}
// DEFAULT_NAMES常量值为"application", ConfigFileApplicationListener.this.names属性默认值为null
// 运行到这里说明用户没有指定"spring.config.name"属性,那么使用默认配置文件名称"application"
return asResolvedSet(ConfigFileApplicationListener.this.names, DEFAULT_NAMES);
}
/**
* 如果value为空则使用后备值fallback,否则从environment中解析value中的占位符,然后按逗号分隔并反转
* 例如:value为null, fallback为"classpath:/,classpath:/config/,file:./,file:./config/",则返回
* 集合["file:./config/", "file:./", "classpath:/config/", "classpath:/"]
*/
private SetasResolvedSet(String value, String fallback) {
// 以逗号分隔的字符串分割转集合
Listlist = Arrays.asList(StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(
(value != null) ? this.environment.resolvePlaceholders(value) : fallback)));
// 对集合反转
Collections.reverse(list);
return new LinkedHashSet<>(list);
}
}
}FilteredPropertySource
源码java1
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
61package org.springframework.boot.context.config;
import java.util.Set;
import java.util.function.Consumer;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
/**
* Internal {@link PropertySource} implementation used by
* {@link ConfigFileApplicationListener} to filter out properties for specific operations.
*
* @author Phillip Webb
*/
class FilteredPropertySource extends PropertySource<PropertySource> {
private final SetfilteredProperties;
FilteredPropertySource(PropertySource original, SetfilteredProperties) {
super(original.getName(), original);
this.filteredProperties = filteredProperties;
}
public Object getProperty(String name) {
if (this.filteredProperties.contains(name)) {
return null;
}
return getSource().getProperty(name);
}
/**
*
* @parma environment spring boot/cloud的环境对象,包含了springApplicationCommandLineArgs、
* configurationProperties、【bootstrap】、systemProperties、systemEnvironment属性源
*
* @param propertySourceName 字符串"defaultProperties"
* @parm filteredProperties 集合["spring.profiles.active", "spring.profiles.include"]
* @param operation 消费者
*
*/
static void apply(ConfigurableEnvironment environment, String propertySourceName, SetfilteredProperties,
Consumer> operation) {
MutablePropertySources propertySources = environment.getPropertySources();
// 获取name为"defaultProperties"的属性源
PropertySource original = propertySources.get(propertySourceName);
if (original == null) {
operation.accept(null);
return;
}
propertySources.replace(propertySourceName, new FilteredPropertySource(original, filteredProperties));
try {
operation.accept(original);
}
finally {
propertySources.replace(propertySourceName, original);
}
}
}
DebugAgentEnvironmentPostProcessor
3.6.5、绑定ConfigurableEnvironment到当前的SpringApplication实例中
1 | bindToSpringApplication(environment); |
bindToSpringApplication()方法的具体源码如下:
1 | protected void bindToSpringApplication(ConfigurableEnvironment environment) { |
3.7、configureIgnoreBeanInfo(environment)
1 | private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) { |
该方法好像不太重要,不深究它。
3.8、创建并打印Banner
1 | Banner printedBanner = printBanner(environment); |
这是用来打印 Banner 的处理类,这个没什么好说的。
3.9、创建ApplicationContext
1 | context = createApplicationContext(); |
3.9.1、ApplicationContext的创建
来看下 createApplicationContext()
方法的源码:
1 | public class SpringApplication { |
其实就是如果指定了applicationContextClass则使用指定的applicationContextClass创建ConfigurableApplicationContext,如果没有明确设置applicationContextClass,则根据不同的应用类型初始化不同的上下文应用类,可以看到createApplicationContext()方法返回了ConfigurableApplicationContext。
那么这个ConfigurableApplicationContext又是什么?
查看ConfigurableApplicationContext的源码,可以发现它是个接口,又继承了ApplicationContext接口,下面先看ApplicationContext接口的源码。
3.9.2、ApplicationContext接口
1 | /** |
3.9.3、ConfigurableApplicationContext
1 | /** |
3.10、准备异常报告器
1 | exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, |
逻辑和之前实例化ApplicationContextInitializer和ApplicationListener的一样,一样调用的是 getSpringFactoriesInstances
方法来获取配置的异常类名称并实例化所有的异常处理类。
该异常报告处理类配置在 spring-boot-2.2.1.RELEASE.jar!/META-INF/spring.factories
这个配置文件里面。
1 | # Error Reporters |
3.11、准备ApplicationContext
1 | prepareContext(context, environment, listeners, applicationArguments, printedBanner); |
来看下 prepareContext()
方法的源码:
1 | public class SpringApplication { |
下面具体分析每一步
3.11.1、将ConfigurationEnvironment存到ConfigurationApplicationContext中
1 | // 将environment保存到上一步创建的ConfigurableApplicationContext中 |
3.11.2、ConfigurableApplicationContext创建后的后置处理逻辑
1 | // ConfigurableApplicationContext创建后的后置处理逻辑 |
postProcessApplicationContext方法的源码如下:
1 | public class SpringApplication { |
3.11.3、回调ApplicationContextInitializer.initialize(context)
1 | applyInitializers(context); |
applyInitializers(context)的源码如下:
1 | public class SpringApplication { |
这里的getInitializers()方法返回的是当前类中的initializer
属性,而initializer
属性是在SpringApplication
的构造函数中加载的所有spring.factories
中key为org.springframework.context.ApplicationContextInitializer
的实现类,具体的ApplicationContextInitializer
的加载源码参见前文第2小节。
springorg.springframework.context.ApplicationContextInitializer
的实现类出处如下:
spring-boot-2.2.1.RELEASE.jar!\META-INF\spring.factories
properties1
2
3
4
5
6
7# Application Context Initializers
\ =
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializerspring-boot-autoconfigure-2.2.1.RELEASE.jar!\META-INF\spring.factories
共2个properties1
2
3
4# Initializers
\ =
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
3.11.4、发布ApplicationContextInitializedEvent事件
1 | listeners.contextPrepared(context); |
SpringApplicationRunListeners
类的contextPrepared方法源码如下:
1 | class SpringApplicationRunListeners { |
SpringApplicationRunListener
的实现类只有EventPublishingRunListener
,contextPrepared方法的源码如下:
1 | public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered { |
3.11.5、打印启动信息
1 | if (this.logStartupInfo) { |
3.12、刷新ApplicationContext
1 | refreshContext(context); |
这个主要是刷新 Spring 的应用上下文,源码如下:
1 | private void refreshContext(ConfigurableApplicationContext context) { |
3.13、ApplicationContext刷新后置处理
1 | afterRefresh(context, applicationArguments); |
这个方法的源码是空的,目前可以做一些自定义的后置处理操作。
1 | protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) { |
3.14、停止StopWatch
1 | stopWatch.stop(); |
1 | public void stop() throws IllegalStateException { |
计时监听器停止,并统计一些任务执行信息。
3.15、输出日志记录执行主类名、时间信息
1 | if (this.logStartupInfo) { |
3.16、发布ApplicationStartedEvent事件
1 | listeners.started(context); |
触发所有 SpringApplicationRunListener 监听器的 started 事件方法。
1 | class SpringApplicationRunListeners { |
SpringApplicationRunListener
的实现类只有EventPublishingRunListener
,started源码如下:
1 | public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered { |
3.17、执行所有 Runner 运行器
1 | callRunners(context, applicationArguments); |
callRunners方法源码如下:
1 | private void callRunners(ApplicationContext context, ApplicationArguments args) { |
执行所有 ApplicationRunner
和 CommandLineRunner
这两种运行器。
3.18、发布ApplicationReadyEvent事件
1 | try { |
触发所有 SpringApplicationRunListener 监听器的 running 事件方法。
1 | class SpringApplicationRunListeners { |
SpringApplicationRunListener
的实现类只有EventPublishingRunListener
,running源码如下:
1 | public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered { |
3.19、返回应用上下文
1 | return context; |