如何管理共享库中的 spring-cloud bootstrap 属性?

how to manage spring-cloud bootstrap properties in a shared library?

我正在构建一个库,它为使用我们的 Spring Cloud Config/Eureka 设置的应用程序提供了一个自以为是的配置。我们的想法是将此配置作为自定义启动器提供,在单个微服务应用程序中很少或没有 spring 与云相关的样板文件。

此时,我想放入此库的大部分共享配置都包含 bootstrap.yml 中的内容。我想在我的自定义启动器中提供 bootstrap.yml,但使用该库的应用程序仍然需要能够提供它们自己的 bootstrap.yml,即使只是这样它们才能正确设置它们的 spring.application.name .

由于 bootstrap.yml 从类路径加载的方式,如果应用程序有自己的 bootstrap.yml,Spring 似乎会忽略共享库中的那个。由于 bootstrap 上下文对待 ApplicationContextInitializers.

的特殊方式,我什至不能使用 ApplicationContextInitializer 来自定义环境

有人对适用于此的方法有任何建议吗?我想提供一个嵌入式库,使我们固执己见的 bootstrap 配置无需在我们所有项目中复制样板 bootstrap.yml

我找到了解决方法。此解决方案的目标是:

  • 从共享库中的 yaml 文件加载值。
  • 允许使用该库的应用程序引入它们自己的 bootstrap.yml,该库也加载到环境中。
  • bootstrap.yml 中的值应覆盖共享 yaml 中的值。

主要挑战是在应用程序生命周期的适当时间点注入一些代码。具体来说,我们需要在将 bootstrap.yml PropertySource 添加到环境之后(以便我们可以以相对于它的正确顺序注入我们的自定义 PropertySource),而且在应用程序开始配置 bean 之前(作为我们的配置价值观控制行为)。

我找到的解决方案是使用自定义 EnvironmentPostProcessor

public class CloudyConfigEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered {

    private YamlPropertySourceLoader loader;

    public CloudyConfigEnvironmentPostProcessor() {
        loader = new YamlPropertySourceLoader();
    }

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment env, SpringApplication application) {
        //ensure that the bootstrap file is only loaded in the bootstrap context
        if (env.getPropertySources().contains("bootstrap")) {
            //Each document in the multi-document yaml must be loaded separately.
            //Start by loading the no-profile configs...
            loadProfile("cloudy-bootstrap", env, null);
            //Then loop through the active profiles and load them.
            for (String profile: env.getActiveProfiles()) {
                loadProfile("cloudy-bootstrap", env, profile);
            }
        }
    }

    private void loadProfile(String prefix, ConfigurableEnvironment env, String profile) {
        try {
            PropertySource<?> propertySource = loader.load(prefix + (profile != null ? "-" + profile: ""), new ClassPathResource(prefix + ".yml"), profile);
            //propertySource will be null if the profile isn't represented in the yml, so skip it if this is the case.
            if (propertySource != null) {
                //add PropertySource after the "applicationConfigurationProperties" source to allow the default yml to override these.
                env.getPropertySources().addAfter("applicationConfigurationProperties", propertySource);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public int getOrder() {
        //must go after ConfigFileApplicationListener
        return Ordered.HIGHEST_PRECEDENCE + 11;
    }

}

此自定义 EnvironmentPostProcessor 可以通过 META-INF/spring.factories:

注入
#Environment PostProcessors
org.springframework.boot.env.EnvironmentPostProcessor=\
com.mycompany.cloudy.bootstrap.autoconfig.CloudyConfigEnvironmentPostProcessor

有几点需要注意:

  • YamlPropertySourceLoader 按配置文件加载 yaml 属性,因此如果您使用的是多文档 yaml 文件,您实际上需要从中单独加载每个配置文件,包括无配置文件配置。
  • ConfigFileApplicationListener 是 EnvironmentPostProcessor,负责将 bootstrap.yml(或常规上下文的 application.yml)加载到环境中,因此为了相对于 [=41= 正确定位自定义 yaml 属性] 属性优先级,您需要在 ConfigFileApplicationListener 之后订购自定义 EnvironmentPostProcessor。

编辑:我最初的回答没有用。我正在用这个替换它,确实如此。

您可以使用 META-INF/spring.factories 文件中的 org.springframework.cloud.bootstrap.BootstrapConfiguration 键将共享库中的 PropertySource 添加到 bootstrap 属性。

例如,您可以创建一个包含以下内容的库:

src/main/java/com/example/mylib/MyLibConfig.java

package com.example.mylib;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

@Configuration
@PropertySource("classpath:mylib-config.properties")
public class MyLibConfig {
}

src/main/resources/mylib-config.properties

eureka.instance.public=true
# or whatever...

src/main/resources/META-INF/spring.factories

org.springframework.cloud.bootstrap.BootstrapConfiguration=com.example.mylib.MyLibConfig

更多详情:http://projects.spring.io/spring-cloud/spring-cloud.html#_customizing_the_bootstrap_configuration