Spring Boot 2.4 中的配置文件处理

工程 | Phil Webb | 2020年8月14日 | ...

Spring Boot 2.4.0.M2 刚刚发布,它带来了关于 `application.properties` 和 `application.yml` 文件加载方式的一些有趣的改变。

如果您的应用程序使用的是最常见的设置,即只使用单个 `application.properties` 或 `application.yml` 文件,那么您可能不会注意到任何区别。但是,如果您有使用更复杂设置的应用程序(例如特定于配置文件的属性),您可能需要继续阅读以了解我们所做的更改以及原因。

我们为何要做出这些改变

在最近的 Spring Boot 版本中,我们一直在努力改进我们的 Kubernetes 支持。我们在 Spring Boot 2.3 中想要添加但未能实现的一项功能是支持卷挂载的配置。

卷挂载配置是 Kubernetes 的一项流行功能,其中 ConfigMap 指令用于将配置直接暴露在文件系统中。您可以挂载一个包含多个键值对的完整 YAML 文件,或者使用更简单的目录树格式,其中文件名是键,文件内容是值。

我们希望同时支持这两种方式,并以一种感觉自然的方式使用,与我们现有的 application.propertiesapplication.yml 支持结合使用。为此,我们需要触及那个令人畏惧的 ConfigFileApplicationListener 类。

ConfigFileApplicationListener 的问题

几年前,一些有趣的 来自电子游戏 Trap Adventure 2 的片段开始流传。它们很好地类比了软件中可能发生的情况。有时你会发现自己面对的是一些很难更改的代码区域。在 Spring Boot 中,ConfigFileApplicationListener 最终变成了这些“陷阱冒险”之一。

并非代码写得不好,或缺少测试。只是随着我们向其中添加功能,我们最终陷入了困境。

我们对这段代码的主要两个问题与特定配置文件的文档(主要是 YAML)有关。 namely

  • 您可以从特定配置文件的文档中启用其他配置文件。

  • 很难知道文档的添加顺序。

以以下示例为例

security.user.password: usera

spring.profiles: local security.user.password: userb runlocal: true

spring.profiles: !dev spring.profiles.include: local security.user.password: userc

这里我们有一个 多文档 YAML 文件(一个由 --- 分隔的三个逻辑文档组成的文件)。

如果使用 --spring.profiles.active=prod 运行,security.user.password 的值是多少?runlocal 属性是否已设置?你确定吗?由于在处理中间文档时配置文件尚未激活,因此该文档甚至都没有被包含进来吗?

我们经常收到关于此文件处理逻辑的问题,但每当我们尝试修复它们时,总会有一些东西会破坏其他人的东西。我们最终决定唯一的出路是重新考虑整个事情。

因此,在 Spring Boot 2.4 中,我们计划对属性和 YAML 文件加载方式进行两项重大更改:

  1. 文档将按照它们定义的顺序加载。

  2. 配置文件将不再从特定配置文件的文档中激活。

文档顺序

从 Spring Boot 2.4 开始,在加载属性和 YAML 文件时将有一个简单的规则。文件中较低位置声明的属性将覆盖较高位置的属性。

这遵循了纯 .properties 文件已经使用的相同排序规则。可以认为每一行都会将一个条目放入一个 Map。当新的具有相同键的值被放入 Map 时,任何现有条目都会被替换。

遵循这些规则,对于多文档 YAML 文件,较低的文档将覆盖较高的文档中的值。

test: "value"

test: "overridden-value"

多文档 Properties 文件

