springboot

多环境配置与外部化配置

By AI-Writer 9 min read

多环境配置与外部化配置

Spring Boot 的配置体系极为灵活,支持多环境切换、外部化配置、强类型绑定等多种特性。掌握这些配置技巧,能让你的应用在不同环境间零改动切换,同时保证敏感信息的安全。

多环境配置

application-{profile}.yml

Spring Boot 通过 Profile 机制支持多环境配置。只需创建以 application-\{profile\}.yml 命名的文件:

plaintext
src/main/resources/
├── application.yml              # 公共配置(所有环境共享)
├── application-dev.yml          # 开发环境
├── application-test.yml         # 测试环境
├── application-prod.yml         # 生产环境
└── application-local.yml        # 本地个性化配置

激活 Profile

bash
# 方式1:启动参数
java -jar app.jar --spring.profiles.active=prod

# 方式2:环境变量(OS 环境变量优先级更高)
export SPRING_PROFILES_ACTIVE=prod

# 方式3:JVM 参数
java -Dspring.profiles.active=prod -jar app.jar

# 方式4:application.yml 中设置(不推荐,会被启动参数覆盖)
spring:
  profiles:
    active: prod

配置优先级

Spring Boot 的配置按优先级从高到低排序(高优先级覆盖低优先级):

  1. 命令行参数 --spring.profiles.active=prod
  2. OS 环境变量 SPRING_PROFILES_ACTIVE=prod
  3. application-{profile}.yml(带 profile 的配置文件)
  4. application.yml(不带 profile 的默认配置)
  5. @PropertySource@TestPropertySource 注解加载的配置文件

配置示例

yaml
# application.yml — 公共配置
spring:
  application:
    name: blog-api
  datasource:
    url: jdbc:mysql://localhost:3306/blog

# application-dev.yml — 开发环境
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/blog_dev
  h2:
    console:
      enabled: true
  jpa:
    show-sql: true

logging:
  level:
    com.example: DEBUG
    org.hibernate.SQL: DEBUG

# application-prod.yml — 生产环境
spring:
  datasource:
    url: jdbc:mysql://prod-db.example.com:3306/blog
  jpa:
    show-sql: false

logging:
  level:
    com.example: INFO
  file:
    name: /var/log/blog/application.log

@ConfigurationProperties 强类型配置绑定

@ConfigurationProperties 是将外部配置绑定到 POJO 的首选方式,相比 @Value 更具类型安全和代码可读性。

基本用法

java
// 配置属性类
@ConfigurationProperties(prefix = "app")
public class AppProperties {

    private String name;
    private String version;
    private int maxConnections = 100;
    private List<String> allowedOrigins = new ArrayList<>();
    private Map<String, String> metadata = new HashMap<>();

    // 支持嵌套配置对象
    private Email email = new Email();
    private Retry retry = new Retry();

    // getters and setters(必须有,或使用 Lombok @Data)
    public static class Email {
        private String host = "smtp.example.com";
        private int port = 587;
        private String username;
        private String password;
        // getters and setters
    }

    public static class Retry {
        private int maxAttempts = 3;
        private long delayMs = 1000;
        // getters and setters
    }
}
yaml
# application.yml
app:
  name: blog-api
  max-connections: 200
  allowed-origins:
    - "http://localhost:3000"
    - "http://dev.example.com"
  metadata:
    team: backend
    owner: alice
  email:
    host: smtp.gmail.com
    port: 587
    username: noreply@example.com
    password: ${SMTP_PASSWORD}  # 引用环境变量
  retry:
    max-attempts: 5
    delay-ms: 2000

注册为 Bean

java
// 方式1:@EnableConfigurationProperties(推荐,精确控制启用时机)
@Configuration
@EnableConfigurationProperties(AppProperties.class)
public class AppConfig {
    // ...
}

// 方式2:@Component(自动注册为 Bean)
@Component
@ConfigurationProperties(prefix = "app")
@Validated  // 启用配置校验
public class AppProperties {
    // ...
}

启动类上的快捷方式

java
@SpringBootApplication
@ConfigurationPropertiesScan  // Spring Boot 2.2+ 新增,扫描 @ConfigurationProperties 类
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

@Value 占位符

@Value 适合注入单个配置值,不适合绑定复杂结构:

java
@Service
public class NotificationService {

    // 注入简单值
    @Value("${app.email.host}")
    private String smtpHost;

    @Value("${app.email.port:587}")  // 支持默认值
    private int smtpPort;

    @Value("${app.retry.max-attempts:3}")
    private int maxRetries;

