bigywater.tech
Spring · 72

Profile 与 profile 组

spring.profiles.active 写在命令行、环境变量、配置文件里谁说了算?@Profile({"dev","!cloud"}) 是「或」还是「且」?profile 组的顺序影响谁覆盖谁?为什么把 spring.profiles.active 写进 @PropertySource 没有用?这篇在 Spring Boot 4.0.8 上逐个场景实测。

9 个 Bean
逐个激活 profile 对照 @Profile 的 9 种写法;激活 dev 时 5 个生效,激活 prod 时只剩 1 个
prod → proddb, prodmq
group.prod=proddb,prodmq 激活 prod 得到 [prod, proddb, prodmq],app.v 来自靠后的 prodmq;顺序反过来就是 proddb
0 个
写在 @PropertySource 文件里的 spring.profiles.active=dev:getProperty 读得到 dev,激活的 profile 是 0 个

激活方式与优先级

实测 · 本机执行输出
  什么都不设:
    active=[] default=[default]
    app.v = base ←| 'file:[application.properties]'
    生效的 @Profile Bean:!prod、default、{dev, !cloud}
  application.properties 里写 spring.profiles.active=dev:
    active=[dev] default=[default]
    app.v = dev ←| 'file:[application-dev.properties]'
  -Dspring.profiles.active=dev,prod(系统属性,两个 profile):
    active=[dev, prod] default=[default]
    app.v = prod ←| 'file:[application-prod.properties]'
  环境变量 SPRING_PROFILES_ACTIVE=qa:
    active=[qa] default=[default]
    app.v = qa ←| 'file:[application-qa.properties]'
  命令行 --spring.profiles.active=prod,文件里写的是 dev:
    active=[prod] default=[default]
    app.v = prod ←| 'file:[application-prod.properties]'
  系统属性 -Dspring.profiles.active=prod,环境变量 SPRING_PROFILES_ACTIVE=qa:
    active=[prod] default=[default]
    app.v = prod ←| 'file:[application-prod.properties]'
  代码里 setAdditionalProfiles("qa") 再配 --spring.profiles.active=dev:
    active=[qa, dev] default=[default]
    app.v = dev ←| 'file:[application-dev.properties]'
  spring.profiles.include=qa,配 --spring.profiles.active=dev:
    active=[qa, dev] default=[default]
    app.v = dev ←| 'file:[application-dev.properties]'
  spring.profiles.default=fallback(没有激活任何 profile):
    active=[] default=[fallback]
    app.v = fallback ←| 'file:[application-fallback.properties]'
    生效的 @Profile Bean:!prod、{dev, !cloud}
  spring.profiles.default=fallback,同时激活 dev:
    active=[dev] default=[fallback]
    app.v = dev ←| 'file:[application-dev.properties]'
    生效的 @Profile Bean:dev、!prod、dev | test、(dev | test) & !cloud、{dev, !cloud}

命令行 > 系统属性 > 环境变量 > 配置文件;include 和 additionalProfiles 追加在前面;default 只在没激活任何 profile 时才用。

@Profile 表达式

实测 · 本机执行输出
  激活 [无]:
    active=[] default=[default]
    生效的 @Profile Bean:!prod、default、{dev, !cloud}
  激活 [dev]:
    active=[dev] default=[default]
    生效的 @Profile Bean:dev、!prod、dev | test、(dev | test) & !cloud、{dev, !cloud}
  激活 [prod]:
    active=[prod] default=[default]
    生效的 @Profile Bean:{dev, !cloud}
  激活 [dev,cloud]:
    active=[dev, cloud] default=[default]
    生效的 @Profile Bean:dev、!prod、dev & cloud、dev | test、{dev, !cloud}
  激活 [test]:
    active=[test] default=[default]
    生效的 @Profile Bean:!prod、dev | test、(dev | test) & !cloud、{dev, !cloud}
  激活 [x]:
    active=[x] default=[default]
    生效的 @Profile Bean:!prod、{x, y}、{dev, !cloud}
  激活 [cloud]:
    active=[cloud] default=[default]
    生效的 @Profile Bean:!prod
  激活 [Dev]:
    active=[Dev] default=[default]
    生效的 @Profile Bean:!prod、{dev, !cloud}
  -- 写错的表达式
  @Profile("dev & cloud | test"),激活 dev:
    启动失败(没有 FailureAnalyzer 报告,堆栈里的异常):java.lang.IllegalArgumentException: Malformed profile expression [dev & cloud | test]
    (不激活任何 profile)
    启动失败(没有 FailureAnalyzer 报告,堆栈里的异常):java.lang.IllegalArgumentException: Malformed profile expression [dev & cloud | test]
  @Profile("(dev | test"),激活 dev:
    启动成功,Bean 存在=true
    (不激活任何 profile)
    启动成功,Bean 存在=false
  @Profile("dev &"),激活 dev:
    启动成功,Bean 存在=true
    (不激活任何 profile)
    启动成功,Bean 存在=false
  @Profile(""),激活 dev:
    启动失败(没有 FailureAnalyzer 报告,堆栈里的异常):java.lang.IllegalArgumentException: Invalid profile expression []: must contain text
    (不激活任何 profile)
    启动失败(没有 FailureAnalyzer 报告,堆栈里的异常):java.lang.IllegalArgumentException: Invalid profile expression []: must contain text

数组是「或」,带逗号的字符串不是;混用 & 和 | 要加括号;名称区分大小写。

