SpringBooot配置中spring.profiles配置剖析

/**
 * 这里只针对spring.profiles声明,有特殊用法
 * spring.profiles.active和spring.profiles.include就是将文件激活
 */
class ConfigFileApplicationListener {
    // 开始查找对应的配置文件资源,进行解析
    public void load(PropertySourceLoader loader, String location, Profile profile, DocumentFilter filter, DocumentConsumer consumer) {
        // 加载指定路径文件的资源
        Resource resource = this.resourceLoader.getResource(location);
        // 如果文件不存在,不处理
        if (resource == null || !resource.exists()) {
            return;
        }
        // 如果文件没有后缀名,不处理
        if (!StringUtils.hasText(StringUtils.getFilenameExtension(resource.getFilename()))) {
            return;
        }
        // 设置解析到的配置属性的名称
        String name = "applicationConfig: [" + location + "]";
        // 开始解析配置文件,并转换为Document对象
        List<Document> documents = this.loadDocuments(loader, name, resource);
        // 如果没有加载到属性配置,不需要处理
        if (CollectionUtils.isEmpty(documents)) {
            return;
        }
        // 最终需要保存的属性配置对应的Document
        List<Document> loaded = new ArrayList<>();
        // 遍历解析好的属性配置,一般情况只会有一个
        // 因为一个配置文件一般情况下只会有一个Document对象(PropertySource)
        for (Document document : documents) {
            // 将解析好的Document对象交给文档过滤器filter来进行过滤
            // 如果过滤成功,符合条件
            if (filter.match(document)) {
                // 从当前加载的配置文件对应的属性对象中,获取spring.profiles.active的属性值
                // 保存这个激活的配置信息到当前Loader对象中
                this.addActiveProfiles(document.getActiveProfiles());
                // 从当前加载的配置文件对应的属性对象中,获取spring.profiles.include的属性值
                // 保存这个激活的配置信息到当前Loader对象中
                this.addIncludedProfiles(document.getIncludeProfiles());
                // 保存符合条件的配置属性文件
                loaded.add(document);
            }
        }
        // 将这个配置属性进行倒序
        Collections.reverse(loaded);
        // 如果加载到了符合条件的配置属性文件
        if (!loaded.isEmpty()) {
            /**
             * 遍历所有的符合条件的属性文件Document对象
             * 然后交给DocumentConsumer文档消费者进行处理
             * 实际上就是将解析好的环境缓存到Loader的loaders对象中,loaders属性对象保存了所有符合条件的环境配置->属性配置的映射
             * @see {@link Loader#addToLoaded}
             */
            loaded.forEach((document) -> consumer.accept(profile, document));
        }
    }

    // 调用配置属性加载器加载属性文件,并且将环境相关的配置信息与解析到的属性配置封装成Document对象
    public List<Document> loadDocuments(PropertySourceLoader loader, String name, Resource resource) {
        // 创建缓存KEY
        DocumentsCacheKey cacheKey = new DocumentsCacheKey(loader, resource);
        // 是否已经处理过
        List<Document> documents = this.loadDocumentsCache.get(cacheKey);
        // 没有处理过该文件
        if (documents == null) {
            // 使用属性文件加载器加载属性文件
            List<PropertySource<?>> loaded = loader.load(name, resource);
            // 将属性配置封装为Document对象
            documents = this.asDocuments(loaded);
            // 缓存该Document
            this.loadDocumentsCache.put(cacheKey, documents);
        }
        // 返回解析转换后的Document对象
        return documents;
    }

