You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
source-code-hunter/docs/SpringBoot/SpringBoot-application-load.md

281 lines
7.5 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# Spring Boot application 文件加载
- Author: [HuiFer](https://github.com/huifer)
- 源码阅读仓库: [SourceHot-spring-boot](https://github.com/SourceHot/spring-boot-read)
## 如何找到这个加载的过程
1. 创建配置文件`application.yml`
2. 全局搜索yml
![image-20200319083048849](../../../images/SpringBoot/image-20200319083048849.png)
3. 换成`properties`搜索
![image-20200319083140225](../../../images/SpringBoot/image-20200319083140225.png)
4. 我们以`yml`为例打上断点开始源码追踪
看到调用堆栈
![image-20200319083345067](../../../images/SpringBoot/image-20200319083345067.png)
- 一步一步回上去看如何调用具体方法的
## ConfigFileApplicationListener
- 配置文件监听器
### 调用过程
![image-20200319082131146](../../../images/SpringBoot/image-20200319082131146.png)
![image-20200319082544653](../../../images/SpringBoot/image-20200319082544653.png)
`org.springframework.boot.context.config.ConfigFileApplicationListener#addPropertySources`
```java
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
RandomValuePropertySource.addToEnvironment(environment);
// 加载器加载信息
new Loader(environment, resourceLoader).load();
}
```
### Loader
- 配置资源加载器
构造方法
```java
Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
// 环境配置
this.environment = environment;
// 占位符处理器
this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(this.environment);
// 资源加载器
this.resourceLoader = (resourceLoader != null) ? resourceLoader : new DefaultResourceLoader();
// 配置信息加载器初始化
this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,
getClass().getClassLoader());
}
```
- 熟悉的老朋友`this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class, getClass().getClassLoader())` 看看**`spring.factories`**有什么
- 搜索目标: `org.springframework.boot.env.PropertySourceLoader`
![image-20200319084141748](../../../images/SpringBoot/image-20200319084141748.png)
![image-20200319084151997](../../../images/SpringBoot/image-20200319084151997.png)
观察发现里面有一个`YamlPropertySourceLoader`和我们之前找yml字符串的时候找到的类是一样的。说明搜索方式没有什么问题。
![image-20200319084357652](../../../images/SpringBoot/image-20200319084357652.png)
初始化完成,后续进行解析了
### load 方法
```java
void load() {
FilteredPropertySource.apply(this.environment, DEFAULT_PROPERTIES, LOAD_FILTERED_PROPERTY,
(defaultProperties) -> {
this.profiles = new LinkedList<>();
this.processedProfiles = new LinkedList<>();
this.activatedProfiles = false;
this.loaded = new LinkedHashMap<>();
// 初始化配置文件
initializeProfiles();
while (!this.profiles.isEmpty()) {
Profile profile = this.profiles.poll();
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);
});
}
```
### initializeProfiles
- 初始化`private Deque<Profile> profiles;` 属性
- ![image-20200319084902957](../../../images/SpringBoot/image-20200319084902957.png)
### load
- `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)`
```JAVA
private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
getSearchLocations().forEach(
// 本地路径
(location) -> {
// 是不是文件夹
boolean isFolder = location.endsWith("/");
// 文件名,默认application
Set<String> names = isFolder ? getSearchNames() : NO_SEARCH_NAMES;
// 循环加载
names.forEach((name) -> {
load(location, name, profile, filterFactory, consumer);
});
});
}
```
- 资源路径可能性
![image-20200319085446640](../../../images/SpringBoot/image-20200319085446640.png)
该方法采用循环每个路径下面都去尝试一遍
- 中间过程省略,我们直接看最后的加载行为
- `org.springframework.boot.context.config.ConfigFileApplicationListener.Loader#loadDocuments`
```java
private List<Document> loadDocuments(PropertySourceLoader loader, String name, Resource resource)
throws IOException {
// 文档的缓存key
DocumentsCacheKey cacheKey = new DocumentsCacheKey(loader, resource);
// 文档信息
List<Document> documents = this.loadDocumentsCache.get(cacheKey);
if (documents == null) {
// 执行加载,将配置文件读取返回
List<PropertySource<?>> loaded = loader.load(name, resource);
// 数据转换
documents = asDocuments(loaded);
// 缓存设置
this.loadDocumentsCache.put(cacheKey, documents);
}
return documents;
}
```
此处的`loader.load()`调用具体的loader实现类进行执行方法
### yml 解析
```java
@Override
public List<PropertySource<?>> load(String name, Resource resource) throws IOException {
if (!ClassUtils.isPresent("org.yaml.snakeyaml.Yaml", null)) {
throw new IllegalStateException(
"Attempted to load " + name + " but snakeyaml was not found on the classpath");
}
// 将资源转换成集合对象
List<Map<String, Object>> loaded = new OriginTrackedYamlLoader(resource).load();
if (loaded.isEmpty()) {
return Collections.emptyList();
}
List<PropertySource<?>> propertySources = new ArrayList<>(loaded.size());
for (int i = 0; i < loaded.size(); i++) {
String documentNumber = (loaded.size() != 1) ? " (document #" + i + ")" : "";
// 放入返回结果中
propertySources.add(new OriginTrackedMapPropertySource(name + documentNumber,
Collections.unmodifiableMap(loaded.get(i)), true));
}
return propertySources;
}
```
![image-20200319090446231](../../../images/SpringBoot/image-20200319090446231.png)
- `PropertiesPropertySourceLoader`解析同理不在次展开描述了
### asDocuments
```JAVA
/**
* 将 {@link PropertySource} 转换成 {@link Document}
* @param loaded
* @return
*/
private List<Document> asDocuments(List<PropertySource<?>> loaded) {
if (loaded == null) {
return Collections.emptyList();
}
return loaded.stream().map(
// 循环创建新对象
(propertySource) -> {
// 对象创建
Binder binder = new Binder(ConfigurationPropertySources.from(propertySource),
this.placeholdersResolver);
/**
* 通过 {@link Binder} 将数据进行绑定,创建 {@link Document}进行返回
*/
return new Document(propertySource, binder.bind("spring.profiles", STRING_ARRAY).orElse(null),
getProfiles(binder, ACTIVE_PROFILES_PROPERTY),
getProfiles(binder, INCLUDE_PROFILES_PROPERTY));
}).collect(Collectors.toList());
}
```
-----