Spring外部化配置:外部应用程序属性

本文详细解释了SpringBoot3.2如何自动查找并加载application.properties和application.yaml文件,以及如何通过环境属性如spring.config.name和spring.config.location进行定制。还介绍了profile特定配置、通配符位置、导入额外数据和激活属性的使用方法。
摘要由CSDN通过智能技术生成

https://docs.spring.io/spring-boot/docs/3.2.0/reference/htmlsingle/#features.external-config.files

当Spring Boot应用程序启动时,它会自动从以下位置查找并加载application.propertiesapplication.yaml文件:

  1. 从类路径
    a. 类路径的根目录
    b. 类路径下的 /config
  2. 从当前目录
    a. 当前目录
    b. 当前目录下的 /config子目录
    c. config/子目录的直接子目录

上面列表按优先级排序(较低项的值会覆盖较早项的值)。
从加载的文件中获取的文档被添加为Spring Environment中的PropertySources

如果你不喜欢使用“application”作为配置文件的名称,你可以通过指定一个spring.config.name环境属性来切换到另一个文件名。例如,为了查找myproject.propertiesmyproject.yaml文件,你可以按如下方式运行你的应用程序:

$ java -jar myproject.jar --spring.config.name=myproject

你还可以通过使用spring.config.location环境属性来引用一个明确的位置。该属性接受一个或多个位置的逗号分隔列表以供检查。
以下示例展示了如何指定两个不同的文件:

$ java -jar myproject.jar --spring.config.location=\
    optional:classpath:/default.properties,\
    optional:classpath:/override.properties

提示:如果你认为配置文件的位置是可选的,并且不介意它们是否存在,你可以使用optional:前缀。这样,如果指定的位置没有找到配置文件,Spring Boot 不会抛出错误。

注意spring.config.namespring.config.locationspring.config.additional-location这三个属性在非常早期的时候就被用来确定需要加载哪些文件。因此,它们必须被定义为环境属性(通常是操作系统环境变量、系统属性或命令行参数)。

如果spring.config.location包含的是目录(而不是文件),那么它们应该以/结尾。在运行时,将会从spring.config.name生成的文件名追加到这些目录后面,然后再进行加载。而在spring.config.location中指定的文件则会被直接导入。

提示:目录和文件location值都会被扩展以检查特定于profile的文件。例如,如果你的spring.config.locationclasspath:myconfig.properties,你也会发现相应的classpath:myconfig-<profile>.properties文件被加载了。这里,<profile>是一个占位符,代表当前激活的Spring profile的名称。

在大多数情况下,你添加的每个spring.config.location项都会引用单个文件或目录。位置会按照定义的顺序进行处理,后面的位置可以覆盖前面的值。

如果你有一个复杂的配置位置设置,并且你使用了特定于profile的配置文件,你可能需要提供更多信息,以便Spring Boot知道如何对它们进行分组。一个位置组(location group)是一系列在同一级别上考虑的位置的集合。例如,你可能想要将所有类路径位置分组,然后将所有外部位置分组。位置组内的项应使用分号(;)分隔。

通过使用spring.config.location配置的位置将替换默认位置。例如,如果spring.config.location被配置为optional:classpath:/custom-config/,optional:file:./custom-config/,那么将考虑以下完整的位置集合:

  1. optional:classpath:custom-config/
  2. optional:file:./custom-config/

