Spring Boot 2.4中的配置文件处理

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

Spring Boot 2.4.0.M2 已发布,它带来了一些关于加载application.propertiesapplication.yml文件方式的有趣变化。

如果你的应用程序只使用单个application.propertiesapplication.yml文件,这种相当典型的设置,你可能不会注意到任何区别。但是,如果你的应用程序使用了更复杂的设置(例如特定于配置文件的属性),你可能需要继续阅读以了解我们做了哪些更改以及原因。

我们为什么要进行这些更改

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

卷挂载配置是Kubernetes的一项流行功能,其中使用ConfigMap指令将配置直接显示在文件系统上。你可以挂载包含多个键和值的组合的完整YAML文件,也可以使用更简单的目录树格式,其中文件名是键,文件的内容是值。

我们希望同时提供这两种支持,并以一种与我们现有的application.propertiesapplication.yml支持自然融合的方式。为此,我们需要修改令人头疼的ConfigFileApplicationListener类。

ConfigFileApplicationListener的问题

几年前,来自视频游戏《Trap Adventure 2》的一些有趣的片段开始流行起来。它们非常类似于软件中可能发生的情况。有时你只是发现自己有一些很难更改的代码区域。在Spring Boot中,ConfigFileApplicationListener最终变成了这些“陷阱冒险”之一。

这并不是说代码写得很糟糕,或者缺少测试。只是当我们向其中添加功能时,我们最终把自己弄得一团糟。

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

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

  • 很难知道文档将按什么顺序添加。

以下是一个示例

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"

多文档属性文件

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

例如,上述YAML代码段的属性等效项将是

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 # 将失败 test=overridden value

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

配置文件组

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

使用多个独立的配置文件可能会使您的代码更容易理解,但在部署方面并不理想。您不希望强制用户记住他们必须始终同时激活 `proddb`、`prodmq`、`prodmetrics` 等。相反,您只需要他们能够激活单个 `prod` 配置文件即可。组允许您做到这一点。

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

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

导入附加配置

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

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

使用最新的里程碑版本,您可以在 `application.properties` 或 `application.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.application` 和 `test` 属性。`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` 属性中指定的 location 字符串是完全可插入的,可以通过编写一些自定义类来扩展。我们预计第三方库将来可能会开始支持自定义位置。例如,您可以想象第三方 jar 支持 `archaius://…​`、`vault://…​` 或 `zookeeper://…​` 等位置。

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

使用旧版处理

如果您正在升级现有的 Spring Boot 应用程序,并且您不希望使用所有这些新功能,您可以切换回旧的处理器。为此,您可以将 `spring.config.use-legacy-processing` 设置为 `true` 到您的 `application.properties` 或 `application.yml` 文件中。这应该可以让您获得与 Spring Boot 2.3 应用程序相同的应用程序配置处理。

如果您确实发现需要切换到旧版处理,因为我们错过了一个特定的用例,请在 GitHub 上提出问题,我们将尝试解决它。

总结

我们希望新的配置数据处理类有用,并且不会造成太多升级难题。如果您想了解更多信息,您可以查看更新的参考文档

获取 Spring 新闻通讯

与 Spring 新闻通讯保持联系

订阅

领先一步

VMware 提供培训和认证,以加快您的进度。

了解更多

获取支持

Tanzu Spring在一个简单的订阅中提供对OpenJDK™、Spring和Apache Tomcat®的支持和二进制文件。

了解更多

即将举行的活动

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

查看全部