参考:Springboot源码之application.yaml读取过程
当SpringBoot版本<2.4.0时:
SpringBoot配置文件一般为application.yml或application.properties等,其加载流程在SpringApplication 的run() 方法中的ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); 中完成,当环境准备好会触发EventPublishingRunListener implement SpringApplicationRunListener 的 environmentPrepared() 方法,该方法广播ApplicationEnvironmentPreparedEvent ,该事件有一个ConfigFileApplicationListener implements EnvironmentPostProcessor, SmartApplicationListener, Ordered 监听器,该类用于加载配置文件。以上具体的调用过程如下: 具体看看ConfigFileApplicationListener 这个类如何加载配置文件的:
public class ConfigFileApplicationListener implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {
...
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
}
if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent(event);
}
}
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
postProcessors.add(this);
AnnotationAwareOrderComparator.sort(postProcessors);
for (EnvironmentPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
}
}
...
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
addPropertySources(environment, application.getResourceLoader());
}
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
RandomValuePropertySource.addToEnvironment(environment);
new Loader(environment, resourceLoader).load();
}
}
private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
getSearchLocations().forEach((location) -> {
boolean isDirectory = location.endsWith("/");
Set<String> names = isDirectory ? getSearchNames() : NO_SEARCH_NAMES;
names.forEach((name) -> load(location, name, profile, filterFactory, consumer));
});
}
private void load(String location, String name, Profile profile, DocumentFilterFactory filterFactory,
DocumentConsumer consumer) {
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 '/'");
}
Set<String> processed = new HashSet<>();
for (PropertySourceLoader loader : this.propertySourceLoaders) {
for (String fileExtension : loader.getFileExtensions()) {
if (processed.add(fileExtension)) {
loadForFileExtension(loader, location + name, "." + fileExtension, profile, filterFactory,
consumer);
}
}
}
}
当SpringBoot版本>=2.4.0时:
上文中ConfigFileApplicationListener 类于2.4.0版本被废弃,取而代之的是ConfigDataEnvironmentPostProcessor ,通过这两个类的注释也可以看出: 之前加载application.yml or properties文件和springboot是耦合在一个ConfigFileApplicationListener中,也就是说,只有调用springboot的这个监听器才能解析配置文件,而现在SpringBoot这里抽象出了加载文件的过程。
先来看一下org.springframework.boot:spring-boot.jar 包下的
M
E
T
A
?
I
N
F
META-INF
META?INF的spring.factories 文件,需要注意以下三个key:value: 看到下面两个接口属于同一个包,显然是有一定联系的。看看它们的注释:
ConfigDataLocationResolver: Strategy interface used to resolve {@link ConfigDataLocation locations} into one or more {@link ConfigDataResource resources}. ConfigDataLoader: Strategy class that can be used to load {@link ConfigData } for a given{@link ConfigDataResource}.
他们都是策略模式的上层接口,之间的联系如下图: 再看第一个接口:
PropertySourceLoader: to load a {@link PropertySource }.
实际上,在加载springboot配置文件的过程中StandardConfigDataSource extends ConfigDataSource 中通过字段StandardConfigDataReference reference 的字段方法PropertySourceLoader.load() 来实现ConfigDataLoader.load() 。这个StandardConfigDataReference 既有 ConfigDataLocation 也有PropertySourceLoader ,起到了承上启下的reference作用。结构图如下: 至于StandardConfigDataLoader怎么调用的,我就不画图了,流程如下: ConfigDataEnvironmentPostProcessor 的postProcessEnvironment()
→
\rightarrow
→ConfigDataEnvironment 的processAndApply()
→
\rightarrow
→同类中的processInitial()
→
\rightarrow
→ConfigDataEnvironmentContributors 的withProcessedImports()
→
\rightarrow
→ConfigDataImporter 的resolveAndLoad()
→
\rightarrow
→同类的load()
→
\rightarrow
→ConfigDataLoaders 的load()
→
\rightarrow
→StandardConfigDataLoader 的load() 。
|