如果你更希望添加额外的位置而不是替换它们,你可以使用spring.config.additional-location。从额外位置加载的属性可以覆盖默认位置中的属性。例如,如果spring.config.additional-location被配置为optional:classpath:/custom-config/,optional:file:./custom-config/,那么将考虑以下完整的位置集合:

  1. optional:classpath:/;optional:classpath:/config/
  2. optional:file:./;optional:file:./config/;optional:file:./config/*/
  3. optional:classpath:custom-config/
  4. optional:file:./custom-config/

这种搜索顺序允许你在一个配置文件中指定默认值,然后在另一个配置文件中选择性地覆盖这些值。你可以在默认位置之一application.properties(或者通过spring.config.name选择的任何其它基本名称)中为应用程序提供默认值。然后,这些默认值可以在运行时被自定义位置之一的不同文件所覆盖。

可选位置(Optional Locations)

默认情况下,当指定的配置数据位置不存在时,Spring Boot会抛出一个ConfigDataLocationNotFoundException异常,导致应用程序无法启动。

如果你想要指定一个位置,但并不总是介意它是否存在,你可以使用optional:前缀。你可以在使用spring.config.locationspring.config.additional-location属性时,以及在使用spring.config.import声明时使用这个前缀。

例如,如果spring.config.import的值设置为optional:file:./myconfig.properties,那么即使myconfig.properties文件不存在,你的应用程序也可以启动。

如果你想要忽略所有的ConfigDataLocationNotFoundExceptions并始终继续启动你的应用程序,你可以使用spring.config.on-not-found属性。通过设置其值为ignore来使用SpringApplication.setDefaultProperties(…​)或者系统/环境变量。

通配符位置(Wildcard Locations)

如果配置文件位置的最后一个路径段包含*字符,它就被认为是一个通配符位置。在加载配置时,通配符会被扩展,以便也检查直接子目录。在存在多个配置属性源的环境中,例如Kubernetes,通配符位置特别有用。

例如,如果你有一些Redis配置和一些MySQL配置,你可能想要将这两部分配置分开,同时要求它们都在一个application.properties文件中存在。这可能会导致两个单独的application.properties文件被挂载到不同的位置,如/config/redis/application.properties/config/mysql/application.properties。在这种情况下,使用一个通配符位置config/*/,将会导致两个文件都被处理。

