Spring Boot 的环境管理是每个项目都绕不开的话题。同一份代码要跑在开发、测试、生产等多个环境里,数据库连接、Redis 地址、日志级别、接口域名往往各不相同,如果靠手动改配置文件来切换环境,不仅麻烦而且极易出错,一旦把测试库地址带到生产环境,后果不堪设想。Spring Boot 内置了一套以 Environment 为核心、以 Profile 为切换开关的环境管理体系,理解它的加载规则和优先级,是做好配置管理的基础。

一、先搞懂 Environment 抽象:配置是怎么被加载的
Spring Boot 中所有的配置项最终都会汇入 Environment 对象。它由两部分组成:一是 PropertySources,即配置来源的集合,包括 application.yml、环境变量、JVM 系统属性、命令行参数等;二是 Profiles,用来标识当前激活的是哪一套环境。每个配置来源都有一个优先级,高优先级的来源会覆盖低优先级的同名配置。
默认的优先级从高到低大致是:命令行参数、SpringApplication.setDefaultProperties 设置的默认值、JVM 系统属性、操作系统环境变量、application-{profile}.yml、application.yml。这个顺序非常关键,比如你在 application.yml 里写了 server.port=8080,又用 java -jar app.jar --server.port=9090 启动,最终生效的一定是 9090,因为命令行参数优先级更高。
可以在代码中直接注入 Environment 来验证配置是否加载成功:
@RestController
public class EnvController {
@Autowired
private Environment environment;
@GetMapping("/env")
public String env() {
// 读取当前激活的 profile
String[] activeProfiles = environment.getActiveProfiles();
// 读取任意配置项
String appName = environment.getProperty("spring.application.name");
return "activeProfiles=" + String.join(",", activeProfiles)
+ ", appName=" + appName;
}
}接口返回的结果能直观告诉你当前跑的是哪个环境、配置有没有被读到,是排查配置问题最常用的手段之一。
二、用 Profile 实现多环境配置文件切换
最经典的做法是按环境拆分配置文件。主配置 application.yml 存放公共配置,然后为每个环境建一个独立文件,命名格式为 application-{profile}.yml,例如 application-dev.yml、application-test.yml、application-prod.yml。目录结构如下:
src/main/resources ├── application.yml # 公共配置 + 指定默认环境 ├── application-dev.yml # 开发环境 ├── application-test.yml # 测试环境 └── application-prod.yml # 生产环境
主配置文件中通过 spring.profiles.active 指定默认激活的环境,开发阶段默认用 dev:
spring:
application:
name: order-service
profiles:
active: dev # 默认激活开发环境application-dev.yml 里只写和环境相关的差异配置:
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/order_dev?useSSL=false
username: dev_user
password: dev123
logging:
level:
com.example: debugapplication-prod.yml 则指向生产资源,并把日志级别收紧:
spring:
datasource:
url: jdbc:mysql://prod-db-host:3306/order_prod?useSSL=true
username: prod_user
password: ${DB_PASSWORD}
logging:
level:
com.example: warn打包部署时不需要改文件,直接用命令行覆盖激活的环境即可:java -jar app.jar --spring.profiles.active=prod,或者通过环境变量 SPRING_PROFILES_ACTIVE=prod 设置。由于命令行参数和环境变量的优先级高于配置文件,生产环境可以做到一份 jar 包随处运行。
除了整文件切换,还可以用 spring.config.activate.on-profile 在同一个 yml 文件里写多个环境段落,配置较少的项目用这种写法更紧凑:
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/app_dev
---
spring:
config:
activate:
on-profile: prod
datasource:
url: jdbc:mysql://prod-host:3306/app_prod三、按环境装配 Bean:@Profile 注解的用法
Profile 不仅能切换配置,还能控制 Bean 的加载。在类或方法上加 @Profile 注解,只有对应环境激活时该 Bean 才会注册到容器中,典型的场景比如开发环境用内存数据库、生产环境用真实数据源:
@Configuration
public class DataSourceConfig {
@Bean
@Profile("dev")
public DataSource devDataSource() {
// 开发环境使用 H2 内存库,启动即可跑
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("schema.sql")
.build();
}
@Bean
@Profile("!dev")
public DataSource prodDataSource() {
// 非开发环境走正式数据源
HikariConfig config = new HikariConfig();
config.setJdbcUrl(env.getProperty("spring.datasource.url"));
config.setUsername(env.getProperty("spring.datasource.username"));
return new HikariDataSource(config);
}
}注意 @Profile("!dev") 这种取反写法,表示除了 dev 之外的所有环境都生效,比逐个罗列 test、prod 更省事。注解里也支持逻辑运算,例如 @Profile({"dev & local"})、@Profile("dev | test"),可以组合出较灵活的激活条件。
配置类内部还提供了 @ConditionalOnProperty 作为补充,它根据某个配置项的值决定是否生效,比如很多开源组件的自定义开关就是这么实现的。两者搭配使用,可以让不同环境的 Bean 装配完全自动化,不需要在代码里写任何 if 判断。
四、生产环境进阶:敏感信息管理与常见坑
生产环境的数据库密码、云服务密钥不应该明文写在代码仓库里。推荐的做法有两类:一是用占位符引用环境变量,例如上面示例中的 ${DB_PASSWORD},运维在服务器上通过环境变量或启动参数注入真实值;二是引入 Jasypt 对配置项加密,密文以 ENC(xxx) 的形式存放,解密密钥通过环境变量传入:
jasypt:
encryptor:
password: ${JASYPT_KEY}
spring:
datasource:
password: ENC(G8kP2xQ9mVvTn3RtL0aZcQ==)再配合配置中心(如 Nacos、Apollo)可以把配置从 jar 包里彻底剥离,实现配置热更新和统一审计,这是中大型项目的主流方案。
最后列几个配置不生效时的高频排查点:第一,yml 里同一层级键名重复,后面的会覆盖前面的,缩进错位也会导致配置挂到错误的节点下;第二,spring.profiles.active 同时在配置文件和命令行设置,命令行优先,如果发现环境不对,先用 Environment 打印确认实际激活值;第三,多模块项目里多个 jar 都带 application.yml,注意 spring.config.location 或外置配置文件的加载顺序,外置的 config/ 目录优先级高于 jar 包内文件;第四,从配置中心加载的配置优先级通常高于本地文件,排查时要考虑配置中心是否覆盖了本地值。掌握这些规则后,多环境管理就会变得清晰可控。
Spring Boot多环境配置Profile修改时间:2026-09-11 08:22:36