2.4版后管理弹簧引导属性
#java #kotlin #spring #jvm

在多模块应用程序中管理弹簧启动属性可能会非常令人生畏。在这篇文章中,我们看看Spring Boot将如何解决属性冲突以及如何使我们的构建更加一致。版本2.4中引入的更改将帮助我们。让我们回顾一下如何利用新功能并将其纳入项目。

更好地理解2.4版带来的版本2.4我们需要退后一步,并提醒自己在2.4发布之前的工作如何工作。

Spring Boot如何处理属性覆盖?

让我们以这个基本示例。我们可以尝试在单个文件中两次定义相同的值。这样:

main:
  value: option-A
  value: option-B

但这将失败,并且该应用程序将在引导下崩溃。您不能在同一文件中覆盖属性。但是,仅创建另一个属性文件(具有不同格式)是很好的:

# application.yaml

main:
  value: option-A
# application.properties

main.value=option-B

在这种情况下,系统将选择option-b,因为.properties文件的值比.YAML文件的值更高。值的完整层次结构看起来像这样:(从最低到最高优先级开始)

  • 代码中定义的默认值
@ConfigurationProperties(prefix = "main")
class MyConfigurationProperties {
    var value: String = "CODE_DEFAULT"
}
  • 默认情况下定义的属性.yaml文件
  • 默认情况下定义的属性.properties文件
  • 在特定于Profile的.YAML文件上定义的属性
  • 在特定于Profific .properties文件中定义的属性
  • 环境变量
  • JVM选项

多模块系统中的属性

,让我们提高档位,看看如何在多模块应用程序中管理属性。要处理真实的事物,请假设我们的应用程序包含3个模块:主一个,模块和模块。该项目的树结构看起来像这样:

├── src
│   ├── main
│   │   ├── resources
│   │   │   ├── application.yaml
├── moduleA
│   ├── src
│   │   ├── main
│   │   │   ├── resources
│   │   │   │   ├── application.yaml
├── moduleB
│   ├── src
│   │   ├── main
│   │   │   ├── resources
│   │   │   │   ├── application.yaml

现在,如果我们在所有三个文件中定义了相同的属性,哪个版本将被挑选?好吧...我们可以预测这将是主要模块中的一个,这是正确的。如果我们删除主应用程序.yaml文件怎么办?然后,Spring Boot将从其余两个文件之一中拾取值。哪一个?这是不确定性的。让我们完全避免这样的情况。任何歧义都是潜在的风险危害。这是一个特别有害的,因为我们可能会在应用程序进行生产时发现我们的错误。

与某些人可能假设的相反,Spring Boot不会将所有模块中的所有树配置文件合并到一个文件中。仅使用一个版本的文件,其余的将被扔掉。

一种简单的方法是定义单个应用程序中的所有属性。甚至没有每个模块创建专用的YAML文件。但是我们有这些模块是有原因的,我们的意图很可能是保留所有逻辑封装。那么,为什么我们应该在外面传播模块特异性属性呢?这没有道理。

在Spring Boot 2.4之前,我们可以通过使用Spring.Profiles.ixlude来解决此问题。但是,在2.4发布后,此功能不再存在。 blog post详细描述了贬值的原因。简短版本是 - 此功能产生非确定性结果。有了该选项,什么是替代方案?

一扇门关闭时,另一扇门打开。

Spring Boot 2.4之后可以做什么

Spring Boot 2.4引入了spring.config.import,这是可以预见的。该属性的想法很简单:我们只指出了我们想在最终配置文件中使用的其他来源。让我们看看如何在多模块应用程序中使用此新功能。

# src/main/resources/application.yaml

spring:
  config:
    import:
      - moduleA.yaml
      - moduleB.yaml

在主应用程序中。yaml文件,我们正在加载针对应用中存在的模块的其他配置文件。让我们创建这些文件。通过这些更改,我们的源树应该看起来像这样:

├── src
│   ├── main
│   │   ├── resources
│   │   │   ├── application.yaml
├── moduleA
│   ├── src
│   │   ├── main
│   │   │   ├── resources
│   │   │   │   ├── moduleA.yaml
├── moduleB
│   ├── src
│   │   ├── main
│   │   │   ├── resources
│   │   │   │   ├── moduleB.yaml

在模块专用文件中:moduleA.yamlmoduleB.yaml我们现在可以放置特定于模块和该模块的配置。

# moduleA/src/main/resources/moduleA.yaml

module-a:
  value: some value

即使我们仅对模块特定/自定义属性使用moduleA.yaml文件,仍然可以覆盖该文件中的全局属性。例如,如果我们在moduleA.yaml中设置应用程序port server.port=3000,则该值可能会从主应用程序中覆盖属性。这可能会令人困惑,并打破了特定于轮廓的配置的最初意图。为了使事物整洁和整洁,让我们将所有属性都以一个自定义名称空间为单位的所有属性,仅适用于该模块。例如,moduleA中的所有属性都必须从module-a开始。

总结一下:将所有通用配置保留在主application.yaml内,仅在模块特定文件内的模块特定属性,仅在模块唯一名称空间下。

它如何与自定义配置文件一起使用?

他们仍然按预期工作!意思是,如果我们与该文件的moduleA.yaml值一起创建moduleA-prod.yaml文件,将用于覆盖基本文件的属性。整洁的!不需要其他更改才能使其正常工作!

它如何与集成测试一起使用?

如果测试配置很简单,则可以将其放入模块特定的application-test.yaml中,例如:

# moduleA/src/test/resources/application-test.yaml

module-a:
  value: some test value
module-b:
  value: test value for the other module

如果配置更广泛,并且在每个测试配置文件上均复制它不正确,加上配置不必从模块更改为模块,我们可以应用与相同的技巧前。通过导入application-test.yaml中的共享模块配置文件:

# moduleA/src/test/resources/application-test.yaml

spring:
  config:
    import:
      - moduleB.yaml

module-a:
  value: some test value
# moduleB/src/main/resources/moduleB-test.yaml

module-b:
  value: shared testing property

请注意,moduleB-test.yaml在Main Not Test Directory内部!

Damian Garstecki,后端开发人员 @ Bright Inventions