环境与 Profile
Spring Boot 通过 Profile 机制实现多环境配置隔离,让同一套代码在开发、测试、生产环境使用不同的配置,无需手动切换。
核心概念
| 概念 | 说明 |
|---|---|
| Profile | 环境标识符(如 dev、test、prod),激活后对应配置生效 |
| Environment | Spring 抽象,聚合了所有 PropertySource,可注入使用 |
| PropertySource | 配置来源,包括配置文件、系统变量、命令行参数等 |
spring.profiles.active | 指定当前激活的 Profile |
配置文件与 Profile 绑定
多文件方式(推荐)
每个 Profile 对应独立文件,优先级高于主配置:
src/main/resources/
├── application.yml # 公共配置
├── application-dev.yml # 开发环境
├── application-test.yml # 测试环境
└── application-prod.yml # 生产环境
application.yml 中指定默认激活的 Profile:
spring:
profiles:
default: dev多文档方式(单文件)
同一个 application.yml 内用 --- 分隔文档块:
# 公共配置
server:
port: 8080
---
spring:
config:
activate:
on-profile: dev
datasource:
url: jdbc:mysql://localhost:3306/dev_db
---
spring:
config:
activate:
on-profile: prod
datasource:
url: jdbc:mysql://prod-host:3306/prod_db注意:Spring Boot 2.4 起弃用了
spring.profiles块,改用spring.config.activate.on-profile。
激活 Profile
配置文件
spring:
profiles:
active: dev命令行参数(优先级最高,覆盖配置文件)
java -jar app.jar --spring.profiles.active=prod环境变量
export SPRING_PROFILES_ACTIVE=prod
java -jar app.jarJVM 参数
java -Dspring.profiles.active=prod -jar app.jar测试中激活
@SpringBootTest
@ActiveProfiles("test")
class UserServiceTest {
// 使用 application-test.yml 配置
}相关内容参见 测试。
@Profile 注解
条件注册 Bean
@Configuration
public class DataSourceConfig {
@Bean
@Profile("dev")
public DataSource devDataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.build();
}
@Bean
@Profile("prod")
public DataSource prodDataSource() {
HikariConfig config = new HikariConfig();
config.setJdbcUrl(env.getProperty("datasource.url"));
return new HikariDataSource(config);
}
}组合用法
@Profile("!prod") // 非生产环境
@Profile({"dev", "test"}) // dev 或 test
@Profile("dev & local") // dev 且 local(Spring 5.1+)与 @Conditional 的关系:@Profile 本质上是 @Conditional(ProfileCondition.class) 的封装,更细粒度的条件控制参见 条件注解。
Profile 组(Spring Boot 2.4+)
将多个 Profile 组合为一个逻辑组,只需激活组名即可:
spring:
profiles:
group:
prod:
- prod-db
- prod-mq
- prod-cache
dev:
- dev-db
- local-mock激活 prod 时,prod-db、prod-mq、prod-cache 全部生效。
Environment 接口
注入 Environment 可在代码中动态读取当前环境信息:
@Service
public class AppInfoService {
@Autowired
private Environment env;
public void printInfo() {
String[] profiles = env.getActiveProfiles();
String dbUrl = env.getProperty("spring.datasource.url");
boolean isProd = env.acceptsProfiles(Profiles.of("prod"));
}
}@PropertySource
从自定义配置文件加载属性(不支持 YAML,只支持 .properties):
@Configuration
@PropertySource("classpath:custom.properties")
@PropertySource(value = "file:/opt/app/secret.properties", ignoreResourceNotFound = true)
public class AppConfig {
}若要支持 YAML,需自定义 PropertySourceFactory。
配置优先级(从高到低)
Spring Boot 的配置来源覆盖从 CI/CD 流水线到本地打包文件的整条链路,优先级从高到低:
| 优先级 | 来源 | 典型载体 |
|---|---|---|
| 1 | 命令行参数 --key=value | java -jar、Dockerfile CMD、Jenkinsfile sh |
| 2 | OS 环境变量 | Dockerfile ENV、docker run -e、Jenkinsfile environment {} |
| 3 | JVM 系统属性 -Dkey=value | java -D 启动参数 |
| 4 | Nacos 远程配置 | Spring Cloud Alibaba 拉取的 PropertySource |
| 5 | 外置 application-{profile}.yml | jar 包外 ./config/ 或 ./ 目录 |
| 6 | 外置 application.yml | jar 包外 ./config/ 或 ./ 目录 |
| 7 | bootstrap-{profile}.yml | classpath,引导上下文 |
| 8 | bootstrap.yml | classpath,引导上下文 |
| 9 | classpath application-{profile}.yml | jar 内打包的 Profile 配置 |
| 10 | classpath application.yml | jar 内打包的通用默认配置 |
同一位置下,Profile 文件(
application-{profile}.yml)始终比基础文件(application.yml)优先级高。
1. 命令行参数(最高优先级)
--key=value 格式,Spring Boot 将其注册为最高优先级的 PropertySource,可覆盖任何其他来源:
java -jar app.jar --spring.profiles.active=prod --server.port=9090Jenkinsfile 中传入:
stage('Deploy') {
steps {
sh 'java -jar app.jar --spring.profiles.active=prod --server.port=8080'
}
}Dockerfile 中传入:
# ENTRYPOINT 固定主命令,CMD 作为默认参数,docker run 时可覆盖 CMD 部分
ENTRYPOINT ["java", "-jar", "app.jar"]
CMD ["--spring.profiles.active=prod"]2. OS 环境变量
Spring Boot 自动将环境变量映射为属性:大写 + 下划线 → 小写 + 点号(SERVER_PORT → server.port)。
Dockerfile 中设置(镜像级,构建时固化):
ENV SPRING_PROFILES_ACTIVE=prod
ENV SERVER_PORT=8080
ENV SPRING_DATASOURCE_URL=jdbc:mysql://prod-db:3306/mydbdocker run 时覆盖(容器级,运行时灵活):
docker run \
-e SPRING_PROFILES_ACTIVE=prod \
-e SERVER_PORT=8080 \
-e SPRING_DATASOURCE_PASSWORD=secret \
myapp:latest
docker run -e与 DockerfileENV同属 OS 环境变量,前者在运行时注入,会覆盖镜像内烘焙的ENV值。
Jenkinsfile 中设置(流水线级):
pipeline {
environment {
SPRING_PROFILES_ACTIVE = 'prod'
SERVER_PORT = '8080'
}
stages {
stage('Deploy') {
steps {
// 敏感信息通过 Credentials 注入,不写死在 Jenkinsfile 中
withCredentials([string(credentialsId: 'db-password', variable: 'SPRING_DATASOURCE_PASSWORD')]) {
sh 'java -jar app.jar'
}
}
}
}
}
environment {}块中的变量在sh步骤执行时作为 OS 环境变量注入子进程,Spring Boot 启动时可直接读取。
3. JVM 系统属性
-D 参数,优先级低于环境变量,常用于非 Spring 框架的 JVM 级配置:
java -Dspring.profiles.active=prod \
-Dserver.port=8080 \
-jar app.jar4. Nacos 远程配置
Spring Cloud Alibaba 将 Nacos 配置注册为高优先级 PropertySource(高于本地文件,低于命令行和环境变量)。连接信息写在 bootstrap.yml 中,应用启动时自动拉取:
# bootstrap.yml
spring:
application:
name: my-service
cloud:
nacos:
config:
server-addr: ${NACOS_SERVER:localhost:8848}
namespace: ${NACOS_NAMESPACE:prod}
file-extension: yaml
# 扩展配置,列表靠后的优先级更高
extension-configs:
- data-id: common.yaml
group: GLOBAL
refresh: true
- data-id: my-service-prod.yaml
group: DEFAULT_GROUP
refresh: trueNacos 内部优先级:主配置(${spring.application.name}.yaml)> 扩展配置(逆序)> 共享配置(shared-configs)。
若需要本地配置覆盖 Nacos(如本地调试),可设置:
spring:
cloud:
config:
override-none: true5 & 6. 外置配置文件(jar 包外部)
将配置文件放在 jar 包同级目录或其 config/ 子目录,Spring Boot 启动时自动加载,无需修改 jar 包本身,适合容器化部署时通过 volume 挂载差异化配置:
/opt/app/
├── app.jar
├── config/
│ ├── application.yml # 外置基础配置(优先级 6)
│ └── application-prod.yml # 外置 Profile 配置(优先级 5)
└── application.yml # 次级外置配置
Spring Boot 外部配置搜索顺序(后加载的优先级更高):
classpath:/ → classpath:/config/ → file:./ → file:./config/*/ → file:./config/
Docker 部署时挂载外部配置目录:
VOLUME ["/opt/app/config"]docker run -v /host/config:/opt/app/config myapp:latest7 & 8. bootstrap.yml(引导上下文)
bootstrap 上下文在应用上下文之前启动,专门用于加载连接配置中心的基础参数(Nacos 地址、namespace 等),不应放置业务配置:
# bootstrap.yml —— 只放引导基础设施所需的最小配置
spring:
application:
name: my-service
cloud:
nacos:
config:
server-addr: ${NACOS_SERVER:localhost:8848}
namespace: ${NACOS_NAMESPACE:dev}bootstrap 上下文的属性优先级低于应用上下文(
application.yml会覆盖bootstrap.yml中同名属性),因此bootstrap.yml中不要放需要被 Profile 覆盖的业务值。
9 & 10. classpath 内置配置文件(最低优先级)
打包在 jar 内部,作为所有环境的默认兜底值:
src/main/resources/
├── application.yml # 公共默认值(优先级 10)
├── application-dev.yml # dev Profile 默认值(优先级 9)
├── application-test.yml
└── application-prod.yml
完整覆盖链路示例
以数据库密码为例,展示从研发到生产的典型覆盖关系:
classpath application.yml spring.datasource.password=(不写,无默认)
↑ 被覆盖
classpath application-dev.yml spring.datasource.password=dev123
↑ 被覆盖
Nacos 远程配置 spring.datasource.password=test456(test 环境)
↑ 被覆盖
OS 环境变量(Jenkinsfile/Docker) SPRING_DATASOURCE_PASSWORD=prod_secret
↑ 被覆盖(紧急场景)
命令行参数 --spring.datasource.password=override
属性绑定到 Bean 的方式参见 属性绑定,自动配置中 Profile 的应用参见 自动配置。
常见场景
区分日志级别
# application-dev.yml
logging:
level:
root: DEBUG
com.example: TRACE
# application-prod.yml
logging:
level:
root: WARN关闭 Swagger(生产环境)
@Bean
@Profile("!prod")
public OpenAPI openAPI() {
return new OpenAPI().info(new Info().title("API 文档"));
}数据库初始化脚本
# application-dev.yml
spring:
sql:
init:
mode: always # 每次启动都执行 schema.sql / data.sql
# application-prod.yml
spring:
sql:
init:
mode: never