    // 注入环境变量
    @Value("${HOME:/tmp}")
    private String homeDir;

    @Value("${spring.application.name}")
    private String appName;

    // SpEL 表达式
    @Value("#{systemProperties['user.timezone'] ?: 'Asia/Shanghai'}")
    private String timezone;

    // 注入列表(需要借助 SpEL)
    @Value("#{'${app.allowed-origins}'.split(',')}")
    private List<String> allowedOrigins;

    // ❌ @Value 不支持复杂对象绑定
    // ✅ 应该使用 @ConfigurationProperties
}

配置校验

@Validated + @Constraint

java
@Component
@ConfigurationProperties(prefix = "app")
@Validated  // 开启校验
public class AppProperties {

    @NotBlank
    @Pattern(regexp = "^[a-z][a-z0-9-]*$")
    private String name;

    @Min(1)
    @Max(65535)
    private int port;

    @Email
    private String adminEmail;

    @NestedConfigurationProperty  // 嵌套对象也要校验
    private Email email = new Email();
}

启用后,若配置不合法,Spring Boot 启动时会抛出 BindException 并列出所有错误:

plaintext
Binding to target org.springframework.boot.context.properties.bind.BindException: ... failed:

    Property: app.port
    Value: 99999
    Reason: 必须介于 1 到 65535 之间

敏感信息加密

jasypt-spring-boot(社区方案)

xml
<dependency>
    <groupId>com.github.ulisesbocchio</groupId>
    <artifactId>jasypt-spring-boot-starter</artifactId>
    <version>3.0.5</version>
</dependency>
yaml
# 使用 ENC() 包裹加密值
spring:
  datasource:
    password: ENC(abc123DEF456/xxx==)  # AES 加密后的密文

jasypt:
  encryptor:
    password: ${JASYPT_PASSWORD}  # 脱敏:密码来自环境变量
    algorithm: PBEWithMD5AndDES

加密时使用命令行工具生成密文:

bash
# Maven 插件方式
./mvnw jasypt:encrypt -Djasypt.encryptor.password=mySecret

# 命令行方式
java -cp jasypt-*.jar org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI \
    input=myPassword algorithm=PBEWithMD5AndDES password=mySecret

配置加载顺序

Spring Boot 配置文件加载顺序(后面的覆盖前面的):

plaintext
1. classpath:/application.yml
2. classpath:/application-{profile}.yml
3. file:./config/application.yml(项目根目录 config 子目录)
4. file:./application.yml(项目根目录)
5. classpath:/application-local.yml(本地覆盖)
6. OS 环境变量
7. 命令行参数

最佳实践:application.yml 提交到版本控制,仅在本地创建 application-local.yml 覆盖个性化设置(如本地数据库密码),并在 .gitignore 中排除 *-local.yml

运行时动态配置刷新

Spring Boot 3.x 支持可选配置,在运行时重新绑定配置值:

java
@ConfigurationProperties(prefix = "app.feature")
@RefreshScope  // Spring Cloud 注解:重新创建 Bean 并绑定新配置
public class FeatureProperties {

    private boolean darkMode = false;
    private List<String> enabledModules = new ArrayList<>();

    // getters and setters
}

当通过 Spring Cloud Config Server 或 Actuator 端点修改配置后,带 @RefreshScope 的 Bean 会重新创建并读取新值。

小结

  • 多环境通过 application-\{profile\}.yml 实现,激活方式是 --spring.profiles.active=xxx
  • @ConfigurationProperties 是强类型配置绑定的首选方式,配合 @Validated 可实现配置校验
  • @Value 适合单个简单值注入,支持默认值和 SpEL 表达式
  • 敏感信息通过环境变量 + ${VAR} 占位符方式注入,避免明文写在配置文件中
  • Spring Boot 3.x 支持 ConfigurationPropertiesScan 注解,自动扫描配置属性类
#springboot #configuration #yaml #profile

评论

A

Written by

AI-Writer

Related Articles

springboot
#2

Spring Boot 自动配置原理

深入理解 @SpringBootApplication 注解组合、自动配置生效机制(spring.factories 与 AutoConfiguration.imports)、自定义 Starter 的编写方法

Read More
springboot
#7

Spring Security 安全认证

Spring Security 6.x 核心架构、认证与授权概念、基于 JWT 的无状态登录实现、OAuth 2.0 资源服务器入门、@PreAuthorize 方法级安全注解

Read More
springboot
#6

多环境配置与外部化配置

application-&#123;profile&#125;.yml 多环境切换、@ConfigurationProperties 强类型配置绑定、@Value 占位符、配置加密与自定义配置加载顺序

Read More