ConfigFileApplicationListener

实现了resources的加载工作

一句话概述:
ConfigFileApplicationListener加载file:./config,file:,classpath:/config,classpath:。这4个路径下的application命名的配置文件。
通过PropertiesPropertySourceLoader来加载后缀是"properties", "xml"的文件。
通过YamlPropertySourceLoader来加载后缀是"yml", "yaml"的文件。
所有加载的配置放在OriginTrackedMapPropertySource对象中,最后被封装成Document数组对象,随后获取到profile(spring:profiles:active:dev),加载对应的profile文件(如:application-dev.yaml),最终封装到ConfigurableEnvironment中返回,如下图

ConfigurableEnvironment.png

代码

SpringApplication

ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);

    private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
        // Create and configure the environment
        // 创建 ConfigurableEnvironment 对象,并进行配置
        ConfigurableEnvironment environment = getOrCreateEnvironment();
        configureEnvironment(environment, applicationArguments.getSourceArgs());
        // 通知 SpringApplicationRunListener 的数组,环境变量已经准备完成。
        listeners.environmentPrepared(environment);
        // 绑定 environment 到 SpringApplication 上
        bindToSpringApplication(environment);
        // 如果非自定义 environment ,则根据条件转换
        if (!this.isCustomEnvironment) {
            environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
        }
        // 如果有 attach 到 environment 上的 MutablePropertySources ,则添加到 environment 的 PropertySource 中。
        ConfigurationPropertySources.attach(environment);
        return environment;
    }

listeners.environmentPrepared(environment);

通知一下监听ConfigFileApplicationListener的onApplicationEvent方法


private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
        // 加载指定类型 EnvironmentPostProcessor 对应的,在 `META-INF/spring.factories` 里的类名的数组
        List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
        // 加入自己
        postProcessors.add(this);
        // 排序 postProcessors 数组
        AnnotationAwareOrderComparator.sort(postProcessors);
        // 遍历 postProcessors 数组,逐个执行。
        for (EnvironmentPostProcessor postProcessor : postProcessors) {
            //@2
            postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
        }
    }
Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
            this.environment = environment;
            // 创建 PropertySourcesPlaceholdersResolver 对象
            this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(this.environment);
            // 创建 DefaultResourceLoader 对象
            this.resourceLoader = (resourceLoader != null) ? resourceLoader : new DefaultResourceLoader();
            // 加载指定类型 PropertySourceLoader 对应的,在 `META-INF/spring.factories` 里的类名的数组
            this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class, getClass().getClassLoader());
        }
public void load() {
            // 初始化变量
            this.profiles = new LinkedList<>(); // 未处理的 Profile 集合
            this.processedProfiles = new LinkedList<>(); // 已处理的 Profile 集合
            this.activatedProfiles = false;
            this.loaded = new LinkedHashMap<>();
            // 初始化 Spring Profiles 相关
            initializeProfiles();
            // 遍历 profiles 数组
            while (!this.profiles.isEmpty()) {
                // 获得 Profile 对象
                Profile profile = this.profiles.poll();
                // 添加 Profile 到 environment 中
                if (profile != null && !profile.isDefaultProfile()) {
                    addProfileToEnvironment(profile.getName());
                }
                // 加载配置
                load(profile, this::getPositiveProfileFilter,
                        addToLoaded(MutablePropertySources::addLast, false));
                // 添加到 processedProfiles 中,表示已处理
                this.processedProfiles.add(profile);
            }
            // 获得真正加载的 Profile 们,添加到 environment 中。
            resetEnvironmentProfiles(this.processedProfiles);
            // 加载配置
            load(null, this::getNegativeProfileFilter,
                    addToLoaded(MutablePropertySources::addFirst, true));
            // 将加载的配置对应的 MutablePropertySources 到 environment 中
            addLoadedPropertySources();
        }
private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
            // 获得要检索配置的路径
            getSearchLocations().forEach((location) -> {
                // 判断是否为文件夹
                boolean isFolder = location.endsWith("/");
                // 获得要检索配置的文件名集合
                Set<String> names = isFolder ? getSearchNames() : NO_SEARCH_NAMES;
                // 遍历文件名集合,逐个加载配置文件
                names.forEach(
                        (name) -> load(location, name, profile, filterFactory, consumer));
            });
        }
