先行一步
VMware 提供培训和认证,助您快速提升。
了解更多大家好,Spring 粉丝们!欢迎来到新一期的 Spring 技巧!在本期中,我们将探讨一个基础且我希望早些时候就已涉及的主题:配置。不,我不是指函数式配置或 Java 配置之类的东西,我指的是那些决定你的代码如何执行的字符串值。就是你放在 application.properties 中的那些配置。
Spring 中的所有配置都源自 Spring 的 Environment
抽象。Environment
有点像一个字典——一个包含键值对的 Map。Environment
只是一个接口,通过它我们可以询问关于,你知道,Environment
的问题。这个抽象存在于 Spring Framework 中,并在十多年前的 Spring 3 中引入。在那之前,有一个专门的机制来集成配置,称为属性占位符解析(property placeholder resolution)。现在的环境机制以及围绕该接口的一系列类完全取代了旧的支持。如果你发现有博客仍然使用那些旧类型,我建议你转向更新更好的领域?:)
我们开始吧。前往 Spring Initializr 生成一个新项目,确保选择 Spring Cloud Vault
、Lombok
和 Spring Cloud Config Client
。我将我的项目命名为 configuration
。点击 Generate
生成应用程序。在您常用的 IDE 中打开项目。如果您想跟着操作,请务必禁用 Spring Cloud Vault 和 Spring Cloud Config Client 的依赖项。我们暂时不需要它们。
大多数 Spring Boot 开发者第一步会使用 application.properties。甚至你在 Spring Initializr 生成新项目时,它都会在 src/main/resources/
文件夹中放入一个空的 application.properties
文件!非常方便。你一定是在 Spring Initializr 上创建项目的,对吧?你可以使用 application.properties 或 application.yml。我个人不太喜欢 .yml
文件,但如果你更喜欢它,也可以使用。
Spring Boot 在启动时会自动加载 application.properties
。你可以在 Java 代码中通过 Environment 解引用(获取)属性文件中的值。在 application.properties
文件中放入一个属性,像这样。
message-from-application-properties=Hello from application.properties
现在,让我们编辑代码来读取那个值。
package com.example.configuration;
import lombok.extern.log4j.Log4j2;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
@Log4j2
@SpringBootApplication
public class ConfigurationApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigurationApplication.class, args);
}
@Bean
ApplicationRunner applicationRunner(Environment environment) {
return args -> {
log.info("message from application.properties " + environment.getProperty("message-from-application-properties"));
};
}
}
运行它,你会在日志输出中看到配置属性文件中的值。如果你想改变 Spring Boot 默认读取的文件,你也可以做到。不过这是一个鸡生蛋蛋生鸡的问题——你需要指定一个 Spring Boot 将用来确定加载所有属性的位置的属性。所以你需要将此属性指定在 application.properties 文件之外。你可以使用程序参数或环境变量来填充 spring.config.name
属性。
export SPRING_CONFIG_NAME=foo
现在在作用域内使用该环境变量重新运行应用程序,它将失败,因为它会尝试加载 foo.properties
,而不是 application.properties
。
顺便说一句,你也可以使用位于应用程序 *外部*,紧邻 jar 包的配置来运行应用程序,像这样。如果你这样运行应用程序,外部 application.properties
文件中的值将覆盖 .jar
文件内部的值。
.
├── application.properties
└── configuration-0.0.1-SNAPSHOT.jar
0 directories, 2 files
Spring Boot 也了解 Spring profile(配置文件)。Profile 是一种机制,允许你标记对象和属性文件,以便在运行时选择性地激活或禁用它们。如果你想拥有特定于环境的配置,这非常有用。你可以将 Spring bean 或配置文件标记为属于特定的 profile,当该 profile 被激活时,Spring 会自动为你加载它。
Profile 名称基本是任意的。有些 profile 是神奇的——Spring 会以特定方式处理它们。其中最有趣的是 default
,当没有其他 profile 处于激活状态时,它会被激活。但一般来说,名称取决于你。我发现将我的 profile 映射到不同的环境(如 dev
、qa
、staging
、prod
等)非常有用。
假设有一个名为 dev
的 profile。Spring Boot 将自动加载 application-dev.properties
。它会在加载 application.properties 的基础上加载它。如果两个文件中的值有冲突,那么更具体的文件——带有 profile 的那个——将优先。你可以有一个在没有特定 profile 时应用的默认值,然后在特定 profile 的配置中提供具体的值。
有几种不同的方式可以激活给定的 profile,但最简单的方式就是在命令行上指定。或者你可以在 IDE 的运行配置对话框中启用它。IntelliJ 和 Spring Tool Suite 都提供了一个地方来指定运行应用程序时要使用的 profile。你也可以设置一个环境变量 SPRING_PROFILES_ACTIVE
,或者在命令行上指定一个参数 --spring.profiles.active
。两者都接受逗号分隔的 profile 列表——你可以同时激活多个 profile。
我们来试试。创建一个名为 application-dev.properties
的文件。在其中放入以下值。
message-from-application-properties=Hello from dev application.properties
这个属性的键与 application.properties
中的那个相同。这里的 Java 代码与我们之前的完全相同。只需确保在启动 Spring 应用程序之前指定 profile。你可以使用环境变量、属性文件等。你甚至可以在 main()
方法中构建 SpringApplication
时以编程方式定义它。
package com.example.configuration.profiles;
import lombok.extern.log4j.Log4j2;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
@Log4j2
@SpringBootApplication
public class ConfigurationApplication {
public static void main(String[] args) {
// this works
// export SPRING_PROFILES_ACTIVE=dev
// System.setProperty("spring.profiles.active", "dev"); // so does this
new SpringApplicationBuilder()
.profiles("dev") // and so does this
.sources(ConfigurationApplication.class)
.run(args);
}
@Bean
ApplicationRunner applicationRunner(Environment environment) {
return args -> {
log.info("message from application.properties " + environment.getProperty("message-from-application-properties"));
};
}
}
运行应用程序,你会在输出中看到特殊消息的反映。
到目前为止,我们一直使用 Environment 来注入配置。你也可以使用 @Value
注解将值注入为参数。你可能已经知道了。但你知道你也可以指定默认值,以便在没有其他匹配值时返回吗?有很多原因你可能想这样做。你可以用它来提供回退值,并在有人拼写错误时使其更加透明。它也很有用,因为你获得了一个可能有用的值,即使有人不知道他们需要激活某个 profile 或其他东西。
package com.example.configuration.value;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@Log4j2
@SpringBootApplication
public class ConfigurationApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigurationApplication.class, args);
}
@Bean
ApplicationRunner applicationRunner(
@Value("${message-from-application-properties:OOPS!}") String valueDoesExist,
@Value("${mesage-from-application-properties:OOPS!}") String valueDoesNotExist) {
return args -> {
log.info("message from application.properties " + valueDoesExist);
log.info("missing message from application.properties " + valueDoesNotExist);
};
}
}
很方便吧?另外请注意,你提供的默认 String 反过来可以插入其他属性。所以你可以这样做,假设你的应用程序配置中某处确实存在一个名为 default-error-message
的键
${message-from-application-properties:${default-error-message:YIKES!}}
它会先评估第一个属性(如果存在),然后是第二个,最后是字符串 YIKES!
。
之前,我们讨论了如何使用环境变量或程序参数来指定 profile。这种机制——使用环境变量或程序参数来配置 Spring Boot——是通用的。你可以对任何任意键使用它,Spring Boot 会为你规范化配置。任何你可以放入 application.properties 中的键都可以通过这种方式从外部指定。让我们看一些例子。假设你想指定数据源连接的 URL。你 *可以* 将该值硬编码在 application.properties 中,但这不太安全。更好的做法是创建一个仅在生产环境中存在的环境变量。这样,开发者就无法访问生产数据库的密钥等等。
我们来试试。这是示例的 Java 代码。
package com.example.configuration.envvars;
import lombok.extern.log4j.Log4j2;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
@Log4j2
@SpringBootApplication
public class ConfigurationApplication {
public static void main(String[] args) {
// simulate program arguments
String[] actualArgs = new String[]{"spring.datasource.url=jdbc:postgres://localhost/some-prod-db"};
SpringApplication.run(ConfigurationApplication.class, actualArgs);
}
@Bean
ApplicationRunner applicationRunner(Environment environment) {
return args -> {
log.info("our database URL connection will be " + environment.getProperty("spring.datasource.url"));
};
}
}
在运行它之前,请确保在你用来运行应用程序的 shell 中导出环境变量,或指定程序参数。我通过拦截传递给 Spring Boot 应用程序的 public static void main(String [] args)
来模拟后者——程序参数。你也可以像这样指定一个环境变量:
export SPRING_DATASOURCE_URL=some-arbitrary-value
mvn -DskipTests=true spring-boot:run
多次运行程序,尝试不同的方法,你将在输出中看到这些值。应用程序中没有连接数据库的自动配置,所以我们用这个属性作为示例。这个 URL 不必是有效的 URL(至少在你向 classpath 中添加 Spring 的 JDBC 支持和 JDBC 驱动程序之前)。
Spring Boot 在获取值方面非常灵活。它不在乎你使用 SPRING_DATASOURCE_URL
、spring.datasource.url
等等。Spring Boot 将这称为 *松散绑定*(relaxed binding)。它允许你以不同环境中最自然的方式进行操作,同时仍然适用于 Spring Boot。
将应用程序配置从环境中外部化的这个想法并非新事物。它在 12 要素宣言中得到了充分理解和描述。12 要素宣言指出,特定于环境的配置应该存在于该环境中,而不是在代码本身中。这是因为我们希望所有环境使用一个构建版本。变化的事物应该是外部化的。到目前为止,我们已经看到 Spring Boot 可以从命令行参数(程序参数)和环境变量中引入配置。它还可以读取来自 JOpt 的配置。如果你碰巧运行在一个有 JNDI 上下文的应用服务器中,它甚至可以从 JNDI 上下文中获取!
Spring Boot 引入任何环境变量的能力在这里很有益。它也比使用程序参数更安全,因为程序参数会显示在操作系统工具的输出中。环境变量更适合。
到目前为止,我们已经看到 Spring Boot 可以从许多不同的地方引入配置。它了解 profile,它了解 .yml
和 .properties
。它非常灵活!但如果它不知道如何做你想做的事情呢?你可以使用自定义的 PropertySource<T>
轻松地教它新的技巧。如果你想例如,将应用程序与存储在外部数据库、目录或其他 Spring Boot 不会自动了解的事物中的配置集成,你可能希望这样做。
package com.example.configuration.propertysource;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.PropertySource;
@Log4j2
@SpringBootApplication
public class ConfigurationApplication {
public static void main(String[] args) {
new SpringApplicationBuilder()
.sources(ConfigurationApplication.class)
.initializers(context -> context
.getEnvironment()
.getPropertySources()
.addLast(new BootifulPropertySource())
)
.run(args);
}
@Bean
ApplicationRunner applicationRunner(@Value("${bootiful-message}") String bootifulMessage) {
return args -> {
log.info("message from custom PropertySource: " + bootifulMessage);
};
}
}
class BootifulPropertySource extends PropertySource<String> {
BootifulPropertySource() {
super("bootiful");
}
@Override
public Object getProperty(String name) {
if (name.equalsIgnoreCase("bootiful-message")) {
return "Hello from " + BootifulPropertySource.class.getSimpleName() + "!";
}
return null;
}
}
上述示例是注册 PropertySource
的最安全方式,它注册得足够早,所有需要它的东西都能找到它。你也可以在运行时进行,当 Spring 开始连接对象并且你可以访问已配置的对象时,但我不确定这是否在所有情况下都奏效。这里展示了它可能的样子。
package com.example.configuration.propertysource;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertySource;
@Log4j2
@SpringBootApplication
public class ConfigurationApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigurationApplication.class, args);
}
@Bean
ApplicationRunner applicationRunner(@Value("${bootiful-message}") String bootifulMessage) {
return args -> {
log.info("message from custom PropertySource: " + bootifulMessage);
};
}
@Autowired
void contributeToTheEnvironment(ConfigurableEnvironment environment) {
environment.getPropertySources().addLast(new BootifulPropertySource());
}
}
class BootifulPropertySource extends PropertySource<String> {
BootifulPropertySource() {
super("bootiful");
}
@Override
public Object getProperty(String name) {
if (name.equalsIgnoreCase("bootiful-message")) {
return "Hello from " + BootifulPropertySource.class.getSimpleName() + "!";
}
return null;
}
}
到目前为止,我们几乎完全着眼于如何从别处获取属性值。然而,我们还没有讨论这些字符串一旦进入我们的工作内存并可供应用程序使用后会发生什么。大多数时候,它们只是字符串,我们可以按原样使用它们。然而,有时将它们转换为其他类型的值——int、Date、double 等等——是很有用的。这项工作——将字符串转换为其他类型——可以成为另一整期 Spring Tips 视频的主题,也许我很快就会做。简而言之,这里有很多相互关联的部分——ConversionService
、Converter<T>
、Spring Boot 的 Binder
等等。对于常见情况,这通常会正常工作。例如,你可以指定一个属性 server.port = 8080
,然后将其作为 int 注入到你的应用程序中:
@Value("${server.port}") int port
自动将这些值绑定到一个对象可能很有帮助。这正是 Spring Boot 的 ConfigurationProperties
为你所做的。让我们看看实际效果。
假设你有一个 application.properties 文件,包含以下属性:
bootiful.message = Hello from a @ConfiguratinoProperties
然后你可以运行应用程序,看到配置值已经为我们绑定到了对象上。
package com.example.configuration.cp;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
@Log4j2
@SpringBootApplication
@EnableConfigurationProperties(BootifulProperties.class)
public class ConfigurationApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigurationApplication.class, args);
}
@Bean
ApplicationRunner applicationRunner(BootifulProperties bootifulProperties) {
return args -> {
log.info("message from @ConfigurationProperties " + bootifulProperties.getMessage());
};
}
}
@Data
@RequiredArgsConstructor
@ConstructorBinding
@ConfigurationProperties("bootiful")
class BootifulProperties {
private final String message;
}
BootifulProperties
对象上的 @Data
和 @RequiredArgsConstructor
注解来自 Lombok。@Data
合成 final 字段的 getter 方法以及非 final 字段的 getter 和 setter 方法。@RequiredArgsConstructor
合成类中所有 final 字段的构造函数。结果是一个通过构造函数初始化后不可变的对象。Spring Boot 的 ConfigurationProperties 机制默认不知道不可变对象;你需要使用 @ConstructorBinding
注解(Spring Boot 的一个相当新的补充)来使其在这里正确工作。这在其他编程语言(如 Kotlin(data class ...
)和 Scala(case class ...
))中更有用,这些语言有创建不可变对象的语法糖。
我们已经看到 Spring 可以加载紧邻应用程序 .jar
包的配置,并且可以从环境变量和程序参数加载配置。将信息导入到 Spring Boot 应用程序并不困难,但这有点零散。环境变量难以进行版本控制,程序参数也难以保护安全。
为了解决其中一些问题,Spring Cloud 团队构建了 Spring Cloud Config Server。Spring Cloud Config Server 是一个 HTTP API,它连接着后端存储引擎。存储是可插拔的,最常见的是 Git 仓库,但也支持其他方式。这些包括 Subversion、本地文件系统,甚至 MongoDB。
我们将设置一个新的 Spring Cloud Config Server。前往 Spring Initializr,选择 Config Server
,然后点击 Generate
。在您常用的 IDE 中打开它。
我们需要做两件事才能使其工作:首先,我们必须使用一个注解,然后提供一个配置值来指向包含我们配置文件的 Git 仓库。这是 application.properties
:
spring.cloud.config.server.git.uri=https://github.com/joshlong/greetings-config-repository.git
server.port=8888
这是你的主类应该看起来的样子:
package com.example.configserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
@EnableConfigServer
@SpringBootApplication
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
运行应用程序——mvn spring-boot:run
或者直接在您常用的 IDE 中运行应用程序。它现在可用了。它将充当 Github 仓库中 Git 配置的代理。然后其他客户端可以使用 Spring Cloud Config Client 从 Spring Cloud Config Server 中引入他们的配置,而后者又会从 Git 仓库中引入配置。注意:为了演示方便,我使其尽可能不安全,但你应该(并且能够)保护链条中的这两个环节——从配置客户端到配置服务器,以及从配置服务器到 Git 仓库。Spring Cloud Config Server、Spring Cloud Config Client 和 Github 都能很好地协同工作,并且安全。
现在,回到我们的配置应用程序的构建文件,确保取消注释 Spring Cloud Config Client 依赖项。要启动 Spring Cloud Config Server,它需要一些——你猜对了!——配置。这是一个经典的鸡生蛋蛋生鸡的问题。这个配置需要在其他配置之前更早被评估。你可以将这个配置放在一个名为 bootstrap.properties
的文件中。
你需要标识你的应用程序,给它一个名字,这样当它连接到 Spring Cloud Config Server 时,它就知道要为我们提供哪个配置。我们在这里指定的名称将匹配到 Git 仓库中的一个属性文件。这是你应该放入文件中的内容:
spring.cloud.config.uri=http://localhost:8888
spring.application.name=bootiful
现在我们可以读取 Git 仓库中 bootiful.properties 文件中的任何我们想要的值,该文件的内容是:
message-from-config-server = Hello, Spring Cloud Config Server
我们可以像这样引入那个配置文件:
package com.example.configuration.configclient;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@Log4j2
@SpringBootApplication
public class ConfigurationApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigurationApplication.class, args);
}
@Bean
ApplicationRunner applicationRunner(@Value("${message-from-config-server}") String configServer) {
return args -> {
log.info("message from the Spring Cloud Config Server: " + configServer);
};
}
}
你应该在输出中看到这个值。不错!Spring Cloud Config Server 为我们做了很多很酷的事情。它可以为我们加密值。它可以帮助属性进行版本控制。我最喜欢的一件事是,你可以独立于代码库的更改来更改配置。你可以将其与 Spring Cloud 的 @RefreshScope
结合使用,在应用程序启动运行后动态地重新配置它。(我应该做一个关于 refresh scope 及其各种用途的视频...)Spring Cloud Config Server 是 Spring Cloud 模块中最受欢迎的模块之一,这是有原因的——它可以用于单体应用,也可以用于微服务。
如果配置得当,Spring Cloud Config Server 可以加密属性文件中的值。它确实有效。很多人也使用 Hashicorp 出色的 Vault 产品,这是一款功能更全面的安全产品。Vault 可以使用 UI、CLI 或 HTTP API 来安全地保护、存储和严格控制对 token、密码、证书、加密密钥以及其他敏感数据的访问。你也可以使用 Spring Cloud Vault 项目轻松地将其用作属性源。取消注释构建文件中的 Spring Cloud Vault 依赖项,让我们看看如何设置 Hashicorp Vault。
下载最新版本,然后运行以下命令。我假设您使用的是 Linux 或类 Unix 环境。虽然可能需要一些翻译,但应该很容易转换为 Windows。我不会试图解释 Vault 的所有内容;相反,我建议您参考 Hashicorp Vault 出色的入门指南。这里是我所知的最不安全但最快的方式来设置并使其工作。首先,运行 Vault 服务器。我在这里提供了一个根 token,但通常你会使用 Vault 在启动时提供的 token。
export VAULT_ADDR="https://localhost:8200"
export VAULT_SKIP_VERIFY=true
export VAULT_TOKEN=00000000-0000-0000-0000-000000000000
vault server --dev --dev-root-token-id="00000000-0000-0000-0000-000000000000"
一旦启动,在另一个 shell 中,向 Vault 服务器安装一些值,像这样:
export VAULT_ADDR="http://localhost:8200"
export VAULT_SKIP_VERIFY=true
export VAULT_TOKEN=00000000-0000-0000-0000-000000000000
vault kv put secret/bootiful message-from-vault-server="Hello Spring Cloud Vault"
这会将键 message-from-vault-server
及其值 Hello Spring Cloud Vault
放入 Vault 服务中。现在,让我们修改我们的应用程序,使其连接到该 Vault 实例以读取安全值。像使用 Spring Cloud Config Client 一样,我们需要一个 bootstrap.properties 文件。
spring.application.name=bootiful
spring.cloud.vault.token=${VAULT_TOKEN}
spring.cloud.vault.scheme=http
然后,你就可以像使用任何其他配置值一样使用这个属性了。
package com.example.configuration.vault;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@Log4j2
@SpringBootApplication
public class ConfigurationApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigurationApplication.class, args);
}
@Bean
ApplicationRunner applicationRunner(@Value("${message-from-vault-server:}") String valueFromVaultServer) {
return args -> {
log.info("message from the Spring Cloud Vault Server : " + valueFromVaultServer);
};
}
}
现在,在你运行它之前,请确保也配置了与我们两次与 vault
CLI 交互时使用的相同的三个环境变量:VAULT_TOKEN
、VAULT_SKIP_VERIFY
和 VAULT_ADDR
。然后运行它,你应该在控制台上看到你写入 Hashicorp Vault 的值被反映出来。
希望你学到了关于 Spring 中丰富多彩且引人入胜的配置世界的一些知识。掌握了这些信息,你现在可以更好地使用其他支持属性解析的项目。有了这些工作原理的知识,你就可以集成来自不同 Spring 集成的配置了,而这样的集成数量非常多!你可能会使用 Spring Cloud Netflix 的 Archaius 集成,或者 Spring Cloud Kubernetes 的 Configmaps 集成,或者 Spring Cloud GCP 的 Google Runtime Configuration API 集成,或者 Spring Cloud Azure 的 Microsoft Azure Key Vault 集成,等等。
我在这里只提到了少数几个产品/集成,但这并不重要,它们的用法将是相同的,如果集成正确的话:云是极限!