默认情况下,Spring Boot在默认搜索位置中包含config/*/。这意味着将会搜索jar包外的/config目录下的所有子目录。

你可以使用spring.config.locationspring.config.additional-location属性来自己定义通配符位置。

注意:通配符位置必须只包含一个*,并且以*/结尾(对于目录搜索位置)或者以/*<filename>结尾(对于文件搜索位置)。带有通配符的位置将根据文件名的绝对路径按字母顺序排序。

提示:通配符位置仅适用于外部目录。你不能在classpath:位置中使用通配符。

profile特定的配置文件(Profile Specific Files)

除了application属性文件外,Spring Boot还会尝试使用命名约定application-{profile}来加载特定于profile的配置文件。例如,如果你的应用程序激活了一个名为prod的profile并且使用YAML文件,那么application.yamlapplication-prod.yaml都将被考虑在内。

特定于profile的属性从与标准application.properties相同的位置加载。特定于profile的文件始终覆盖非特定于profile的文件。如果指定了多个profile,则应用“最后胜出”的策略。例如,如果通过spring.profiles.active属性指定了prodlive这两个profile,那么application-prod.properties中的值可以被application-live.properties中的值覆盖。

注意
“最后胜出”的策略适用于位置组( location group)级别。spring.config.location的值为classpath:/cfg/,classpath:/ext/将不会与classpath:/cfg/;classpath:/ext/具有相同的覆盖规则。

例如,继续我们上面的prodlive示例,我们可能有以下文件:

/cfg
  application-live.properties
/ext
  application-live.properties
  application-prod.properties

spring.config.location的值为classpath:/cfg/,classpath:/ext/时,我们会先处理所有/cfg文件,然后再处理所有/ext文件:

  1. /cfg/application-live.properties
  2. ext/application-prod.properties
  3. /ext/application-live.properties

spring.config.location的值为classpath:/cfg/;classpath:/ext/(使用;作为分隔符)时,会在同一级别处理/cfg/ext

  1. /ext/application-prod.properties
  2. /cfg/application-live.properties
  3. /ext/application-live.properties

Environment具有一组默认的profile(默认为[default]),如果在没有设置活动的profile的情况下,将会使用这些默认的profile。换句话说,如果没有显式激活任何profile,那么将考虑application-default中的属性。

注意
属性文件只会被加载一次。如果你已经直接导入了一个特定于profile的属性文件,那么它就不会被再次导入。

导入额外数据(Importing Additional Data)

应用程序属性可以使用spring.config.import属性从其它位置导入更多的配置数据。导入的数据在发现时即进行处理,并被视为在声明导入的文档下方立即插入的额外文档。

例如,在类路径application.properties文件中,你可能会有以下内容:

spring.application.name=myapp
spring.config.import=optional:file:./dev.properties

这将触发在当前目录中导入dev.properties文件(如果该文件存在)。从导入的dev.properties文件中获取的值将优先于触发导入的文件中的值。在上面的示例中,dev.properties可以重新定义spring.application.name为不同的值。

无论声明了多少次,一个import只会被导入一次。在properties/YAML文件内的单个文档中定义导入的顺序并不重要。例如,下面的两个示例产生相同的结果:

spring.config.import=my.properties
my.property=value
my.property=value
spring.config.import=my.properties

在上面的两个示例中,从my.properties文件中获取的值将优先于触发其导入的文件中的值。

可以在单个spring.config.import键下指定多个位置。位置将按照定义的顺序进行处理,后续的导入将优先于之前的导入。

注意
在适当的情况下,还会考虑导入特定于profile的变体。上面的示例将导入my.properties以及任何my-<profile>.properties变体。

提示
Spring Boot包含可插拔的API,允许支持各种不同的位置地址。默认情况下,你可以导入Java Properties、YAML和“配置树(configuration trees)”。

第三方JAR包可以提供对额外技术的支持(文件不一定是本地的)。例如,你可以想象配置数据来自外部存储,如Consul、Apache ZooKeeper或Netflix Archaius。

如果你想支持自己的位置,请参阅org.springframework.boot.context.config包中的ConfigDataLocationResolverConfigDataLoader类。

导入没有扩展名的文件(Importing Extensionless Files)

一些云平台无法为挂载的卷文件添加文件扩展名。要导入这些没有扩展名的文件,你需要给Spring Boot一个提示,以便它知道如何加载它们。你可以通过在方括号中放置扩展名提示来做到这一点。

例如,假设你有一个/etc/config/myconfig文件,你希望将其作为YAML导入。你可以使用以下方式从application.properties中导入它:

spring.config.import=file:/etc/config/myconfig[.yaml]

使用配置树(Using Configuration Trees)

在云平台(如Kubernetes)上运行应用程序时,你通常需要读取平台提供的配置值。使用环境变量进行此类操作并不罕见,但这可能会有缺点,尤其是如果该值应该保密。

作为环境变量的替代方案,许多云平台现在允许你将配置映射到挂载的数据卷中。例如,Kubernetes可以挂载ConfigMapsSecrets

有两种常见的卷挂载模式可以使用:

  1. 单个文件包含一套完整的属性(通常以YAML格式编写)。
  2. 将多个文件写入目录树,文件名成为“键”,内容成为“值”。

对于第一种情况,你可以使用上面描述的spring.config.import直接导入YAML或Properties 文件。
对于第二种情况,你需要使用configtree:前缀,以便Spring Boot知道它需要将所有文件作为属性公开。

作为一个例子,假设Kubernetes已经挂载了以下卷:

etc/
  config/
    myapp/
      username
      password

username文件的内容将是一个配置值,而password的内容将是一个secret。
要导入这些属性,你可以将以下内容添加到你的application.propertiesapplication.yaml文件中:

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

然后,你可以以通常的方式从Environment中访问或注入myapp.usernamemyapp.password属性。

提示:配置树下的文件夹和文件的名称形成属性名称。在上面的示例中,为了将属性作为usernamepassword进行访问,你可以将spring.config.import设置为optional:configtree:/etc/config/myapp

注意:使用点符号的文件名也可以正确映射。例如,在上面的示例中,/etc/config中名为myapp.username的文件将导致Environment中出现一个名为myapp.username的属性。

提示:根据预期的内容,配置树的值可以绑定到字符串String和byte[]类型。

如果你需要从同一个父文件夹中导入多个配置树,你可以使用通配符快捷方式。任何以/*/结尾的configtree: 位置都将导入所有直接子项作为配置树。与非通配符导入一样,每个配置树下的文件夹和文件的名称形成属性名称。
例如,给定以下卷:

