在多模块应用程序中管理弹簧启动属性可能会非常令人生畏。在这篇文章中,我们看看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.yaml
和moduleB.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