https://docs.spring.io/spring-boot/docs/3.2.0/reference/htmlsingle/#features.external-config.files
当Spring Boot应用程序启动时,它会自动从以下位置查找并加载application.properties
和application.yaml
文件:
- 从类路径
a. 类路径的根目录
b. 类路径下的/config
包 - 从当前目录
a. 当前目录
b. 当前目录下的/config
子目录
c. config/子目录的直接子目录
上面列表按优先级排序(较低项的值会覆盖较早项的值)。
从加载的文件中获取的文档被添加为Spring Environment
中的PropertySources
。
如果你不喜欢使用“application
”作为配置文件的名称,你可以通过指定一个spring.config.name
环境属性来切换到另一个文件名。例如,为了查找myproject.properties
和myproject.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.name
、spring.config.location
和spring.config.additional-location
这三个属性在非常早期的时候就被用来确定需要加载哪些文件。因此,它们必须被定义为环境属性(通常是操作系统环境变量、系统属性或命令行参数)。
如果spring.config.location
包含的是目录(而不是文件),那么它们应该以/
结尾。在运行时,将会从spring.config.name
生成的文件名追加到这些目录后面,然后再进行加载。而在spring.config.location
中指定的文件则会被直接导入。
提示:目录和文件location值都会被扩展以检查特定于profile的文件。例如,如果你的spring.config.location
是classpath: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/
,那么将考虑以下完整的位置集合:
optional:classpath:custom-config/
optional:file:./custom-config/
如果你更希望添加额外的位置而不是替换它们,你可以使用spring.config.additional-location
。从额外位置加载的属性可以覆盖默认位置中的属性。例如,如果spring.config.additional-location
被配置为optional:classpath:/custom-config/,optional:file:./custom-config/
,那么将考虑以下完整的位置集合:
optional:classpath:/;optional:classpath:/config/
optional:file:./;optional:file:./config/;optional:file:./config/*/
optional:classpath:custom-config/
optional:file:./custom-config/
这种搜索顺序允许你在一个配置文件中指定默认值,然后在另一个配置文件中选择性地覆盖这些值。你可以在默认位置之一application.properties
(或者通过spring.config.name
选择的任何其它基本名称)中为应用程序提供默认值。然后,这些默认值可以在运行时被自定义位置之一的不同文件所覆盖。
可选位置(Optional Locations)
默认情况下,当指定的配置数据位置不存在时,Spring Boot会抛出一个ConfigDataLocationNotFoundException
异常,导致应用程序无法启动。
如果你想要指定一个位置,但并不总是介意它是否存在,你可以使用optional:
前缀。你可以在使用spring.config.location
和spring.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.location
和spring.config.additional-location
属性来自己定义通配符位置。
注意:通配符位置必须只包含一个*
,并且以*/
结尾(对于目录搜索位置)或者以/*<filename>
结尾(对于文件搜索位置)。带有通配符的位置将根据文件名的绝对路径按字母顺序排序。
提示:通配符位置仅适用于外部目录。你不能在classpath:
位置中使用通配符。
profile特定的配置文件(Profile Specific Files)
除了application
属性文件外,Spring Boot还会尝试使用命名约定application-{profile}
来加载特定于profile的配置文件。例如,如果你的应用程序激活了一个名为prod
的profile并且使用YAML文件,那么application.yaml
和application-prod.yaml
都将被考虑在内。
特定于profile的属性从与标准application.properties
相同的位置加载。特定于profile的文件始终覆盖非特定于profile的文件。如果指定了多个profile,则应用“最后胜出”的策略。例如,如果通过spring.profiles.active
属性指定了prod
和live
这两个profile,那么application-prod.properties
中的值可以被application-live.properties
中的值覆盖。
注意
“最后胜出”的策略适用于位置组( location group)级别。spring.config.location
的值为classpath:/cfg/,classpath:/ext/
将不会与classpath:/cfg/;classpath:/ext/
具有相同的覆盖规则。
例如,继续我们上面的prod
,live
示例,我们可能有以下文件:
/cfg
application-live.properties
/ext
application-live.properties
application-prod.properties
当spring.config.location
的值为classpath:/cfg/,classpath:/ext/
时,我们会先处理所有/cfg
文件,然后再处理所有/ext
文件:
/cfg/application-live.properties
ext/application-prod.properties
/ext/application-live.properties
当spring.config.location
的值为classpath:/cfg/;classpath:/ext/
(使用;
作为分隔符)时,会在同一级别处理/cfg
和/ext
:
/ext/application-prod.properties
/cfg/application-live.properties
/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
包中的ConfigDataLocationResolver
和ConfigDataLoader
类。
导入没有扩展名的文件(Importing Extensionless Files)
一些云平台无法为挂载的卷文件添加文件扩展名。要导入这些没有扩展名的文件,你需要给Spring Boot一个提示,以便它知道如何加载它们。你可以通过在方括号中放置扩展名提示来做到这一点。
例如,假设你有一个/etc/config/myconfig
文件,你希望将其作为YAML导入。你可以使用以下方式从application.properties
中导入它:
spring.config.import=file:/etc/config/myconfig[.yaml]
使用配置树(Using Configuration Trees)
在云平台(如Kubernetes)上运行应用程序时,你通常需要读取平台提供的配置值。使用环境变量进行此类操作并不罕见,但这可能会有缺点,尤其是如果该值应该保密。
作为环境变量的替代方案,许多云平台现在允许你将配置映射到挂载的数据卷中。例如,Kubernetes可以挂载ConfigMaps
和Secrets
。
有两种常见的卷挂载模式可以使用:
- 单个文件包含一套完整的属性(通常以YAML格式编写)。
- 将多个文件写入目录树,文件名成为“键”,内容成为“值”。
对于第一种情况,你可以使用上面描述的spring.config.import
直接导入YAML或Properties
文件。
对于第二种情况,你需要使用configtree:
前缀,以便Spring Boot知道它需要将所有文件作为属性公开。
作为一个例子,假设Kubernetes已经挂载了以下卷:
etc/
config/
myapp/
username
password
username
文件的内容将是一个配置值,而password
的内容将是一个secret。
要导入这些属性,你可以将以下内容添加到你的application.properties
或application.yaml
文件中:
spring.config.import=optional:configtree:/etc/config/
然后,你可以以通常的方式从Environment
中访问或注入myapp.username
和myapp.password
属性。
提示:配置树下的文件夹和文件的名称形成属性名称。在上面的示例中,为了将属性作为username
和password
进行访问,你可以将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.username
、db.password
、mq.username
和mq.password
属性。
注意:使用通配符加载的目录将按字母顺序排序。如果你需要不同的顺序,那么你应该将每个位置作为单独的导入列出。
配置树也可用于Docker secret。当Docker集群服务被授予访问secret时,该secret将被挂载到容器中。例如,如果名为db.password
的secret被挂载到/run/secrets/
位置,你可以使用以下方式将db.password
提供给Spring环境:
spring.config.import=optional:configtree:/run/secrets/
属性占位符(Property Placeholders)
当使用application.properties
和application.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-price
和 demo.itemPrice
形式,以及从系统环境中提取 DEMO_ITEMPRICE
。如果你使用 ${demo.itemPrice}
代替,那么 demo.item-price
和 DEMO_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