美文网首页
springBoot应用中配置文件的加载过程

springBoot应用中配置文件的加载过程

作者: kele2018 | 来源:发表于2021-07-26 15:48 被阅读0次

1、问题拆解

(1)springBoot默认会去哪里找应用的配置文件?
(2)配置文件的名称是什么?
(3)配置文件的后缀是什么?
(4)采用哪种方式加载进来?
(5)内容解析?

2、拉取栈帧

getInputStream:182, ClassPathResource (org.springframework.core.io)
process:199, YamlProcessor (org.springframework.beans.factory.config)
process:164, YamlProcessor (org.springframework.beans.factory.config)
load:76, OriginTrackedYamlLoader (org.springframework.boot.env)
load:50, YamlPropertySourceLoader (org.springframework.boot.env)
loadDocuments:607, ConfigFileApplicationListener$Loader (org.springframework.boot.context.config)
/**
m6、加载文件
**/
load:523, ConfigFileApplicationListener$Loader (org.springframework.boot.context.config)
loadForFileExtension:498, ConfigFileApplicationListener$Loader (org.springframework.boot.context.config)
 /**
 m5、处理配置文件的后缀
 **/
load:468, ConfigFileApplicationListener$Loader (org.springframework.boot.context.config)
lambda$null$7:447, ConfigFileApplicationListener$Loader (org.springframework.boot.context.config)
accept:-1, 1430199669 (org.springframework.boot.context.config.ConfigFileApplicationListener$Loader$$Lambda$118)
forEach:75, Iterable (java.lang)
lambda$load$8:447, ConfigFileApplicationListener$Loader (org.springframework.boot.context.config)
accept:-1, 1213871206 (org.springframework.boot.context.config.ConfigFileApplicationListener$Loader$$Lambda$115)
forEach:75, Iterable (java.lang)
 /**
 m4、处理配置文件的路径和名称
 **/
load:444, ConfigFileApplicationListener$Loader (org.springframework.boot.context.config)
lambda$load$0:347, ConfigFileApplicationListener$Loader (org.springframework.boot.context.config)
accept:-1, 1100109058 (org.springframework.boot.context.config.ConfigFileApplicationListener$Loader$$Lambda$99)
apply:54, FilteredPropertySource (org.springframework.boot.context.config)
/**
m3、委托给内部类Loader
**/
load:335, ConfigFileApplicationListener$Loader (org.springframework.boot.context.config)
addPropertySources:226, ConfigFileApplicationListener (org.springframework.boot.context.config)
/**
 m2、ConfigFileApplicationListener也是一个EnvironmentPostProcessor, 
**/
postProcessEnvironment:210, ConfigFileApplicationListener (org.springframework.boot.context.config)
onApplicationEnvironmentPreparedEvent:200, ConfigFileApplicationListener (org.springframework.boot.context.config)
 /**
 m1、ConfigFileApplicationListener是一个ApplicationListener,
 **/
onApplicationEvent:188, ConfigFileApplicationListener (org.springframework.boot.context.config)
doInvokeListener:172, SimpleApplicationEventMulticaster (org.springframework.context.event)
invokeListener:165, SimpleApplicationEventMulticaster (org.springframework.context.event)
multicastEvent:139, SimpleApplicationEventMulticaster (org.springframework.context.event)
multicastEvent:127, SimpleApplicationEventMulticaster (org.springframework.context.event)
environmentPrepared:80, EventPublishingRunListener (org.springframework.boot.context.event)
environmentPrepared:53, SpringApplicationRunListeners (org.springframework.boot)
prepareEnvironment:345, SpringApplication (org.springframework.boot)
run:308, SpringApplication (org.springframework.boot)
run:1237, SpringApplication (org.springframework.boot)
run:1226, SpringApplication (org.springframework.boot)
main:62, GfundActbApiApplication (com.gome.gfund.actb)

3、问题解析

m1和m2是springBoot启动时的固有流程,我们从m3处开始看:

