監聽器:ConfigFileApplicationListener、BootstrapApplicationListener
1、系統啟動時,調用SpringApplication.run()
2、初始化監聽器
//初始化SpringApplicationRunListeners,並且初始化變量listeners,將EventPublishingRunListener存放與listeners中
1)SpringApplicationRunListeners listeners = this.getRunListeners(args);
2)
3)SpringFactoriesLoader.loadSpringFactories()加載META-INF/spring.factories下的配置監聽器
3、調用ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);內部調用步驟如下
1)listeners.environmentPrepared((ConfigurableEnvironment)environment);
監聽器的准備工作
2)listener.environmentPrepared(environment);
循環listeners中的監聽器,執行environmentPrepared方法,其中加載配置文件的監聽器就在循環列表中
3)執行EventPublishingRunListener中的environmentPrepared()方法
public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
}
4)SimpleApplicationEventMulticaster類中的doInvokeListener()方法
5)執行ConfigFiledApplicationListerner類中的onApplicationEvent(event)方法
6)執行ConfigFileApplicationListener中的onApplicationEnviromentPrepareEvent()方法
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
List<EnvironmentPostProcessor> postProcessors = this.loadPostProcessors();
postProcessors.add(this);
AnnotationAwareOrderComparator.sort(postProcessors);
Iterator var3 = postProcessors.iterator();
while(var3.hasNext()) {
EnvironmentPostProcessor postProcessor = (EnvironmentPostProcessor)var3.next();
postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
}
}
注意:AnnotationAwareOrderComparator.sort(postProcessors); 對List<EnvironmentPostProcessor>集合進行排序,排序規則按照spring中的注解@Order注解和@Priority注解排序
7)迭代postPrccessors集合,並且執行集合中的postProcessEnvironment方法,重點看ConfigFileApplicationListener類中的postProcessEnvironment方法
其中會添加一個屬性源進來ResourceLoader
8)調用load方法加載屬性
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
RandomValuePropertySource.addToEnvironment(environment);
(new ConfigFileApplicationListener.Loader(environment, resourceLoader)).load();
}
9) load方法中調用apply方法
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);
});
}
10) apply方法默認獲取defaultProperties屬性,如果獲取為空,則從上一步中執行函數式編程中的accept方法,下邊移步到accept方法一探究竟,首先apply方法中會執行一個初始化方法initializeProfiles,方法中會向profiles中放入null,然后再放入一個"default",用來從外部遍歷,遍歷為null的時候,加載springboot的默認配置,遍歷為“default"時,加載用戶配置,從而充分體現了約定大於配置
11)遍歷profiles集合,遍歷的第一次為null,執行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));
});
}
首先執行getSearchLocations方法,獲取默認的路徑,默認的路徑為如果啟動參數中有"spring.config.location",則加載spring.config.location中指定的配置文件,否則用默認的DEFAULT_SEARCH_LOCATIONS,
classpath:/,classpath:/config/,file:./,file:./config/*/,file:./config/
12)取出Locations為classpath:/,classpath:/config/,file:./,file:./config/*/,file:./config/
遍歷set,按照先后順序取配置文件
13)獲取配置文件名字,如果配置spring.confing.name的話,則去配置的名字,否則用默認的"application"
14)取到location和name之后,執行load(String location, String name, Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer)方法,加載配置,如下圖
15 最終通過以鈎子程序的形式調用函數式編程完成屬性的賦值
調用Map中的computeIfAbsent,如果不存在profile的key,則添加新的數據,並將value值返回