etc/
  config/
    dbconfig/
      db/
        username
        password
    mqconfig/
      mq/
        username
        password

你可以使用configtree:/etc/config/*/作为导入位置:

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

这将添加db.usernamedb.passwordmq.usernamemq.password属性。

注意:使用通配符加载的目录将按字母顺序排序。如果你需要不同的顺序,那么你应该将每个位置作为单独的导入列出。

配置树也可用于Docker secret。当Docker集群服务被授予访问secret时,该secret将被挂载到容器中。例如,如果名为db.password的secret被挂载到/run/secrets/位置,你可以使用以下方式将db.password提供给Spring环境:

spring.config.import=optional:configtree:/run/secrets/

属性占位符(Property Placeholders)

当使用application.propertiesapplication.yaml中的值时,它们会通过现有的Environment进行过滤,因此你可以引用先前定义的值(例如,从系统属性或环境变量)。可以在值中的任何位置使用标准的${name}属性占位符语法。属性占位符还可以使用:将默认值与属性名分开来指定默认值,例如${name:default}

以下示例展示了使用带默认值和不带默认值的占位符的用法:

app.name=MyApp
app.description=${app.name} is a Spring Boot application written by ${username:Unknown}

假设username属性没有在其它地方设置,app.description的值将为MyApp is a Spring Boot application written by Unknown

提示
你应该始终使用规范形式(仅使用小写字母的kebab-case)在占位符中引用属性名称。这将允许Spring Boot使用与它在放宽绑定@ConfigurationProperties时相同的逻辑。
“kebab-case” 通常指的是一种命名约定,其中单词由连字符(-)连接,并且所有字母都是小写。这种命名方式在编程和配置文件中很常见,特别是在那些需要区分不同单词但又不想使用驼峰命名法(camelCase)的场合。
例如,${demo.item-price} 将从 application.properties 文件中提取 demo.item-pricedemo.itemPrice 形式,以及从系统环境中提取 DEMO_ITEMPRICE。如果你使用 ${demo.itemPrice} 代替,那么 demo.item-priceDEMO_ITEMPRICE 将不会被考虑。

处理多文档文件(Working With Multi-Document Files)

Spring Boot 允许你将单个物理文件分割成多个逻辑文档,并且每个逻辑文档可以独立地添加。这些文档按照从上到下的顺序进行处理。后来的文档可以覆盖先前文档中定义的属性。

对于 application.yaml 文件,使用标准的 YAML 多文档语法。三个连续的连字符(-)表示一个文档的结束和下一个文档的开始。
例如,以下文件包含两个逻辑文档:

spring:
  application:
    name: "MyApp"
---
spring:
  application:
    name: "MyCloudApp"
  config:
    activate:
      on-cloud-platform: "kubernetes"

对于 application.properties 文件,使用特殊的 #---!--- 注释来标记文档的分隔:

spring.application.name=MyApp
#---
spring.application.name=MyCloudApp
spring.config.activate.on-cloud-platform=kubernetes

注意:属性文件分隔符前面不能有空格,并且必须恰好有三个连字符。分隔符前后的行不能有相同的注释前缀。

提示:多文档属性文件经常与激活属性(如 spring.config.activate.on-profile)一起使用。

注意@PropertySource@TestPropertySource 注解不支持加载多文档属性文件。

激活属性(Activation Properties)

在某些条件下仅激活特定的属性集是非常有用的。例如,你可能有一些属性只在特定的profile被激活时才相关。

你可以使用 spring.config.activate.* 来条件性地激活一个属性文档。
以下是可用的激活属性:
在这里插入图片描述

例如,以下配置指定了第二个文档仅在运行在Kubernetes上,并且仅当“prod”或“staging” profile 被激活时才会生效:

myprop=always-set
#---
spring.config.activate.on-cloud-platform=kubernetes
spring.config.activate.on-profile=prod | staging
myotherprop=sometimes-set
  • 11
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值