Spring Boot 如何做多环境配置?@Profile 和 spring.profiles.active 怎么用?
简化版
Spring Boot 用 Profile(环境标识) 实现「一套代码、多套环境配置」——开发(dev)、测试(test)、生产(prod)各用各的数据库、端口、日志级别。做法:① 为每个环境写一个配置文件 application-dev.yml、application-prod.yml;② 用 spring.profiles.active 指定当前激活哪个环境(在 application.yml 里、或启动参数 --spring.profiles.active=prod、或环境变量)。激活哪个 Profile,就加载对应的 application-{profile}.yml,覆盖 application.yml 里的公共配置。代码层面用 @Profile("prod") 注解让某个 Bean 只在特定环境生效。
详细版
配置文件方式(最常用):
application.yml # 公共配置 + 指定激活哪个 profile
application-dev.yml # 开发环境专属(本地数据库、debug 日志)
application-test.yml # 测试环境
application-prod.yml # 生产环境(生产数据库、warn 日志)
# application.yml(公共 + 激活开关)
spring:
profiles:
active: dev # 激活 dev,会加载 application-dev.yml
server:
port: 8080 # 公共配置,各环境共享(除非被 profile 配置覆盖)
# application-prod.yml(生产专属,覆盖公共配置)
spring:
datasource:
url: jdbc:mysql://prod-db:3306/app
server:
port: 80 # 覆盖公共的 8080
激活 Profile 的多种方式(优先级从低到高):
1. application.yml 里 spring.profiles.active: dev (最低,写死不灵活)
2. 启动参数:java -jar app.jar --spring.profiles.active=prod
3. 环境变量:export SPRING_PROFILES_ACTIVE=prod (生产常用)
4. JVM 参数:-Dspring.profiles.active=prod
代码层面 @Profile:
@Configuration
public class DataSourceConfig {
@Bean
@Profile("dev") // 只在 dev 环境创建
public DataSource devDataSource() { return new H2DataSource(); }
@Bean
@Profile("prod") // 只在 prod 环境创建
public DataSource prodDataSource() { return new HikariDataSource(); }
}
⚠️ 生产环境别把
spring.profiles.active写死在application.yml里(比如写成active: dev,一不小心用 dev 配置连了生产就出事)。正确做法:application.yml不指定或指定默认环境,生产用启动参数/环境变量激活 prod(SPRING_PROFILES_ACTIVE=prod),让部署环境决定用哪套配置,代码里不写死。
完整版教学
一、为什么需要多环境配置
同一个应用,在不同环境下的配置天差地别:
开发(dev) 生产(prod)
数据库 本地/H2内存库 生产 MySQL 集群
端口 8080 80
日志级别 DEBUG(看细节) WARN(少输出)
第三方地址 测试沙箱 正式接口
缓存 可关闭 必开
如果把这些配置写死在代码里,每次切环境都要改代码、重新打包,极易出错(忘了改就把测试配置带到生产)。Profile 的思路是:把「随环境变化的配置」抽出来,按环境分文件存放,运行时根据激活的 Profile 加载对应的那套。这样一次打包、到处运行——同一个 jar 包,部署到不同环境时通过「激活不同 Profile」用不同配置,代码和打包产物完全不变。这是「配置与环境解耦」的工程实践。
二、配置文件的命名与加载规则
Spring Boot 的多环境配置靠一个命名约定:application-{profile}.yml:
application.yml → 公共配置,所有环境都加载(基础层)
application-{profile}.yml → 特定 profile 的配置,只在该 profile 激活时加载(覆盖层)
加载逻辑:
1. 先加载 application.yml(公共配置)
2. 看 spring.profiles.active = 哪个 → 加载对应的 application-{active}.yml
3. profile 配置覆盖公共配置里的同名项(后加载的优先)
用一个例子看清覆盖:
application.yml: server.port=8080, app.name=myapp
application-prod.yml: server.port=80
激活 prod 后的最终配置:
server.port = 80 ← 被 prod 覆盖
app.name = myapp ← prod 没配,用公共的
规律:公共配置放 application.yml(各环境共享的),差异配置放 application-{profile}.yml(各环境不同的),profile 配置覆盖公共配置。这样公共部分只写一次,差异部分按环境分开,既不重复又清晰。
三、激活 Profile 的多种方式与优先级
「激活哪个 Profile」有多种设置方式,优先级不同(高优先级覆盖低优先级),这决定了「部署时怎么切环境」:
优先级从低到高:
① application.yml 里 spring.profiles.active: dev
(写死在配置里,不灵活,改环境要改文件重新打包)
② 命令行参数 --spring.profiles.active=prod
(启动时指定,java -jar app.jar --spring.profiles.active=prod)
③ 环境变量 SPRING_PROFILES_ACTIVE=prod
(容器/K8s 部署常用,环境决定配置)
④ JVM 系统属性 -Dspring.profiles.active=prod
生产实践的关键原则:代码/配置文件里不写死生产 Profile,由部署环境(环境变量/启动参数)决定激活哪个。比如:
本地开发:application.yml 里默认 active: dev(方便本地跑)
生产部署:不改代码,用 SPRING_PROFILES_ACTIVE=prod 环境变量激活
→ 同一个 jar,本地跑 dev、生产跑 prod,靠环境变量区分
这样避免了「打包时把环境写死」的风险——高优先级的环境变量能覆盖配置文件里的默认值,让「用哪套配置」的决定权交给部署环境,而不是写死在代码里。
四、@Profile 注解:让 Bean 按环境生效
除了配置文件,@Profile 注解能让代码层面的 Bean 只在特定环境生效:
@Component
@Profile("dev")
public class MockPaymentService implements PaymentService {
// dev 环境用假的支付(不真扣钱),方便本地测试
}
@Component
@Profile("prod")
public class RealPaymentService implements PaymentService {
// prod 环境用真实支付
}
// 激活 dev → 注入 MockPaymentService;激活 prod → 注入 RealPaymentService
用途:当不同环境需要「不同的实现类」而非仅「不同的配置值」时,用 @Profile。典型场景:开发环境用内存数据库/Mock 服务,生产用真实服务。它还支持逻辑组合:@Profile("!prod")(非 prod 环境)、@Profile({"dev", "test"})(dev 或 test)。原理:Profile 本质是一种特殊的 @Conditional(@Profile 底层是 @Conditional(ProfileCondition.class))——所以它也是「条件装配」的一种,只是条件是「当前激活的 Profile 是否匹配」。
五、Profile-specific 配置的进阶用法
除了独立文件,还有几种多环境组织方式:
① 独立文件(推荐,清晰):application-{profile}.yml
② 单文件多文档块(yml 用 --- 分隔,properties 用 #--- ):
# application.yml
spring:
profiles:
active: dev
---
spring:
config:
activate:
on-profile: dev # 这一块只在 dev 生效
datasource:
url: jdbc:h2:mem:test
---
spring:
config:
activate:
on-profile: prod # 这一块只在 prod 生效
③ 激活多个 profile:spring.profiles.active=prod,monitoring
(同时激活 prod 和 monitoring,两套配置叠加)
④ profile 分组(2.4+):把多个 profile 组合成一个组
spring.profiles.group.prod=prod,prod-db,prod-mq
(激活 prod 时自动激活 prod-db、prod-mq)
实践建议:环境多、配置差异大用独立文件(每个环境一个文件,清晰);简单场景用单文件多文档块也行。可以同时激活多个 Profile(如 prod + monitoring),让配置按功能模块化组合。Profile 分组(spring.profiles.group)适合「一个逻辑环境由多个 profile 组成」的情况。
六、常见坑与最佳实践
多环境配置有几个高频坑要避开:
| 坑 | 后果 | 正确做法 |
|---|---|---|
| 把 active 写死成 prod/dev | 打包后环境固定,切环境要改代码 | 用环境变量/启动参数激活 |
| 敏感配置(密码)明文放 profile 文件 | 泄露风险、进 git 仓库 | 用环境变量/配置中心/加密 |
| 公共配置和环境配置写重复 | 改一处漏改多处 | 公共放 application.yml,差异放 profile 文件 |
| 忘了某环境的 profile 文件 | 该环境启动用了公共/默认值 | 每个环境都建对应文件并检查 |
最佳实践总结:公共配置放 application.yml、差异配置分环境文件、激活方式用环境变量(生产)、敏感信息不进配置文件(用环境变量或配置中心)。这样一套代码一次打包,通过环境激活不同 Profile 适配所有环境,既安全又不重复。大型项目还常配合配置中心(Nacos/Apollo)做集中的多环境配置管理,比本地文件更灵活(可动态刷新、可审计)。
记忆钩子:「多环境靠 Profile:application-{profile}.yml 分环境配置、公共放 application.yml、profile 配置覆盖公共;用 spring.profiles.active 激活(生产用环境变量别写死);@Profile 注解让 Bean 按环境生效(底层是 @Conditional);敏感配置别进文件」。
七、常见误区与追问
- 误区:多环境要打多个 jar 包。 一次打包一个 jar,通过激活不同 Profile 用不同配置适配所有环境(一次构建、到处运行),不用为每个环境单独打包。
- 误区:spring.profiles.active 写在 application.yml 里最好。 生产别写死——应由环境变量/启动参数激活,否则打包后环境固定、切换要改代码,还可能误用错环境配置。
- 误区:@Profile 只能用于配置值切换。 它让整个 Bean 按环境生效(不同环境用不同实现类,如 Mock vs 真实服务);仅切配置值用 application-{profile}.yml 即可。
- 误区:激活一个 profile 就不能再激活别的。 可以同时激活多个(
active=prod,monitoring),多套配置叠加;还有 profile 分组把多个组合成一个。 - 追问:@Profile 底层是怎么实现的? 它是一种特殊的 @Conditional——@Profile 底层是 @Conditional(ProfileCondition.class),条件是「当前激活的 Profile 是否匹配注解里指定的」,所以属于条件装配。
- 追问:不同来源的 profile 激活优先级? 从低到高:application.yml 内的 active < 命令行参数 < 环境变量 < JVM 系统属性;高优先级覆盖低优先级,所以环境变量能覆盖配置文件里的默认。
- 追问:生产环境敏感配置(数据库密码)怎么管理? 别明文放 profile 文件(会进 git、易泄露),用环境变量注入、或配置中心(Nacos/Apollo)、或加密(jasypt),让敏感信息不落在代码仓库里。
八、加强记忆
Spring Boot 多环境配置靠 Profile 实现「一套代码、一次打包、多环境运行」。核心两步:① 分环境写配置文件 application-{profile}.yml(dev/test/prod 各一个),公共配置放 application.yml,profile 配置覆盖公共配置;② 用 spring.profiles.active 激活某个环境,激活哪个就加载哪个 application-{profile}.yml。激活方式有多种(配置文件内 < 命令行 < 环境变量 < JVM 属性,高优先级覆盖低),生产关键原则是别把 active 写死在配置里,用环境变量激活(让部署环境决定配置,避免误用)。代码层面用 @Profile("prod") 让 Bean 只在特定环境生效(不同环境用不同实现类,如 Mock vs 真实服务;底层是 @Conditional(ProfileCondition),属于条件装配,支持 !prod、多值组合)。进阶:可同时激活多个 profile、用 profile 分组、单文件多文档块。避坑:active 别写死、敏感配置别进文件(用环境变量/配置中心)、公共与差异分开写。一句话「application-{profile}.yml 分环境、application.yml 放公共、active 激活(生产用环境变量)、@Profile 让 Bean 按环境生效」。