资讯

精准传达 • 有效沟通

从品牌网站建设到网络营销策划,从策略到执行的一站式服务

springboot源码4---配置文件解析-创新互联

ConfigFileApplicationListener
  • 执行时机
  • 解析前工作
    • 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.加载EnvironmentPostProcessor

1.通过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());
}

排序后的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");
}

添加后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>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>loaded = (new OriginTrackedYamlLoader(resource)).load();
            if (loaded.isEmpty()) {return Collections.emptyList();
            } else {List>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
private 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 (PropertySourcesource : sources) {if (added.add(source.getName())) {addLoadedPropertySource(destination, lastAdded, source);
                lastAdded = source.getName();
            }
        }
    }
}

private void addLoadedPropertySource(Mutab lePropertySources destination, String lastAdded,
        PropertySourcesource) {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(PropertySourcedefaultProperties) { 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://www.cdkjz.cn/article/djgsdh.html
多年建站经验

多一份参考,总有益处

联系快上网,免费获得专属《策划方案》及报价

咨询相关问题或预约面谈,可以通过以下方式与我们联系

大客户专线   成都:13518219792   座机:028-86922220