一、核心概念

1.1 什么是多环境配置?

在软件开发中,通常需要在不同的环境(如开发 dev、测试 test、生产 prod)下运行应用。每个环境可能有不同的配置,例如:

  • 数据库连接(URL、用户名、密码)
  • 服务器端口
  • 日志级别
  • 第三方服务 API 密钥
  • 缓存设置

多环境配置就是根据当前运行环境,自动加载对应的配置文件,无需修改代码。

1.2 Spring Boot 的 Profile 机制

Spring Boot 使用 Profile(配置文件)来实现多环境支持:

  • 通过 @Profile("dev") 或配置文件名(如 application-dev.yml)激活特定环境。
  • 运行时通过 spring.profiles.active 属性指定激活哪个 Profile。

1.3 配置文件加载顺序(优先级从高到低)

Spring Boot 按以下顺序加载配置,后加载的会覆盖先加载的

  1. 命令行参数(--spring.profiles.active=prod
  2. SPRING_APPLICATION_JSON(环境变量中的 JSON)
  3. ServletConfig 初始化参数
  4. ServletContext 初始化参数
  5. java:comp/env JNDI 属性
  6. JVM 系统属性(-Dspring.profiles.active=test
  7. 操作系统环境变量
  8. RandomValuePropertySourcerandom.*
  9. jar 包外的 application-{profile}.propertiesapplication-{profile}.yml
  10. jar 包内的 application-{profile}.propertiesapplication-{profile}.yml
  11. jar 包外的 application.propertiesapplication.yml
  12. jar 包内的 application.propertiesapplication.yml

重点:外部配置 > 内部配置;Profile 配置 > 通用配置。


二、详细操作步骤(适合快速实践)

步骤 1:项目结构准备

创建标准 Spring Boot 项目,结构如下:

src/
├── main/
│   ├── java/
│   │   └── com/example/demo/
│   │       ├── DemoApplication.java
│   │       └── controller/
│   └── resources/
│       ├── application.yml                  # 通用配置
│       ├── application-dev.yml             # 开发环境
│       ├── application-test.yml            # 测试环境
│       ├── application-prod.yml            # 生产环境
│       └── bootstrap.yml                   # (可选)云配置中心

步骤 2:创建通用配置文件 application.yml

# application.yml - 通用配置(所有环境共享)
server:
  port: 8080  # 默认端口,可被覆盖

spring:
  application:
    name: my-springboot-app

logging:
  level:
    root: INFO
    com.example: DEBUG  # 包级别日志

步骤 3:创建开发环境配置 application-dev.yml

# application-dev.yml - 开发环境
spring:
  profiles: dev  # 显式声明此文件属于 dev Profile
  datasource:
    url: jdbc:h2:mem:devdb
    driver-class-name: org.h2.Driver
    username: sa
    password:
  jpa:
    hibernate:
      ddl-auto: create-drop
    show-sql: true
  h2:
    console:
      enabled: true  # 启用 H2 控制台

server:
  port: 8081  # 覆盖通用配置的端口

logging:
  level:
    root: DEBUG
    org.springframework.web: DEBUG
    org.hibernate: DEBUG

步骤 4:创建测试环境配置 application-test.yml

# application-test.yml - 测试环境
spring:
  profiles: test
  datasource:
    url: jdbc:mysql://test-db-host:3306/myapp_test?useSSL=false&serverTimezone=UTC
    username: testuser
    password: testpass
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true

server:
  port: 8082

logging:
  level:
    root: INFO
    com.example: INFO

步骤 5:创建生产环境配置 application-prod.yml

# application-prod.yml - 生产环境
spring:
  profiles: prod
  datasource:
    url: jdbc:mysql://prod-db-host:3306/myapp_prod?useSSL=true&serverTimezone=UTC
    username: ${DB_USERNAME:produser}     # 使用环境变量,提供默认值
    password: ${DB_PASSWORD:prodpass}     # 使用环境变量,提供默认值
    driver-class-name: com.mysql.cj.jdbc.Driver
    hikari:
      maximum-pool-size: 20
      minimum-idle: 5
  jpa:
    hibernate:
      ddl-auto: validate
    show-sql: false
    properties:
      hibernate.format_sql: false
  mail:
    host: smtp.prod.com
    username: ${MAIL_USER}
    password: ${MAIL_PASS}

server:
  port: 80
  tomcat:
    max-threads: 200
    min-spare-threads: 10

logging:
  level:
    root: WARN
    com.example: INFO
    org.springframework: WARN
  config: classpath:logback-prod.xml  # 使用自定义生产日志配置

步骤 6:激活 Profile 的多种方式

方式 1:通过 application.yml 激活(不推荐用于生产)

# application.yml
spring:
  profiles:
    active: dev  # 默认激活 dev

注意:这种方式在部署时无法灵活切换,仅用于本地开发。

方式 2:通过命令行参数(推荐)

# 打包
mvn clean package

# 运行并指定 Profile
java -jar myapp.jar --spring.profiles.active=prod

# 或同时激活多个 Profile
java -jar myapp.jar --spring.profiles.active=prod,ssl

方式 3:通过 JVM 系统属性

java -Dspring.profiles.active=test -jar myapp.jar

方式 4:通过操作系统环境变量

# Linux/Mac
export SPRING_PROFILES_ACTIVE=prod
java -jar myapp.jar

# Windows
set SPRING_PROFILES_ACTIVE=prod
java -jar myapp.jar

方式 5:通过 IDE 配置(开发时)

在 IntelliJ IDEA 或 Eclipse 中,运行配置的 "VM options" 添加:

-Dspring.profiles.active=dev

步骤 7:在代码中使用 Profile

7.1 使用 @Profile 注解

@Component
@Profile("dev") // 仅在 dev 环境下创建此 Bean
public class DevDataInitializer {
    @PostConstruct
    public void init() {
        System.out.println("Initializing DEV data...");
    }
}

@Component
@Profile("prod")
public class ProdEmailService {
    public void sendEmail(String to, String content) {
        // 发送真实邮件
    }
}

@Component
@Profile("!prod") // 非生产环境
public class MockEmailService {
    public void sendEmail(String to, String content) {
        System.out.println("Mock: Sending email to " + to);
    }
}

7.2 在配置类中使用

@Configuration
public class DatabaseConfig {

    @Bean
    @Profile("dev")
    public DataSource devDataSource() {
        return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.H2)
            .addScript("schema.sql")
            .build();
    }

    @Bean
    @Profile("prod")
    @Primary
    public DataSource prodDataSource() {
        // 配置生产数据库连接池
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:mysql://...");
        // ... 其他配置
        return new HikariDataSource(config);
    }
}

7.3 在代码中判断当前 Profile

@RestController
public class StatusController {

    @Autowired
    private Environment environment;

    @GetMapping("/status")
    public Map<String, Object> getStatus() {
        Map<String, Object> status = new HashMap<>();
        status.put("activeProfiles", environment.getActiveProfiles());
        status.put("defaultProfiles", environment.getDefaultProfiles());
        status.put("isDev", environment.acceptsProfiles(Profiles.of("dev")));
        status.put("isProd", environment.acceptsProfiles(Profiles.of("prod")));
        return status;
    }
}

步骤 8:使用 @TestPropertySource 进行测试

@SpringBootTest
@TestPropertySource(locations = "classpath:application-test.yml") // 指定测试配置
class MyServiceTest {

    @Test
    void testSomething() {
        // 测试逻辑
    }
}

或使用 @ActiveProfiles

@SpringBootTest
@ActiveProfiles("test") // 激活 test Profile
class MyServiceTest {
    // ...
}

三、常见错误与解决方案

错误现象 原因分析 解决方案
Could not resolve placeholder 'DB_PASSWORD' 环境变量未设置 确保在运行环境设置了 DB_PASSWORD,或在配置中提供默认值 ${DB_PASSWORD:default}
Profile 未生效 激活方式错误或配置文件名错误 检查 application-{profile}.yml 文件名拼写;确认激活命令正确
配置未被覆盖 加载顺序问题 确认外部配置优先级高于内部;检查配置项是否拼写正确
No active profile set 未指定任何 Profile 设置默认 Profile 或在启动时指定 --spring.profiles.active
数据库连接失败 URL、用户名、密码配置错误 检查对应环境的 application-{profile}.yml 文件

四、注意事项

  1. 敏感信息保护
    • 永远不要将密码、密钥等敏感信息硬编码在配置文件中。
    • 使用环境变量(${VAR_NAME})或配置中心(如 Spring Cloud Config、Consul、Vault)。
  2. 配置文件命名
    • 必须是 application-{profile}.propertiesapplication-{profile}.yml
    • {profile} 是 Profile 名称(如 devtestprod)。
  3. Profile 激活
    • 可以同时激活多个 Profile:--spring.profiles.active=prod,ssl,metrics
    • 优先级:命令行 > JVM 属性 > 环境变量 > application.yml 中的设置。
  4. 配置覆盖
    • 同名属性在 Profile 配置中会覆盖通用配置。
    • 不同环境的独有配置(如 H2 控制台)应放在对应 Profile 文件中。
  5. 打包与部署
    • 确保所有环境的配置文件都打包进 jar(默认行为)。
    • 生产部署时,可通过外部配置文件覆盖 jar 内配置。

五、使用技巧

5.1 外部化配置文件

将配置文件放在 jar 包外部,便于修改:

# 放在与 jar 同级目录
java -jar myapp.jar

# 目录结构
myapp.jar
application.yml
application-prod.yml

Spring Boot 会自动加载外部配置。

5.2 使用占位符和默认值

spring:
  datasource:
    username: ${DB_USER:default_user}   # 如果 DB_USER 不存在,使用 default_user
    password: ${DB_PASS:default_pass}
    url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/mydb

5.3 组合 Profile

# 同时激活 prod 和 ssl Profile
java -jar myapp.jar --spring.profiles.active=prod,ssl
# application-ssl.yml
server:
  ssl:
    enabled: true
    key-store: classpath:keystore.p12
    key-store-password: ${KEYSTORE_PASS}

5.4 使用 spring.config.location 指定配置路径

# 指定配置文件位置
java -jar myapp.jar --spring.config.location=classpath:/,file:/config/

# 或多个位置
java -jar myapp.jar --spring.config.location=optional:file:./config/,optional:classpath:/

5.5 验证配置加载

启动时查看日志,确认加载了哪些配置文件:

The following 1 profile is active: "prod"
...
Located property source: class path resource [application-prod.yml]

六、最佳实践

  1. 环境命名标准化
    • 使用 devteststagingprod 等清晰命名。
  2. 最小化差异
    • 尽量保持各环境配置相似,避免“在我机器上能运行”问题。
  3. 自动化部署
    • 使用 CI/CD 工具(Jenkins、GitLab CI)自动打包并指定 Profile。
  4. 配置审计
    • 记录配置变更,确保可追溯。
  5. 生产配置审查
    • 生产配置必须经过严格审查,避免错误。
  6. 使用配置中心
    • 对于微服务,考虑使用 Spring Cloud Config、Consul、Apollo 等集中管理配置。

七、性能优化

  1. 减少配置文件大小
    • 移除不必要的注释和空行。
  2. 避免频繁 IO
    • 配置文件在启动时加载一次,影响较小。
  3. 使用高效的序列化
    • .yml.properties 性能差异极小,按团队习惯选择。
  4. 缓存配置
    • Spring Boot 自动缓存配置,无需额外操作。
  5. Profile 切换开销
    • Profile 切换是启动时行为,运行时无性能影响。

总结

Spring Boot 的多环境配置通过 Profile 机制实现,灵活且强大。关键在于:

  • 正确命名配置文件(application-{profile}.yml)。
  • 使用外部化配置和环境变量保护敏感信息。
  • 通过命令行、环境变量等方式灵活激活 Profile。
  • 遵循最佳实践,确保环境一致性。

推荐部署流程

  1. 开发:--spring.profiles.active=dev
  2. 测试:--spring.profiles.active=test
  3. 生产:--spring.profiles.active=prod + 环境变量提供密钥

本文基于 Spring Boot 3.x 编写。