    /**
     * 将解析好的配置文件对应的配置属性转换为Document对象
     *
     * @param loaded 加载好的配置文件对应的配置属性
     */
    public List<Document> asDocuments(List<PropertySource<?>> loaded) {
        // 遍历当前配置文件中加载的所有属性
        return loaded.stream().map((propertySource) -> {
            // 将这个加载的配置属性,使用配置属性绑定器绑定到指定的对象中
            Binder binder = new Binder(ConfigurationPropertySources.from(propertySource), this.placeholdersResolver);
            // 从当前加载的配置文件对应的属性对象中,将spring.profiles.active的属性绑定到对应的对象中
            Set<Profile> activeProfiles = this.getProfiles(binder, ACTIVE_PROFILES_PROPERTY);
            // 从当前加载的配置文件对应的属性对象中,将spring.profiles.include的属性绑定到对应的对象中
            Set<Profile> includeProfiles = this.getProfiles(binder, INCLUDE_PROFILES_PROPERTY);
            // 从当前加载的配置文件对应的属性对象中,将spring.profiles的属性绑定到对应的数组对象中
            /**
             * @see {@link Document]
             */
            String[] springProfiles = binder.bind("spring.profiles", STRING_ARRAY).orElse(null);
            // 将该配置文件中加载到的配置属性,激活的配置文件,包含的配置文件,和公共key(spring.profiles)的配置文件
            // 注意: 当前Document仅仅表示当前解析的配置文件对应的配置信息
            return new Document(propertySource, activeProfiles, includeProfiles, springProfiles);
        }).collect(Collectors.toList());
    }
}

// Profile配置文件解析器
class ProfilesParser {

    // 解析环境配置属性值
    public static Profiles parse(String... profiles) {
        Profiles[] parsed = new Profiles[profiles.length];
        // 一一对配置的值进行解析
        for (int i = 0; i < profiles.length; i++) {
            // 解析表达式
            parsed[i] = parseExpression(profiles[i]) {
                // 使用(),&(and),!(not),|(or)
                // (dev,pro),在环境对象中,dev和pro只要有一个设置了激活,当前document配置则保留
                // dev|pro,和(dev,pro)效果一样
                // dev&pro,在环境对对象中,dev和pro都设置了激活的情况下,当前document配置才保留,否则忽略该配置文件属性配置
                // !dev ,在环境对象中,dev没有设置激活,则保留当前document配置
                StringTokenizer tokens = new StringTokenizer(expression, "()&|!", true);
            }
        }
        // 返回解析好的环境配置信息
        return new ParsedProfiles(expressions, parsed);
    }

    // 生成关系为or条件的Profiles
    // 使用环境配置进行or运算
    public static Profiles or(Profiles... profiles) {
        return new Profiles() {
            @Override
            public boolean matches(Predicate<String> activeProfile) {
                // 遍历所有的环境配置,所有的环境配置都任何一个匹配成功就算符合条件
                return Arrays.stream(profiles).anyMatch(ProfilesParser.this.isMatch(activeProfile));
            }
        };
    }

    // 生成关系为and条件的Profiles
    // 使用环境配置进行and运算
    public static Profiles and(Profiles... profiles) {
        return new Profiles() {
            @Override
            public boolean matches(Predicate<String> activeProfile) {
                // 遍历所有的环境配置,所有的环境配置都需要匹配成功才算符合条件
                return Arrays.stream(profiles).allMatch(ProfilesParser.this.isMatch(activeProfile));
            }
        };
    }

    // 生成关系为not条件的Profiles
    // 使用环境配置进行非运算
    public static Profiles not(Profiles profiles) {
        return new Profiles() {
            @Override
            public boolean matches(Predicate<String> activeProfile) {
                // 将给定的环境配置进行匹配,再进行取反
                return !profiles.matches(activeProfile);
            }
        };
    }

    // 真实使用环境配置进行匹配
    // 直接利用给定的激活环境匹配器对环境进行匹配,所有的关系最终都是走向这里
    public static Profiles equals(String profile) {
        return new Profiles() {
            @Override
            public boolean matches(Predicate<String> activeProfile) {
                // 直接利用给定的激活环境匹配器对环境进行匹配,所有的关系最终都是走向这里
                return activeProfile.test(profile);
            }
        };
    }

    /**
     * 返回一个对指定Profiles进行匹配的匹配器
     * {@link ParsedProfiles#matches(Predicate)}
     * {@link ProfilesParser#and(Profiles...)}
     * {@link ProfilesParser#or(Profiles...)}
     * {@link ProfilesParser#not(Profiles)}
     * 最终一定会落到{@link ProfilesParser#equals(String)}中
     * {@link ProfilesParser#isMatch(Predicate)}
     */
    public static Predicate<Profiles> isMatch(Predicate<String> activeProfile) {
        return new Predicate() {
            @Override
            public boolean test(Profiles profiles) {
                return profiles.matches(activeProfile);
            }
        };
    }
}

// Profile环境配置的工具类实现类
class ParsedProfiles implements Profiles {
    // spring.profiles中写的表达式字符串
    public final String[] expressions;
    // 解析完成的表达式对象,内部包含环境配置通过表达式描述的关系
    public final Profiles[] parsed;