profile 组

实测 · 本机执行输出
  spring.profiles.group.prod=proddb,prodmq,激活 prod:
    active=[prod, proddb, prodmq] default=[default]
    app.v = mq ←| 'file:[application-prodmq.properties]'
    db.url = jdbc:prod ←| 'file:[application-proddb.properties]'
    mq.host = prodmq ←| 'file:[application-prodmq.properties]'
  顺序反过来 spring.profiles.group.prod=prodmq,proddb:
    active=[prod, prodmq, proddb] default=[default]
    app.v = db ←| 'file:[application-proddb.properties]'
  不激活 prod(组不会生效):
    active=[] default=[default]
    app.v = null ←| -
    db.url = null ←| -
  spring.profiles.group.prod 写在 application-prod.properties 里(profile 专属文件):
    active=[prod] default=[default]
    app.v = prod ←| 'file:[application-prod.properties]'
  spring.profiles.include=proddb 写在 application-prod.properties 里:
    启动失败(没有 FailureAnalyzer 报告,堆栈里的异常):org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property 'spring.profiles.include' imported from location 'file [application-prod.properties]' is invalid in a profile specific resource [origin: URL [file:application-prod.properties] - 2:25]
  组里套组:group.a=b、group.b=c,激活 a:
    active=[a, b, c] default=[default]
    app.v = c ←| 'file:[application-c.properties]'

激活组名时按写的顺序一并激活成员,靠后的覆盖靠前的;组可以套;include 和 group 不能写在 profile 专属文件里。

名称与文件顺序

实测 · 本机执行输出
  --spring.profiles.active=a,b(两个文件都写了 app.v):
    active=[a, b] default=[default]
    app.v = b ←| 'file:[application-b.properties]'
    only.a = 1 ←| 'file:[application-a.properties]'
    only.b = 1 ←| 'file:[application-b.properties]'
  --spring.profiles.active=b,a:
    active=[b, a] default=[default]
    app.v = a ←| 'file:[application-a.properties]'
  --spring.profiles.active="dev_1":
    active=[dev_1] default=[default]
    app.v = null ←| -
  --spring.profiles.active="dev-1":
    active=[dev-1] default=[default]
    app.v = null ←| -
  --spring.profiles.active="dev.1":
    active=[dev.1] default=[default]
    app.v = null ←| -
  --spring.profiles.active="dev+x":
    active=[dev+x] default=[default]
    app.v = null ←| -
  --spring.profiles.active="dev@x":
    active=[dev@x] default=[default]
    app.v = null ←| -
  --spring.profiles.active="dev$x":
    启动失败(没有 FailureAnalyzer 报告,堆栈里的异常):Caused by: java.lang.IllegalStateException: Profile 'dev$x' must contain a letter, digit or allowed char ('-', '_', '.', '+', '@')
  --spring.profiles.active="dev x":
    启动失败:Description: Failed to bind properties under 'spring.profiles.active' to java.util.Set<java.lang.String>: Property: spring.profiles.active Value: "dev x" Origin: "spring.profiles.active" from property source "commandLineArgs" Reason: java.lang.IllegalStateException: Profile 'dev x' must contain a letter, digit or allowed char ('-', '_', '.', '+', '@') Action: Update your application's configuration
  --spring.profiles.active="":
    active=[] default=[default]
    app.v = null ←| -

多个 profile 写在后面的赢;名称只允许字母、数字和 - _ . + @。

写错位置

实测 · 本机执行输出
  写在 @PropertySource 加载的文件里(extra72.properties 里 spring.profiles.active=dev):
    active=[] default=[default]
    app.v = null ←| -
    spring.profiles.active = dev ←| classpath:[extra72.properties]
    生效的 @Profile Bean:!prod、default、{dev, !cloud}
  对照:同样内容写在 application.properties 里:
    active=[dev] default=[default]
    app.v = dev ←| 'file:[application-dev.properties]'
    spring.profiles.active = dev ←| 'file:[application.properties]'
    生效的 @Profile Bean:dev、!prod、dev | test、(dev | test) & !cloud、{dev, !cloud}

@PropertySource 里的 spring.profiles.active 读得到但不激活 profile。

分步演示

profile
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK 21.0.12(Homebrew) / Spring Boot 4.0.8(Spring Framework 7.0.9、Tomcat 11.0.24) / Spring Cloud 2025.1.3;用 javac 编译、java 直接运行,依赖由 Maven 从 Maven Central 解析;数字每次运行会有小幅抖动。用 Spring Boot 4.0.8(spring-boot-starter,非 Web 模式)的单个程序,每个场景一个独立 JVM;配置文件放在临时工作目录;@Profile 的 9 个 Bean 都是普通 @Bean 方法,用返回的标签判断是否生效;写错表达式的场景是单独再编译了一个只含一个 @Profile 的程序
    • 没有验证:测试里的 @ActiveProfiles;Environment.acceptsProfiles 的各种用法;Kubernetes 或 Docker 里用环境变量传 profile 的细节;Spring Cloud Config 按 profile 取配置;profile 名称与配置文件名大小写的关系(本机文件系统不区分大小写,这类结果不可靠,所以只测了 @Profile 的大小写比较);为什么括号不闭合的表达式能通过(我只观察到结果,没有读 Profiles 解析器的源码)
    京ICP备2026067502号-1京公网安备11010502063487号