// m3:org.springframework.boot.context.config.ConfigFileApplicationListener.Loader#load()
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);
                    });
}
/**
m4: org.springframework.boot.context.config.ConfigFileApplicationListener.
Loader#load(org.springframework.boot.context.config.ConfigFileApplicationListener.Profile, 
org.springframework.boot.context.config.ConfigFileApplicationListener.DocumentFilterFactory,
org.springframework.boot.context.config.ConfigFileApplicationListener.DocumentConsumer)

DocumentConsumer:当配置文件中的数据加载进来后,该对象会处理
**/
private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
            /**
            这里会获取配置文件路径(getSearchLocations),这个路径可以自己设置,也有默认的:
            DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/,file:./config/";
            也会获取配置文件名称(getSearchNames),可以自己设置,也有默认的:application  bootStrap;
            **/
            getSearchLocations().forEach((location) -> {
                boolean isDirectory = location.endsWith("/");
                Set<String> names = isDirectory ? getSearchNames() : NO_SEARCH_NAMES;
                names.forEach((name) -> load(location, name, profile, filterFactory, consumer));
            });
}

/**
m5: org.springframework.boot.context.config.ConfigFileApplicationListener.Loader#load(java.lang.String, 
java.lang.String, org.springframework.boot.context.config.ConfigFileApplicationListener.Profile,
org.springframework.boot.context.config.ConfigFileApplicationListener.DocumentFilterFactory, 
org.springframework.boot.context.config.ConfigFileApplicationListener.DocumentConsumer)
**/
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<>();
            /**
            this.propertySourceLoaders = {ArrayList@5074}  size = 4
             0 = {NacosXmlPropertySourceLoader@6079} "xml"
             1 = {PropertiesPropertySourceLoader@6080} "properties", "xml"
             2 = {YamlPropertySourceLoader@5023}  "yml", "yaml" 
             3 = {NacosJsonPropertySourceLoader@6081} "json"
             这里会根据不同的PropertySourceLoader来拿到不同的文件后缀;
            **/
            for (PropertySourceLoader loader : this.propertySourceLoaders) {
                for (String fileExtension : loader.getFileExtensions()) {
                    if (processed.add(fileExtension)) {
                        loadForFileExtension(loader, location + name, "." + fileExtension, profile, filterFactory,
                                consumer);
                    }
                }
            }
}
/**
m6: org.springframework.boot.context.config.ConfigFileApplicationListener.Loader#load(org.springframework.boot.env.PropertySourceLoader, 
java.lang.String, org.springframework.boot.context.config.ConfigFileApplicationListener.Profile, 
org.springframework.boot.context.config.ConfigFileApplicationListener.DocumentFilter,
org.springframework.boot.context.config.ConfigFileApplicationListener.DocumentConsumer)
**/
private void load(PropertySourceLoader loader, String location, Profile profile, DocumentFilter filter,
                DocumentConsumer consumer) {
            Resource[] resources = getResources(location);
            for (Resource resource : resources) {
                try {
                    if (resource == null || !resource.exists()) {
                        if (this.logger.isTraceEnabled()) {
                            StringBuilder description = getDescription("Skipped missing config ", location, resource,
                                    profile);
                            this.logger.trace(description);
                        }
                        continue;
                    }
                    if (!StringUtils.hasText(StringUtils.getFilenameExtension(resource.getFilename()))) {
                        if (this.logger.isTraceEnabled()) {
                            StringBuilder description = getDescription("Skipped empty config extension ", location,
                                    resource, profile);
                            this.logger.trace(description);
                        }
                        continue;
                    }
                    String name = "applicationConfig: [" + getLocationName(location, resource) + "]";
                    /**
                    这里就会真正的加载文件,这里面有一个坑:就是加载文件用的api是this.classLoader.getResourceAsStream(this.path);
                    假如我们应用的classpath下有两个application.yml,这个api只会加载遇到的第一个,这样就导致一些外部配置找不到;
                    **/
                    List<Document> documents = loadDocuments(loader, name, resource);
                    if (CollectionUtils.isEmpty(documents)) {
                        if (this.logger.isTraceEnabled()) {
                            StringBuilder description = getDescription("Skipped unloaded config ", location, resource,
                                    profile);
                            this.logger.trace(description);
                        }
                        continue;
                    }
                    List<Document> loaded = new ArrayList<>();
                    for (Document document : documents) {
                        if (filter.match(document)) {
                            addActiveProfiles(document.getActiveProfiles());
                            addIncludedProfiles(document.getIncludeProfiles());
                            loaded.add(document);
                        }
                    }
                    Collections.reverse(loaded);
                    if (!loaded.isEmpty()) {
                        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) {
                    StringBuilder description = getDescription("Failed to load property source from ", location,
                            resource, profile);
                    throw new IllegalStateException(description.toString(), ex);
                }
            }
        }

相关文章

网友评论

      本文标题:springBoot应用中配置文件的加载过程

      本文链接:https://www.haomeiwen.com/subject/wsfgmltx.html