private void load(String location, String name, Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
            // 默认情况下,name 为 DEFAULT_NAMES=application
            if (!StringUtils.hasText(name)) {
                for (PropertySourceLoader loader : this.propertySourceLoaders) {
                    if (canLoadFileExtension(loader, location)) {
                        load(loader, location, profile, filterFactory.getDocumentFilter(profile), consumer);
                        return;
                    }
                }
            }
            Set<String> processed = new HashSet<>(); // 已处理的文件后缀集合
            // 遍历 propertySourceLoaders 数组,逐个使用 PropertySourceLoader 读取配置
            for (PropertySourceLoader loader : this.propertySourceLoaders) {
                // 遍历每个 PropertySourceLoader 可处理的文件后缀集合
                for (String fileExtension : loader.getFileExtensions()) {
                    // 添加到 processed 中,一个文件后缀,有且仅能被一个 PropertySourceLoader 所处理
                    if (processed.add(fileExtension)) {
                        // 加载 Profile 指定的配置文件(带后缀)
                        loadForFileExtension(loader, location + name, "." + fileExtension,
                                profile, filterFactory, consumer);
                    }
                }
            }
        }
private void loadForFileExtension(PropertySourceLoader loader, String prefix,
                String fileExtension, Profile profile,
                DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
            // 获得 DocumentFilter 对象
            DocumentFilter defaultFilter = filterFactory.getDocumentFilter(null);
            DocumentFilter profileFilter = filterFactory.getDocumentFilter(profile);
            // 加载 Profile 指定的配置文件(带后缀)。
            if (profile != null) {
                // Try profile-specific file & profile section in profile file (gh-340)
                // 加载 Profile 指定的配置文件(带后缀)。
                String profileSpecificFile = prefix + "-" + profile + fileExtension;
                load(loader, profileSpecificFile, profile, defaultFilter, consumer); // 情况一,未配置 spring.profile 属性
                load(loader, profileSpecificFile, profile, profileFilter, consumer); // 情况二,有配置 spring.profile 属性
                // Try profile specific sections in files we've already processed
                // 特殊情况,之前读取 Profile 对应的配置文件,也可被当前 Profile 所读取。
                // 举个例子,假设之前读取了 Profile 为 common 对应配置文件是 application-common.properties ,里面配置了 spring.profile=dev,prod
                //         那么,此时如果读取的 Profile 为 dev 时,也能读取 application-common.properties 这个配置文件
                for (Profile processedProfile : this.processedProfiles) {
                    if (processedProfile != null) {
                        String previouslyLoaded = prefix + "-" + processedProfile + fileExtension; // 拼接之前的配置文件名
                        load(loader, previouslyLoaded, profile, profileFilter, consumer); // 注意噢,传入的 profile 是当前的 profile
                    }
                }
            }
            // Also try the profile-specific section (if any) of the normal file
            // 加载(无需带 Profile)指定的配置文件(带后缀)。
            load(loader, prefix + fileExtension, profile, profileFilter, consumer);
        }
private void load(PropertySourceLoader loader, String location, Profile profile, DocumentFilter filter, DocumentConsumer consumer) {
            try {
                // 判断指定的配置文件是否存在。若不存在,则直接返回
                Resource resource = this.resourceLoader.getResource(location);
                if (!resource.exists()) {
                    if (this.logger.isTraceEnabled()) {
                        StringBuilder description = getDescription("Skipped missing config ", location, resource, profile);
                        this.logger.trace(description);
                    }
                    return;
                }
                // 如果没有文件后缀的配置文件,则忽略,不进行读取
                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);
                    }
                    return;
                }
                // 加载配置文件,返回 Document 数组
                String name = "applicationConfig: [" + location + "]";
                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);
                    }
                    return;
                }
                // 使用 DocumentFilter 过滤匹配的 Document ,添加到 loaded 数组中。
                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);
                // 使用 DocumentConsumer 进行消费 Document ,添加到本地的 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) {
                throw new IllegalStateException("Failed to load property "
                        + "source from location '" + location + "'", ex);
            }
        }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,723评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,003评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,512评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,825评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,874评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,841评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,812评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,582评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,033评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,309评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,450评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,158评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,789评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,409评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,609评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,440评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,357评论 2 352

推荐阅读更多精彩内容