ConfigFileApplicationListener
执行时机
发布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.加载EnvironmentPostProcessor
- 通过SPI方式加载EnvironmentPostProcessor
- 添加ConfigFileApplicationListener自身
- 排序
- 遍历调用postProcessEnvironment
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
//SPI加载EnvironmentPostProcessor
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());
}
}
List<EnvironmentPostProcessor> loadPostProcessors() {
return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class, this.getClass().getClassLoader());
}
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
this.addPropertySources(environment, application.getResourceLoader());
}
排序后的EnvironmentPostProcessor
2.添加随机数配置源
给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
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<PropertySource<?>> load(String name, Resource resource) throws IOException {
Map<String, ?> properties = this.loadProperties(resource);
return properties.isEmpty() ? Collections.emptyList() : Collections.singletonList(new OriginTrackedMapPropertySource(name, Collections.unmodifiableMap(properties), true));
}
private Map<String, ?> loadProperties(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<PropertySource<?>> 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<Map<String, Object>> loaded = (new OriginTrackedYamlLoader(resource)).load();
if (loaded.isEmpty()) {
return Collections.emptyList();
} else {
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((Map)loaded.get(i)), true));
}
return propertySources;
}
}
}
}
开始解析
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.加载所有profile
profile加载顺序如下
- api指定的profile,在环境准备阶段会加入environment的activeProfiles列表
- spring.profiles.include属性指定的profile,最后也会加入environment的activeProfiles列表
- spring.profiles.active属性指定的profile,在环境准备阶段会加入environment的activeProfiles列表
注意:与解析后加入environment中的PropertySource顺序相反
private void initializeProfiles() {
//第1个是null,对应配置文件application.yaml
this.profiles.add((Object)null);
//获取环境准备阶段spring.profiles.active指定的profile
Set<ConfigFileApplicationListener.Profile> activatedViaProperty = this.getProfilesFromProperty("spring.profiles.active");
//通过环境准备阶段spring.profiles.include指定的子项profile
Set<ConfigFileApplicationListener.Profile> includedViaProperty = this.getProfilesFromProperty("spring.profiles.include");
//获取环境准备阶段api方式指定的profile
List<ConfigFileApplicationListener.Profile> otherActiveProfiles = 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 List<ConfigFileApplicationListener.Profile> getOtherActiveProfiles(Set<ConfigFileApplicationListener.Profile> activatedViaProperty, Set<ConfigFileApplicationListener.Profile> includedViaProperty) {
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(Set<ConfigFileApplicationListener.Profile> profiles) {
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();
}
}
}
2.遍历profile解析
- 先解析不带profile的配置文件
- 其次解析带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("/");
Set<String> names = 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 Set<String> getSearchLocations() {
if (this.environment.containsProperty("spring.config.location")) {
return this.getSearchLocations("spring.config.location");
} else {
Set<String> locations = 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 Set<String> getSearchNames() {
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等配置文件,然后拼接出完整路径:
- profile==null时,拼接规则:路径 + 文件名 + 后缀
- 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;
}
}
}
Set<String> processed = 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);
}
解析、加入缓存
- 获取Rerousce,解析封装成PropertySource保存在Document中
- 如果当前还未激活profile,文件中的profile生效,加入profiles队列继续解析
- 删掉default
- 把解析后的PropertySource加入loaded缓存,LinkedHashMap保证了解析顺序
//缓存
private Map<ConfigFileApplicationListener.Profile, MutablePropertySources> loaded = 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
List<Document> documents = loadDocuments(loader, name, resource);
List<ConfigFileApplicationListener.Document> loaded = 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(Set<ConfigFileApplicationListener.Profile> profiles) {
//解析到了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<MutablePropertySources, PropertySource<?>> 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();
List<MutablePropertySources> loaded = new ArrayList<>(this.loaded.values());
//重要,保证了带profile的文件优先于不带profile的
Collections.reverse(loaded);
String lastAdded = null;
Set<String> added = 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);
}
}
所有配置文件加入environment后
4.更新environment的activeProfiles
private void applyActiveProfiles(PropertySource<?> defaultProperties) {
List<String> activeProfiles = 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指定后,后续再指定不再生效