配置写在好几个文件里,谁盖住谁?spring.config.location 和 additional-location 差在哪?import 进来的文件比原文件高还是低?profile 专属文件一定最高吗?这篇在 Spring Boot 4.0.8 上逐个场景实测,并读了源码里的默认位置定义。
不设置(默认位置):
app.name = [cwd-config] ←| 'file:[config/application.properties]'
only.classpath = [yes] ←| 'classpath:[application.properties]'
only.cwd = [yes] ←| 'file:[config/application.properties]'
-Dspring.config.location=file:./custom/(替换默认位置):
app.name = [custom] ←| 'file:[custom/application.properties]'
only.classpath = null ←| -
only.cwd = null ←| -
-Dspring.config.additional-location=file:./custom/(追加在默认位置之上):
app.name = [custom] ←| 'file:[custom/application.properties]'
only.classpath = [yes] ←| 'classpath:[application.properties]'
only.cwd = [yes] ←| 'file:[config/application.properties]'
-Dspring.config.name=other(换文件名,默认位置不变):
app.name = [cwd-config-other] ←| 'file:[config/other.properties]'
only.classpath = null ←| -
only.cwd = null ←| -
-Dspring.config.location=file:./custom/other.properties(直接指定文件):
app.name = [other] ←| 'file:[custom/other.properties]'
only.classpath = null ←| -
only.cwd = null ←| -
-Dspring.config.location=file:./custom(目录末尾没有斜杠):
启动失败(没有 FailureAnalyzer 报告,堆栈里的异常):Caused by: java.lang.IllegalStateException: File extension is not known to any PropertySourceLoader. If the location is meant to reference a directory, it must end in '/' or File.separator
-Dspring.config.location=file:./nope/(目录不存在,没写 optional:):
启动失败:Description: Config data location 'file:./nope/' does not exist Action: Check that the value 'file:./nope/' is correct, or prefix it with 'optional:'
-Dspring.config.location=optional:file:./nope/:
app.name = null ←| -
only.classpath = null ←| -
only.cwd = null ←| -
-Dspring.config.location=classpath:/,file:./custom/(多个位置用逗号,后面的优先):
app.name = [custom] ←| 'file:[custom/application.properties]'
only.classpath = [yes] ←| 'classpath:[application.properties]'
only.cwd = null ←| -
-Dspring.config.location=file:./custom/,classpath:/(顺序反过来):
app.name = [classpath] ←| 'classpath:[application.properties]'
only.classpath = [yes] ←| 'classpath:[application.properties]'
only.cwd = null ←| -location 替换默认位置,additional-location 追加在默认位置之上;目录必须以斜杠结尾,不存在的位置要加 optional:。
application.properties 自己写 app.name=main 并 import 一个也写了 app.name 的文件:
app.name = [imported] ←| 'file:[imp.properties]'
only.imp = [1] ←| 'file:[imp.properties]'
x = [from-main] ←| 'file:[application.properties]'
同一行 import 两个文件(imp.properties,imp2.properties):
app.name = [imported2] ←| 'file:[imp2.properties]'
only.imp = [1] ←| 'file:[imp.properties]'
x = null ←| -
顺序反过来(imp2.properties,imp.properties):
app.name = [imported] ←| 'file:[imp.properties]'
only.imp = [1] ←| 'file:[imp.properties]'
x = null ←| -
被导入的文件再 import 别的文件(链式):
app.name = [b] ←| 'file:[b.properties]'
only.imp = null ←| -
x = [from-b] ←| 'file:[b.properties]'
import 的文件不存在,没写 optional:
启动失败:Description: Config data resource 'file [missing.properties]' via location 'file:./missing.properties' does not exist Action: Check that the value 'file:./missing.properties' at URL [file:application.properties] - 1:22 is correct, or prefix it with 'optional:'
import 的文件不存在,写了 optional:
app.name = [main] ←| 'file:[application.properties]'
only.imp = null ←| -
x = null ←| -
import 的位置写在命令行 --spring.config.import:
app.name = [imported] ←| 'file:[imp.properties]'
only.imp = [1] ←| 'file:[imp.properties]'
x = null ←| -
命令行又写 --app.name=cmdline,文件里 import 的 app.name 还赢吗:
app.name = [cmdline] ←| commandLineArgs
only.imp = [1] ←| 'file:[imp.properties]'
x = null ←| -
import 的文件放在 jar 里(classpath:):
app.name = [cp-imp] ←| 'classpath:[imp.properties]'
only.imp = null ←| -
x = null ←| -被导入的文件覆盖声明它的文件,多个 import 后面的赢,命令行属性仍然最高;缺失的文件要加 optional: 才不报错。
import 一个没有扩展名的文件,不指定格式:
启动失败(没有 FailureAnalyzer 报告,堆栈里的异常):Caused by: java.lang.IllegalStateException: File extension is not known to any PropertySourceLoader. If the location is meant to reference a directory, it must end in '/' or File.separator
加上 [.properties] 指定格式:
db.password = [s3cret] ←| 'file:[secret]'
加上 [.yaml] 指定格式:
db.password = [yaml-secret] ←| 'file:[secret]'
configtree:./tree/(目录里每个文件名是 key,文件内容是 value):
db.password = [pw-with-newline] ←| Config tree '<jtmp>/x70_wd/./tree'
db.user = [root] ←| Config tree '<jtmp>/x70_wd/./tree'
app.name = [from-tree] ←| Config tree '<jtmp>/x70_wd/./tree'
文件内容末尾的换行、空格有没有被保留(只看长度;pw1="abc\n"、pw2="abc\n\n"、pw3=" abc "、pw4="abc"):
pw1 = [abc] 长度 3 ←| Config tree '<jtmp>/x70_wd/./tree'
pw2 = [abc\n\n] 长度 5 ←| Config tree '<jtmp>/x70_wd/./tree'
pw3 = [ abc ] 长度 5 ←| Config tree '<jtmp>/x70_wd/./tree'
pw4 = [abc] 长度 3 ←| Config tree '<jtmp>/x70_wd/./tree'
configtree 的目录不存在,没写 optional:
启动失败:Description: Config data resource 'config tree [<jtmp>/x70_wd/./nope]' via location 'configtree:./nope/' does not exist Action: Check that the value 'configtree:./nope/' at URL [file:application.properties] - 1:22 is correct, or prefix it with 'optional:'无扩展名要用 [.properties] 或 [.yaml] 指定格式;configtree 把文件名当 key、文件内容当 value,只有单个换行才会被去掉。
一个 application.properties 里用 #--- 分成 4 个文档,profile (不激活):
app.name = [base] ←| 'file:[application.properties]'
x = [base] ←| 'file:[application.properties]'
y = [last] ←| 'file:[application.properties]'
一个 application.properties 里用 #--- 分成 4 个文档,profile dev:
app.name = [dev] ←| 'file:[application.properties]'
x = [base] ←| 'file:[application.properties]'
y = [last] ←| 'file:[application.properties]'
一个 application.properties 里用 #--- 分成 4 个文档,profile prod:
app.name = [prod] ←| 'file:[application.properties]'
x = [prod] ←| 'file:[application.properties]'
y = [last] ←| 'file:[application.properties]'
profile 专属的文档写在前面、普通文档写在后面,激活 dev:
app.name = [plain-after] ←| 'file:[application.properties]'
同样的写法用 application.yaml(--- 分文档),激活 dev:
app.name = [yaml-dev] ←| 'file:[application.yaml]'
application.properties 和 application.yaml 同时存在,都写 app.name:
app.name = [props] ←| 'file:[application.properties]'
第二个文档 on-profile=dev 里写了 spring.profiles.active=prod,不激活 dev:
app.name = [base] ←| 'file:[application.properties]'
active profiles = [prod]
同上,--spring.profiles.active=dev:
app.name = [base] ←| 'file:[application.properties]'
active profiles = [dev]
application-dev.properties 里写 spring.profiles.active=prod,--spring.profiles.active=dev:
启动失败(没有 FailureAnalyzer 报告,堆栈里的异常):org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property 'spring.profiles.active' imported from location 'file [application-dev.properties]' is invalid in a profile specific resource [origin: URL [file:application-dev.properties] - 1:24]
on-profile=dev 的文档里写 spring.config.import,激活 dev:
app.name = [imported-by-dev] ←| 'file:[imp.properties]'
同上,不激活 dev(import 不会发生):
app.name = [base] ←| 'file:[application.properties]'同一个文件里文档顺序决定覆盖关系;profile 专属文件里不能写 spring.profiles.active;on-profile 文档里的 import 只在该 profile 激活时才发生。
不激活 profile,Environment 里的配置来源(第 1 个优先级最高):
app.name = [sub2] ←| 'file:[config/sub2/application.properties]'
1. 'file:[config/sub2/application.properties]'
2. 'file:[config/sub1/application.properties]'
3. 'file:[config/application.properties]'
4. 'file:[imp.properties]'
5. 'file:[application.properties]'
6. 'classpath:[config/application.properties]'
7. 'classpath:[application.properties]'
激活 dev(多了 application-dev.properties):
app.name = [cwd-dev] ←| 'file:[application-dev.properties]'
1. 'file:[application-dev.properties]'
2. 'file:[config/sub2/application.properties]'
3. 'file:[config/sub1/application.properties]'
4. 'file:[config/application.properties]'
5. 'file:[imp.properties]'
6. 'file:[application.properties]'
7. 'classpath:[config/application.properties]'
8. 'classpath:[application.properties]'
只在 application.properties 里 import,同时有 application-dev.properties,激活 dev:
app.name = [cwd-dev] ←| 'file:[application-dev.properties]'
-- 同样的 profile 专属文件(dev),只放在下面这一个位置;其余位置只有不带 profile 的 application.properties(classpath:/、classpath:/config/、file:./、file:./config/、file:./config/sub1/ 各一个)
dev 文件只放在 classpath:/:
app.name = [sub1] ←| 'file:[config/sub1/application.properties]'
dev 文件排第 4 位(共 6 个),比它优先的:file:config/sub1/application.properties、file:config/application.properties、file:application.properties;比它低的:classpath:config/application.properties、classpath:application.properties
dev 文件只放在 classpath:/config/:
app.name = [sub1] ←| 'file:[config/sub1/application.properties]'
dev 文件排第 4 位(共 6 个),比它优先的:file:config/sub1/application.properties、file:config/application.properties、file:application.properties;比它低的:classpath:config/application.properties、classpath:application.properties
dev 文件只放在 file:./:
app.name = [DEV] ←| 'file:[application-dev.properties]'
dev 文件排第 1 位(共 6 个),比它优先的:(没有);比它低的:file:config/sub1/application.properties、file:config/application.properties、file:application.properties、classpath:config/application.properties、classpath:application.properties
dev 文件只放在 file:./config/:
app.name = [DEV] ←| 'file:[config/application-dev.properties]'
dev 文件排第 1 位(共 6 个),比它优先的:(没有);比它低的:file:config/sub1/application.properties、file:config/application.properties、file:application.properties、classpath:config/application.properties、classpath:application.properties
dev 文件只放在 file:./config/sub1/:
app.name = [DEV] ←| 'file:[config/sub1/application-dev.properties]'
dev 文件排第 1 位(共 6 个),比它优先的:(没有);比它低的:file:config/sub1/application.properties、file:config/application.properties、file:application.properties、classpath:config/application.properties、classpath:application.properties默认位置分 classpath 和 file 两组,组内 profile 专属文件整体更高,file 整组高于 classpath 整组。