在 Spring Boot 2.4 中,我们决定为 Java properties 文件引入类似 YAML 的多文档支持。多文档 properties 文件使用注释 (#) 后跟熟悉的三个破折号表示法来分割文档(我们选择使用注释是为了不破坏现有的 IDE 工具)。

例如,上面 YAML 片段的 properties 等效写法是

test=value #--- test=overridden-value

特定配置文件的文档

上面的例子有点人工化,因为总是覆盖一个值没有多大意义。更常见的设置是声明第二个文档仅在特定配置文件下激活。

在 Spring Boot 2.3 中,您将使用 spring.profiles 键来实现此目的。在 Spring Boot 2.4 中,我们决定将属性更改为 spring.config.activate.on-profile

例如,如果我们只想在 dev 配置文件激活时覆盖 test,我们可以使用以下方式:

test=value #--- spring.config.activate.on-profile=dev test=overridden-value

配置文件激活

您仍然可以使用 spring.profiles.active 属性来从 application.propertiesapplication.yaml 文件中激活或包含配置文件。

例如,以下内容是完全有效的:

test=value spring.profiles.active=local #--- spring.config.activate.on-profile=dev test=overridden value

您不再允许做的一件事是将该属性与 spring.config.activate.on-profile 结合使用。例如,以下文件现在将引发异常:

test=value #--- spring.config.activate.on-profile=dev spring.profiles.active=local # will fail test=overridden value

我们希望这项新限制最终能使您的 application.propertiesapplication.yml 文件更易于理解和分析。我们也希望它能使 Spring Boot 本身更容易管理和维护。但是,我们确实注意到至少有一种有效的用例,其中人们希望将一个配置文件扩展到多个子配置文件。为了支持这一点,我们正在添加一个名为“配置文件组”的功能。

配置文件组

配置文件组是 Spring Boot 2.4 的一项新功能,允许您将单个配置文件扩展为多个子配置文件。例如,假设您有一组复杂的 @Configuration 类,您使用 @Profile 注解有条件地启用它们。您可能有数据库配置 @Profile("proddb"),消息配置 @Profile("prodmq") 等等。

使用多个独立的配置文件可能使您的代码更易于理解,但对于部署来说并不理想。您不希望强制用户记住他们必须始终同时激活 proddbprodmqprodmetrics 等。相反,您只希望他们能够激活一个 prod 配置文件。组允许您做到这一点。

要定义组,您可以在 application.propertiesapplication.yml 文件中使用 spring.profiles.group 属性。例如:

spring.profiles.group.prod=proddb,prodmq,prodmetrics

导入其他配置

现在我们已经解决了配置文件的基本问题,我们终于可以考虑我们想要提供的新功能了。我们在 Spring Boot 2.4 中交付的主要功能是支持导入其他配置。

在早期版本的 Spring Boot 中,除了 application.propertiesapplication.yml 之外,导入其他属性或 yaml 文件非常困难。您可以使用 spring.config.additional-location 属性,但需要很早设置它,并且它在处理的文件类型方面非常有限。

通过最新的里程碑,您可以在 application.propertiesapplication.yml 文件中直接使用新的 spring.config.import 属性。例如,您可能希望导入一个“git 忽略”的 developer.properties 文件,以便您团队中的任何开发人员都可以快速更改仅针对他们个人的属性。

application.name=myapp spring.config.import=developer.properties

您甚至可以将 spring.config.import 声明与 spring.config.activate.on-profile 属性结合使用。例如,如果 prod 配置文件处于激活状态,我们仅在此处加载 prod.properties

spring.config.activate.on-profile=prod spring.config.import=prod.properties

导入可以被视为添加到声明它们的文档下面的其他文档。它们遵循与常规多文档文件相同的自顶向下排序规则:无论声明多少次,导入都只会被导入一次。

卷挂载配置树

导入定义使用类 URL 的语法作为其值。如果您的位置没有前缀,它将被视为常规文件或文件夹。但是,如果您使用 configtree: 前缀,您就是在告诉 Spring Boot,您期望在该位置有一个 Kubernetes 风格的卷挂载配置树。

例如,您可以在 application.properties 中声明以下内容:

spring.config.import=configtree:/etc/config

如果您有以下挂载的内容:

etc/ +- config/ +- my/ | +- application +- test

您将在 Spring Environment 中获得 my.applicationtest 属性。my.application 的值将是 /etc/config/my/application 的内容,而 test 的值将是 /etc/config/test 的内容。

云平台激活

如果您只想在特定云平台上激活卷挂载的配置树(或任何其他属性),可以使用 spring.config.activate.on-cloud-platform 属性。这与 spring.config.activate.on-profile 属性的工作方式类似,但使用的是 CloudPlatform 值,而不是配置文件名。

如果我们只想在部署到 Kubernetes 时启用上面的 configtree 示例,我们可以这样做:

spring.config.activate.on-cloud-platform=kubernetes spring.config.import=configtree:/etc/config

支持其他位置

spring.config.import 属性中指定的字符串位置是完全可插拔的,可以通过编写一些自定义类来扩展。我们预计第三方库将来可能会开始支持自定义位置。例如,您可以设想第三方 jar 支持 archaius://…​vault://…​zookeeper://…​ 等位置。

如果您有兴趣添加其他位置支持,请查看 org.springframework.boot.context.config 包中的 ConfigDataLocationResolverConfigDataLoader 的 javadoc。

使用旧版处理

如果您正在升级现有的 Spring Boot 应用程序,并且您觉得使用所有这些新功能还不舒服,您可以切换回旧的处理器。要做到这一点,您可以在 application.propertiesapplication.yml 文件中将 spring.config.use-legacy-processing 设置为 true。这应该会使您的应用程序配置处理与 Spring Boot 2.3 应用程序完全相同。

如果您发现需要切换到旧版处理,因为我们遗漏了特定的用例,请在 GitHub 上 提交一个问题,我们将尽力解决。

总结

我们希望新的配置数据处理类能有所帮助,并且它们不会导致太多的升级痛苦。如果您想了解更多关于它们的信息,可以查看更新的 参考文档

获取 Spring 新闻通讯

通过 Spring 新闻通讯保持联系

订阅

领先一步

VMware 提供培训和认证,助您加速进步。

了解更多

获得支持

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件,只需一份简单的订阅。

了解更多

即将举行的活动

查看 Spring 社区所有即将举行的活动。

查看所有