Skip to content

Commit cd37def

Browse files
committed
SpringBoot 配置文件加载分析
1 parent 56e1c71 commit cd37def

13 files changed

Lines changed: 281 additions & 1 deletion

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969

7070
### SpringBoot
7171
- [SpringBoot run方法解析](/docs/SpringBoot/Spring-Boot-Run.md)
72-
72+
- [SpringBoot 配置加载解析](/docs/SpringBoot/SpringBoot-application-load.md)
7373

7474
## MyBatis
7575
### 基础支持层
Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
# Spring Boot application 文件加载
2+
- Author: [HuiFer](https://github.com/huifer)
3+
- 源码阅读仓库: [SourceHot-spring-boot](https://github.com/SourceHot/spring-boot-read)
4+
5+
6+
7+
8+
9+
## 如何找到这个加载的过程
10+
11+
1. 创建配置文件`application.yml`
12+
13+
2. 全局搜索yml
14+
15+
![image-20200319083048849](/images/SpringBoot/image-20200319083048849.png)
16+
17+
3. 换成`properties`搜索
18+
19+
![image-20200319083140225](/images/SpringBoot/image-20200319083140225.png)
20+
21+
4. 我们以`yml`为例打上断点开始源码追踪
22+
23+
24+
25+
26+
27+
看到调用堆栈
28+
29+
![image-20200319083345067](/images/SpringBoot/image-20200319083345067.png)
30+
31+
32+
33+
- 一步一步回上去看如何调用具体方法的
34+
35+
## ConfigFileApplicationListener
36+
37+
- 配置文件监听器
38+
39+
40+
41+
### 调用过程
42+
43+
![image-20200319082131146](/images/SpringBoot/image-20200319082131146.png)
44+
45+
![image-20200319082544653](/images/SpringBoot/image-20200319082544653.png)
46+
47+
48+
49+
`org.springframework.boot.context.config.ConfigFileApplicationListener#addPropertySources`
50+
51+
```java
52+
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
53+
RandomValuePropertySource.addToEnvironment(environment);
54+
// 加载器加载信息
55+
new Loader(environment, resourceLoader).load();
56+
}
57+
```
58+
59+
60+
61+
62+
63+
64+
65+
### Loader
66+
67+
- 配置资源加载器
68+
69+
70+
71+
构造方法
72+
73+
```java
74+
Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
75+
// 环境配置
76+
this.environment = environment;
77+
// 占位符处理器
78+
this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(this.environment);
79+
// 资源加载器
80+
this.resourceLoader = (resourceLoader != null) ? resourceLoader : new DefaultResourceLoader();
81+
// 配置信息加载器初始化
82+
this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,
83+
getClass().getClassLoader());
84+
}
85+
86+
```
87+
88+
89+
90+
- 熟悉的老朋友`this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class, getClass().getClassLoader())`, 看看**`spring.factories`**有什么
91+
92+
- 搜索目标: `org.springframework.boot.env.PropertySourceLoader`
93+
94+
![image-20200319084141748](/images/SpringBoot/image-20200319084141748.png)
95+
96+
97+
98+
![image-20200319084151997](/images/SpringBoot/image-20200319084151997.png)
99+
100+
观察发现里面有一个`YamlPropertySourceLoader`和我们之前找yml字符串的时候找到的类是一样的。说明搜索方式没有什么问题。
101+
102+
![image-20200319084357652](/images/SpringBoot/image-20200319084357652.png)
103+
104+
初始化完成,后续进行解析了
105+
106+
107+
108+
109+
110+
### load 方法
111+
112+
```java
113+
void load() {
114+
FilteredPropertySource.apply(this.environment, DEFAULT_PROPERTIES, LOAD_FILTERED_PROPERTY,
115+
(defaultProperties) -> {
116+
this.profiles = new LinkedList<>();
117+
this.processedProfiles = new LinkedList<>();
118+
this.activatedProfiles = false;
119+
this.loaded = new LinkedHashMap<>();
120+
// 初始化配置文件
121+
initializeProfiles();
122+
while (!this.profiles.isEmpty()) {
123+
Profile profile = this.profiles.poll();
124+
if (isDefaultProfile(profile)) {
125+
addProfileToEnvironment(profile.getName());
126+
}
127+
load(profile, this::getPositiveProfileFilter,
128+
addToLoaded(MutablePropertySources::addLast, false));
129+
this.processedProfiles.add(profile);
130+
}
131+
load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true));
132+
addLoadedPropertySources();
133+
applyActiveProfiles(defaultProperties);
134+
});
135+
}
136+
137+
```
138+
139+
### initializeProfiles
140+
141+
- 初始化`private Deque<Profile> profiles;` 属性
142+
- ![image-20200319084902957](/images/SpringBoot/image-20200319084902957.png)
143+
144+
145+
146+
147+
148+
### load
149+
150+
- `org.springframework.boot.context.config.ConfigFileApplicationListener.Loader#load(org.springframework.boot.context.config.ConfigFileApplicationListener.Profile, org.springframework.boot.context.config.ConfigFileApplicationListener.DocumentFilterFactory, org.springframework.boot.context.config.ConfigFileApplicationListener.DocumentConsumer)`
151+
152+
153+
154+
```JAVA
155+
private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
156+
getSearchLocations().forEach(
157+
// 本地路径
158+
(location) -> {
159+
// 是不是文件夹
160+
boolean isFolder = location.endsWith("/");
161+
// 文件名,默认application
162+
Set<String> names = isFolder ? getSearchNames() : NO_SEARCH_NAMES;
163+
// 循环加载
164+
names.forEach((name) -> {
165+
load(location, name, profile, filterFactory, consumer);
166+
});
167+
});
168+
}
169+
```
170+
171+
- 资源路径可能性
172+
173+
![image-20200319085446640](/images/SpringBoot/image-20200319085446640.png)
174+
175+
176+
177+
该方法采用循环每个路径下面都去尝试一遍
178+
179+
180+
181+
- 中间过程省略,我们直接看最后的加载行为
182+
- `org.springframework.boot.context.config.ConfigFileApplicationListener.Loader#loadDocuments`
183+
184+
```java
185+
private List<Document> loadDocuments(PropertySourceLoader loader, String name, Resource resource)
186+
throws IOException {
187+
// 文档的缓存key
188+
DocumentsCacheKey cacheKey = new DocumentsCacheKey(loader, resource);
189+
// 文档信息
190+
List<Document> documents = this.loadDocumentsCache.get(cacheKey);
191+
if (documents == null) {
192+
// 执行加载,将配置文件读取返回
193+
List<PropertySource<?>> loaded = loader.load(name, resource);
194+
// 数据转换
195+
documents = asDocuments(loaded);
196+
// 缓存设置
197+
this.loadDocumentsCache.put(cacheKey, documents);
198+
}
199+
return documents;
200+
}
201+
202+
```
203+
204+
205+
206+
此处的`loader.load()`调用具体的loader实现类进行执行方法
207+
208+
209+
210+
211+
212+
### yml 解析
213+
214+
```java
215+
@Override
216+
public List<PropertySource<?>> load(String name, Resource resource) throws IOException {
217+
if (!ClassUtils.isPresent("org.yaml.snakeyaml.Yaml", null)) {
218+
throw new IllegalStateException(
219+
"Attempted to load " + name + " but snakeyaml was not found on the classpath");
220+
}
221+
// 将资源转换成集合对象
222+
List<Map<String, Object>> loaded = new OriginTrackedYamlLoader(resource).load();
223+
if (loaded.isEmpty()) {
224+
return Collections.emptyList();
225+
}
226+
List<PropertySource<?>> propertySources = new ArrayList<>(loaded.size());
227+
for (int i = 0; i < loaded.size(); i++) {
228+
String documentNumber = (loaded.size() != 1) ? " (document #" + i + ")" : "";
229+
// 放入返回结果中
230+
propertySources.add(new OriginTrackedMapPropertySource(name + documentNumber,
231+
Collections.unmodifiableMap(loaded.get(i)), true));
232+
}
233+
return propertySources;
234+
}
235+
236+
```
237+
238+
239+
240+
![image-20200319090446231](/images/SpringBoot/image-20200319090446231.png)
241+
242+
243+
244+
245+
246+
- `PropertiesPropertySourceLoader`解析同理不在次展开描述了
247+
248+
249+
250+
### asDocuments
251+
252+
```JAVA
253+
/**
254+
* 将 {@link PropertySource} 转换成 {@link Document}
255+
* @param loaded
256+
* @return
257+
*/
258+
private List<Document> asDocuments(List<PropertySource<?>> loaded) {
259+
if (loaded == null) {
260+
return Collections.emptyList();
261+
}
262+
return loaded.stream().map(
263+
// 循环创建新对象
264+
(propertySource) -> {
265+
// 对象创建
266+
Binder binder = new Binder(ConfigurationPropertySources.from(propertySource),
267+
this.placeholdersResolver);
268+
/**
269+
* 通过 {@link Binder} 将数据进行绑定,创建 {@link Document}进行返回
270+
*/
271+
return new Document(propertySource, binder.bind("spring.profiles", STRING_ARRAY).orElse(null),
272+
getProfiles(binder, ACTIVE_PROFILES_PROPERTY),
273+
getProfiles(binder, INCLUDE_PROFILES_PROPERTY));
274+
}).collect(Collectors.toList());
275+
}
276+
277+
```
278+
279+
-----
280+
47.1 KB
Loading
44.2 KB
Loading
54.8 KB
Loading
70.8 KB
Loading
97.3 KB
Loading
46.6 KB
Loading
16.6 KB
Loading
27.2 KB
Loading

0 commit comments

Comments
 (0)