    public ParsedProfiles(String[] expressions, Profiles[] parsed) {
        this.expressions = expressions;
        this.parsed = parsed;
    }

    /**
     * 使用解析好的表达式与激活配置环境的匹配器进行一一匹配
     *
     * @param activeProfiles 激活配置环境的匹配器
     */
    public boolean matches(Predicate<String> activeProfiles) {
        // 遍历解析到表达式进行逻辑匹配
        for (Profiles candidate : this.parsed) {
            // 使用给定的匹配器进行匹配
            // 实际上,activeProfiles才是真正进行匹配的条件
            // candidate是matches是根据表达式的关系进行逻辑运算,最终通过activeProfiles来处理,得到匹配结果
            /**
             * {@link ProfilesParser#and(Profiles...)}
             * {@link ProfilesParser#or(Profiles...)}
             * {@link ProfilesParser#not(Profiles)}
             * 最终一定会落到{@link ProfilesParser#equals(String)}中
             * {@link ProfilesParser#isMatch(Predicate)}
             */
            if (candidate.matches(activeProfiles)) {
                return true;
            }
        }
        return false;
    }
}

// Profile环境配置的工具类
interface Profiles {
    // 解析给定的环境配置表达式
    public static Profiles of(String... profiles) {
        // 解析spring.profiles的值,因为该值可以是一个表达式
        return ProfilesParser.parse(profiles);
    }

    /**
     * 条件匹配
     * {@link ParsedProfiles#matches(Predicate)}
     * {@link ProfilesParser#and(Profiles...)}
     * {@link ProfilesParser#or(Profiles...)}
     * {@link ProfilesParser#not(Profiles)}
     * 最终一定会落到{@link ProfilesParser#equals(String)}中
     * {@link ProfilesParser#isMatch(Predicate)}
     */
    boolean matches(Predicate<String> activeProfiles);
}

// 解析完的配置文件,包装为文档类型
class Document {
    // 解析到配置文件的所有属性值
    public final PropertySource<?> propertySource;
    /**
     * @see {@link ProfilesParser}
     * @see {@link ParsedProfiles#matches(Predicate)}
     * 从当前加载的配置文件对应的属性对象中,获取spring.profiles的属性值
     * 使用(),&(and),!(not),|(or)
     * (dev,pro),在环境对象中,dev和pro只要有一个设置了激活,当前document配置则保留
     * dev|pro,和(dev,pro)效果一样
     * dev&pro,在环境对对象中,dev和pro都设置了激活的情况下,当前document配置才保留,否则忽略该配置文件属性配置
     * !dev ,在环境对象中,dev没有设置激活,则保留当前document配置
     * <p>
     * 总结: 当配置文件中写了spring.profiles=xxx表达式,则满足这个表达式的条件,该配置文件才会生效,否则会被排除
     * 例如: application.properties中配置了spring.profiles = dev&pro,那么,只有dev和pro配置都被激活的情况下,application.properties才会被加载
     */
    public String[] profiles;
    // 从当前加载的配置文件对应的属性对象中,获取spring.profiles.active的属性值
    public final Set<Profile> activeProfiles;
    // 从当前加载的配置文件对应的属性对象中,获取spring.profiles.include的属性值
    public final Set<Profile> includeProfiles;

    public Document(PropertySource<?> propertySource, String[] profiles, Set<Profile> activeProfiles, Set<Profile> includeProfiles) {
        this.propertySource = propertySource;
        this.profiles = profiles;
        this.activeProfiles = activeProfiles;
        this.includeProfiles = includeProfiles;
    }

    // 从当前加载的配置文件对应的属性对象中,获取spring.profiles的属性值
    public String[] getProfiles() {
        return profiles;
    }

    // 解析到配置文件的所有属性值
    public PropertySource<?> getPropertySource() {
        return propertySource;
    }

    // 从当前加载的配置文件对应的属性对象中,获取spring.profiles.active的属性值
    public Set<Profile> getActiveProfiles() {
        return activeProfiles;
    }

    // 从当前加载的配置文件对应的属性对象中,获取spring.profiles.include的属性值
    public Set<Profile> getIncludeProfiles() {
        return includeProfiles;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值