springboot源码4---配置文件解析-创新互联
- 执行时机
- 解析前工作
- 1.加载EnvironmentPostProcessor
- 2.添加随机数配置源
- 3.SPI加载PropertySourceLoader
- 开始解析
- 1.加载所有profile
- 2.遍历profile解析
- 获取搜索路径和文件名
- 遍历搜索路径下的配置文件
- 解析、加入缓存
- 3.缓存添加到environment
- 4.更新environment的activeProfiles
- 总结
发布environment准备好事件时触发
public class SpringApplication {public ConfigurableApplicationContext run(String... args) {...
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
...
}
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {ConfigurableEnvironment environment = this.getOrCreateEnvironment();
this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach((Environment)environment);
listeners.environmentPrepared((ConfigurableEnvironment)environment);
...
}
}
public class ConfigFileApplicationListener implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {private static final String DEFAULT_PROPERTIES = "defaultProperties";
private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/";
private static final String DEFAULT_NAMES = "application";
public static final String ACTIVE_PROFILES_PROPERTY = "spring.profiles.active";
public static final String INCLUDE_PROFILES_PROPERTY = "spring.profiles.include";
public static final String CONFIG_NAME_PROPERTY = "spring.config.name";
public static final String CONFIG_LOCATION_PROPERTY = "spring.config.location";
public static final String CONFIG_ADDITIONAL_LOCATION_PROPERTY = "spring.config.additional-location";
public void onApplicationEvent(ApplicationEvent event) {if (event instanceof ApplicationEnvironmentPreparedEvent) {this.onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent)event);
}
if (event instanceof ApplicationPreparedEvent) {this.onApplicationPreparedEvent(event);
}
}
}
解析前工作
1.加载EnvironmentPostProcessor1.通过SPI方式加载EnvironmentPostProcessor
2.添加ConfigFileApplicationListener自身
3.排序,遍历调用postProcessEnvironment
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {//SPI加载EnvironmentPostProcessor
ListpostProcessors = 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());
}
}
ListloadPostProcessors() {return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class, this.getClass().getClassLoader());
}
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {this.addPropertySources(environment, application.getResourceLoader());
}
2.添加随机数配置源排序后的EnvironmentPostProcessor
给environment添加随机数配置源是为了解析类似${random.int}这种配置
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {RandomValuePropertySource.addToEnvironment(environment);
(new ConfigFileApplicationListener.Loader(environment, resourceLoader)).load();
}
public static void addToEnvironment(ConfigurableEnvironment environment) {environment.getPropertySources().addAfter("systemEnvironment", new RandomValuePropertySource("random"));
logger.trace("RandomValuePropertySource add to Environment");
}
3.SPI加载PropertySourceLoader添加后environment的配置源,优先级从高到低
class Loader {Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {this.logger = ConfigFileApplicationListener.this.logger;
this.loadDocumentsCache = new HashMap();
this.environment = environment;
this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(this.environment);
this.resourceLoader = (ResourceLoader)(resourceLoader != null ? resourceLoader : new DefaultResourceLoader());
//SPI加载PropertySourceLoader
this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class, this.getClass().getClassLoader());
}
}
加载了2个PropertySourceLoader
PropertiesPropertySourceLoader,支持properties、xml
public class PropertiesPropertySourceLoader implements PropertySourceLoader {private static final String XML_FILE_EXTENSION = ".xml";
public PropertiesPropertySourceLoader() {}
public String[] getFileExtensions() {return new String[]{"properties", "xml"};
}
public List>load(String name, Resource resource) throws IOException {Mapproperties = this.loadProperties(resource);
return properties.isEmpty() ? Collections.emptyList() : Collections.singletonList(new OriginTrackedMapPropertySource(name, Collections.unmodifiableMap(properties), true));
}
private MaploadProperties(Resource resource) throws IOException {String filename = resource.getFilename();
return (Map)(filename != null && filename.endsWith(".xml") ? PropertiesLoaderUtils.loadProperties(resource) : (new OriginTrackedPropertiesLoader(resource)).load());
}
}
YamlPropertySourceLoader,支持yml、yaml
public class YamlPropertySourceLoader implements PropertySourceLoader {public YamlPropertySourceLoader() {}
public String[] getFileExtensions() {return new String[]{"yml", "yaml"};
}
public List>load(String name, Resource resource) throws IOException {if (!ClassUtils.isPresent("org.yaml.snakeyaml.Yaml", (ClassLoader)null)) {throw new IllegalStateException("Attempted to load " + name + " but snakeyaml was not found on the classpath");
} else {List
开始解析void load() {FilteredPropertySource.apply(this.environment, "defaultProperties", ConfigFileApplicationListener.LOAD_FILTERED_PROPERTY, (defaultProperties) ->{//当前激活的profile,双端队列
this.profiles = new LinkedList();
//已被处理过的profile
this.processedProfiles = new LinkedList();
//是否已被激活了,这个标志很重要,如果它为true,后续再激活的profile不会生效
this.activatedProfiles = false;
//缓存,暂存PropertySource,注意它是保持了profile解析顺序的LinkedHashMap,key=profile,value=MutablePropertySources
this.loaded = new LinkedHashMap();
//1、加载目前所有的profile,如果指定了spring.profiles.active,activatedProfiles=true
this.initializeProfiles();
//2、遍历profile进行解析,springboot支持多profile的原因
while(!this.profiles.isEmpty()) {ConfigFileApplicationListener.Profile profile = (ConfigFileApplicationListener.Profile)this.profiles.poll();
if (this.isDefaultProfile(profile)) { //把所有非null和default的profile加入environment
this.addProfileToEnvironment(profile.getName());
}
this.load(profile, this::getPositiveProfileFilter, this.addToLoaded(MutablePropertySources::addLast, false));
this.processedProfiles.add(profile);
}
//
this.load((ConfigFileApplicationListener.Profile)null, this::getNegativeProfileFilter, this.addToLoaded(MutablePropertySources::addFirst, true));
//3、把loaded缓存中的PropertySource加入environment
this.addLoadedPropertySources();
//4、更新environment的activeProfiles列表
this.applyActiveProfiles(defaultProperties);
});
}
1.加载所有profileprivate void initializeProfiles() {//第1个是null,对应配置文件application.yaml
this.profiles.add((Object)null);
//获取环境准备阶段spring.profiles.active指定的profile
SetactivatedViaProperty = this.getProfilesFromProperty("spring.profiles.active");
//通过环境准备阶段spring.profiles.include指定的子项profile
SetincludedViaProperty = this.getProfilesFromProperty("spring.profiles.include");
//获取环境准备阶段api方式指定的profile
ListotherActiveProfiles = this.getOtherActiveProfiles(activatedViaProperty, includedViaProperty);
this.profiles.addAll(otherActiveProfiles);
this.profiles.addAll(includedViaProperty);
this.addActiveProfiles(activatedViaProperty);
//没有指定任何profile时,default兜底
if (this.profiles.size() == 1) {String[] var4 = this.environment.getDefaultProfiles();
int var5 = var4.length;
for(int var6 = 0; var6< var5; ++var6) {String defaultProfileName = var4[var6];
ConfigFileApplicationListener.Profile defaultProfile = new ConfigFileApplicationListener.Profile(defaultProfileName, true);
this.profiles.add(defaultProfile);
}
}
}
private ListgetOtherActiveProfiles(SetactivatedViaProperty, SetincludedViaProperty) {return (List)Arrays.stream(this.environment.getActiveProfiles()).map(ConfigFileApplicationListener.Profile::new).filter((profile) ->{return !activatedViaProperty.contains(profile) && !includedViaProperty.contains(profile);
}).collect(Collectors.toList());
}
void addActiveProfiles(Setprofiles) { if (!profiles.isEmpty()) { if (this.activatedProfiles) { if (this.logger.isDebugEnabled()) { this.logger.debug("Profiles already activated, '" + profiles + "' will not be applied");
}
} else { this.profiles.addAll(profiles);
if (this.logger.isDebugEnabled()) { this.logger.debug("Activated activeProfiles " + StringUtils.collectionToCommaDelimitedString(profiles));
}
//设置activatedProfiles标志,后续激活的profile不再生效
this.activatedProfiles = true;
this.removeUnprocessedDefaultProfiles();
}
}
}
profile加载顺序如下,与解析后加入environment中的PropertySource顺序相反
api指定的profile,在环境准备阶段会加入environment的activeProfiles列表
spring.profiles.include属性指定的profile,最后也会加入environment的activeProfiles列表
spring.profiles.active属性指定的profile,在环境准备阶段会加入environment的activeProfiles列表
2.遍历profile解析1.先解析不带profile的配置文件
2.其次解析带profile的配置文件
void load() {//第1个始终为null,即不带profile的配置文件,可能解析出新的激活profile放入队列
while(!this.profiles.isEmpty()) { ConfigFileApplicationListener.Profile profile = (ConfigFileApplicationListener.Profile)this.profiles.poll();
if (this.isDefaultProfile(profile)) { this.addProfileToEnvironment(profile.getName());
}
this.load(profile, this::getPositiveProfileFilter, this.addToLoaded(MutablePropertySources::addLast, false));
this.processedProfiles.add(profile);
}
this.load((ConfigFileApplicationListener.Profile)null, this::getNegativeProfileFilter, this.addToLoaded(MutablePropertySources::addFirst, true));
...
}
获取搜索路径和文件名private void load(ConfigFileApplicationListener.Profile profile, ConfigFileApplicationListener.DocumentFilterFactory filterFactory, ConfigFileApplicationListener.DocumentConsumer consumer) { this.getSearchLocations().forEach((location) ->{//目录必须以/结尾,容易掉坑
boolean isFolder = location.endsWith("/");
Setnames = isFolder ? this.getSearchNames() : ConfigFileApplicationListener.NO_SEARCH_NAMES;
names.forEach((name) ->{this.load(location, name, profile, filterFactory, consumer);
});
});
}
获取搜索路径,spring.config.location或者spring.config.additional-location + 4个默认路径(file:./config/, file:./, classpath:/config/, classpath:/,优先级从高到低)
private SetgetSearchLocations() {if (this.environment.containsProperty("spring.config.location")) {return this.getSearchLocations("spring.config.location");
} else {Setlocations = this.getSearchLocations("spring.config.additional-location");
locations.addAll(this.asResolvedSet(ConfigFileApplicationListener.this.searchLocations, "classpath:/,classpath:/config/,file:./,file:./config/"));
return locations;
}
}
获取搜索文件名,spring.config.name或者application
private SetgetSearchNames() {if (this.environment.containsProperty("spring.config.name")) {String property = this.environment.getProperty("spring.config.name");
return this.asResolvedSet(property, (String)null);
} else {return this.asResolvedSet(ConfigFileApplicationListener.this.names, "application");
}
}
遍历搜索路径下的配置文件依次遍历properties、xml、yml、yaml,配置文件的全路径规则:
1.profile==null时,路径 + 文件名 + 后缀(properties、yml、yaml)
2.profile!=null时,路径 + 文件名-profile + 后缀
private void load(String location, String name, Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
//完整文件路径,直接根据后缀匹配出1个PropertySourceLoader
if (!StringUtils.hasText(name)) {//PropertiesPropertySourceLoader和YamlPropertySourceLoader
for (PropertySourceLoader loader : this.propertySourceLoaders) {if (canLoadFileExtension(loader, location)) { //解析
load(loader, location, profile, filterFactory.getDocumentFilter(profile), consumer);
return;
}
}
}
Setprocessed = new HashSet<>();
//非完整文件,因为不知道后缀,所以尝试解析每个PropertySourceLoader支持的所有文件
for (PropertySourceLoader loader : this.propertySourceLoaders) {for (String fileExtension : loader.getFileExtensions()) {if (processed.add(fileExtension)) {loadForFileExtension(loader, location + name, "." + fileExtension, profile, filterFactory, consumer);
}
}
}
}
private void loadForFileExtension(PropertySourceLoader loader, String prefix, String fileExtension, Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {if (profile != null) { //文件拼接规则1
String profileSpecificFile = prefix + "-" + profile + fileExtension;
this.load(loader, profileSpecificFile, profile, defaultFilter, consumer);
this.load(loader, profileSpecificFile, profile, profileFilter, consumer);
Iterator var10 = this.processedProfiles.iterator();
while(var10.hasNext()) { ConfigFileApplicationListener.Profile processedProfile = (ConfigFileApplicationListener.Profile)var10.next();
if (processedProfile != null) { String previouslyLoaded = prefix + "-" + processedProfile + fileExtension;
this.load(loader, previouslyLoaded, profile, profileFilter, consumer);
}
}
}
//文件拼接规则2
this.load(loader, prefix + fileExtension, profile, profileFilter, consumer);
}
解析、加入缓存1.获取Rerousce,解析封装成PropertySource保存在Document中
2.如果当前还未激活profile,文件中的profile生效,加入profiles队列继续解析
3.删掉default
4.把解析后的PropertySource加入loaded缓存,LinkedHashMap保证了解析顺序
//缓存
private Maploaded = new LinkedHashMap();
private void load(PropertySourceLoader loader, String location, Profile profile, DocumentFilter filter,
DocumentConsumer consumer) {try {Resource resource = this.resourceLoader.getResource(location);
...
//获取Rerousce,解析封装成PropertySource保存在Document中
//yaml中可能存在文档块,每个块就是1个Document
Listdocuments = loadDocuments(loader, name, resource);
Listloaded = new ArrayList();
for (ConfigFileApplicationListener.Document document: documents){ if (filter.match(document)) {this.addActiveProfiles(document.getActiveProfiles());
this.addIncludedProfiles(document.getIncludeProfiles());
loaded.add(document);
}
}
Collections.reverse(loaded);
if (!loaded.isEmpty()) {//回调addToLoaded返回的lambda
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);
}
}
void addActiveProfiles(Setprofiles) {//解析到了profile
if (!profiles.isEmpty()) { if (this.activatedProfiles) { if (this.logger.isDebugEnabled()) { this.logger.debug("Profiles already activated, '" + profiles + "' will not be applied");
}
} else { //当前还未激活profile,文件中的profile才生效,
this.profiles.addAll(profiles);
if (this.logger.isDebugEnabled()) { this.logger.debug("Activated activeProfiles " + StringUtils.collectionToCommaDelimitedString(profiles));
}
this.activatedProfiles = true;
//删除default profile,比如最开始未激活profile,此时profiles包括default,当加载application文件后发现激活了profile,此时就会走到这
this.removeUnprocessedDefaultProfiles();
}
}
private DocumentConsumer addToLoaded(BiConsumer>addMethod,
boolean checkForExisting) {//回调方法
return (profile, document) ->{if (checkForExisting) {for (MutablePropertySources merged : this.loaded.values()) {if (merged.contains(document.getPropertySource().getName())) {return;
}
}
}
MutablePropertySources merged = this.loaded.computeIfAbsent(profile,
(k) ->new MutablePropertySources());
//又回调MutablePropertySources::addFirst或utablePropertySources::addLast加入loaded缓存中
addMethod.accept(merged, document.getPropertySource());
};
}
3.缓存添加到environment把缓存中的PropertySource反转后加入environment最后,这也是为什么配置文件优先级低于命令行、系统属性、环境变量
private void addLoadedPropertySources() {MutablePropertySources destination = this.environment.getPropertySources();
Listloaded = new ArrayList<>(this.loaded.values());
//重要,保证了带profile的文件优先于不带profile的
Collections.reverse(loaded);
String lastAdded = null;
Setadded = new HashSet<>();
for (MutablePropertySources sources : loaded) {for (PropertySource>source : sources) {if (added.add(source.getName())) {addLoadedPropertySource(destination, lastAdded, source);
lastAdded = source.getName();
}
}
}
}
private void addLoadedPropertySource(Mutab lePropertySources destination, String lastAdded,
PropertySource>source) {if (lastAdded == null) {if (destination.contains(DEFAULT_PROPERTIES)) {destination.addBefore(DEFAULT_PROPERTIES, source);
}
else {destination.addLast(source);
}
}
else {destination.addAfter(lastAdded, source);
}
}
4.更新environment的activeProfiles所有配置文件加入environment后,优先级从高到低
private void applyActiveProfiles(PropertySource>defaultProperties) { ListactiveProfiles = new ArrayList();
if (defaultProperties != null) { Binder binder = new Binder(ConfigurationPropertySources.from(defaultProperties), new PropertySourcesPlaceholdersResolver(this.environment));
activeProfiles.addAll(this.getDefaultProfiles(binder, "spring.profiles.include"));
if (!this.activatedProfiles) { activeProfiles.addAll(this.getDefaultProfiles(binder, "spring.profiles.active"));
}
}
//去掉null和default
this.processedProfiles.stream().filter(this::isDefaultProfile).map(ConfigFileApplicationListener.Profile::getName).forEach(activeProfiles::add);
this.environment.setActiveProfiles((String[])activeProfiles.toArray(new String[0]));
}
private boolean isDefaultProfile(ConfigFileApplicationListener.Profile profile) { return profile != null && !profile.isDefaultProfile();
}
总结解析profile的优先级:api -->spring.profiles.include -->spring.profiles.active
加入environment的优先级:spring.profiles.active -->spring.profiles.include -->api
解析路径的优先级:file:./config/ -->file:./ -->classpath:/config/ -->classpath:/
命令行参数、系统属性、环境变量的优先级大于配置文件
profile激活原则:spring.profiles.active指定后,后续再指定不再生效
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧
当前题目:springboot源码4---配置文件解析-创新互联
网站URL:http://myzitong.com/article/djgsdh.html