Spring Boot 外部化配置详解

1 阅读4分钟

Spring Boot 外部化配置详解

一、什么是外部化配置?

外部化配置是指将应用配置(如数据库连接、端口、日志级别等)从代码中分离出来,放在应用外部(如配置文件、环境变量、命令行参数等),以便在不修改代码的情况下改变应用行为。

Spring Boot 支持多种外部配置来源,按优先级从高到低排列,高优先级的配置会覆盖低优先级的配置。

二、配置来源与优先级

Spring Boot 的配置加载顺序如下(从高到低):

优先级配置来源示例
1命令行参数--server.port=8081
2操作系统环境变量SERVER_PORT=8081
3JVM 系统属性-Dserver.port=8081
4application-{profile}.properties/ymlapplication-prod.yml
5application.properties/yml默认配置文件
6@PropertySource 注解加载自定义配置文件
7默认值代码中的 @Value 默认值

理解这个优先级顺序是排查配置不生效问题的前提。

# 命令行参数最高优先级
java -jar myapp.jar --server.port=8081

# 环境变量(将 . 替换为 _,转为大写)
export SERVER_PORT=8081
java -jar myapp.jar

# JVM 系统属性
java -Dserver.port=8081 -jar myapp.jar

三、Profile 多环境配置

Profile 是 Spring Boot 多环境管理的基础机制。通过激活不同的 Profile,应用会加载对应的配置文件。

3.1 多文件方式

application.yml           # 通用配置
application-dev.yml       # 开发环境
application-test.yml      # 测试环境
application-prod.yml      # 生产环境

3.2 单文件多文档块方式

# 默认配置
spring:
  profiles:
    active: dev

---
# 开发环境
spring:
  config:
    activate:
      on-profile: dev
server:
  port: 8080

---
# 生产环境
spring:
  config:
    activate:
      on-profile: prod
server:
  port: 80

3.3 激活 Profile 的方式

# 命令行激活
java -jar myapp.jar --spring.profiles.active=prod

# 环境变量激活
export SPRING_PROFILES_ACTIVE=prod

# JVM 系统属性激活
java -Dspring.profiles.active=prod -jar myapp.jar

四、@Value 与 @ConfigurationProperties

@Value 注入单个配置值

@Service
public class AppConfig {
    @Value("${app.name:default-app}")
    private String appName;
    
    @Value("${app.timeout:30}")
    private int timeout;
    
    @Value("${app.features.enabled:false}")
    private boolean featureEnabled;
}

@Value 支持 SpEL 表达式,但只能注入简单类型。它会在 Spring 容器初始化时处理,如果配置缺失且没有默认值,启动时会抛出异常。

@Value("#{${app.ports} ?: '{8080,8081}'}")
private int[] ports;

@ConfigurationProperties 批量绑定

@Component
@ConfigurationProperties(prefix = "app")
@Data
public class AppProperties {
    private String name;
    private int timeout;
    private List<String> servers;
    private Map<String, String> features;
    private Security security = new Security();
    
    @Data
    public static class Security {
        private boolean enabled;
        private String secret;
    }
}

对应的配置:

app:
  name: my-app
  timeout: 30
  servers:
    - server1.example.com
    - server2.example.com
  features:
    cache: true
    logging: false
  security:
    enabled: true
    secret: xyz123

@ConfigurationProperties 适合绑定一组相关配置,支持复杂类型(List、Map、嵌套对象),适合批量管理配置项。@Value 适合绑定单个配置值,适合只在少数地方使用的配置。

验证配置

配合 @Validated 进行配置校验:

@Component
@ConfigurationProperties(prefix = "app")
@Validated
@Data
public class AppProperties {
    @NotBlank
    private String name;
    
    @Min(1)
    @Max(60)
    private int timeout;
}

五、配置文件的位置

Spring Boot 会按以下顺序查找 application.yml 或 application.properties:

  1. 项目根目录下的 config 子目录(/config/)
  2. 项目根目录
  3. classpath 下的 config 包(classpath:/config/)
  4. classpath 根目录

外置配置文件优先级更高,适合生产环境覆盖配置。

# 外置配置文件
/opt/app/config/application.yml

# 默认 classpath 配置文件
src/main/resources/application.yml

可以显式指定配置文件位置:

java -jar myapp.jar --spring.config.location=/opt/config/application.yml

spring.config.location 会覆盖默认路径,spring.config.additional-location 会在默认路径基础上添加额外路径。

六、随机值生成

Spring Boot 支持使用 RandomValuePropertySource 生成随机值:

app:
  secret: ${random.value}
  uuid: ${random.uuid}
  number: ${random.int(100)}
  range: ${random.int(10,20)}
  long: ${random.long}
@Value("${app.secret}")
private String secret;

@Value("${app.uuid}")
private String uuid;

七、自定义配置文件

@PropertySource

加载自定义配置文件:

@Configuration
@PropertySource("classpath:custom.properties")
@PropertySource("file:/opt/config/custom.properties")
public class CustomConfig {
    @Value("${custom.value}")
    private String customValue;
}

自定义 PropertySource

在代码中添加配置来源:

@Component
public class CustomPropertySourceConfig {
    @Bean
    public PropertySource<?> customPropertySource() {
        Map<String, Object> map = new HashMap<>();
        map.put("custom.key", "custom-value");
        return new MapPropertySource("custom", map);
    }
}

八、类型安全配置提示

为自定义配置类添加元数据,让 IDE 提供自动补全提示:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

编译后会生成 META-INF/spring-configuration-metadata.json,IDE 会识别并提供配置提示。

@Component
@ConfigurationProperties(prefix = "app")
public class AppProperties {
    /**
     * Application name
     */
    private String name;
    
    /**
     * Connection timeout in seconds
     */
    private int timeout = 30;
}

九、敏感信息处理

生产环境中,数据库密码等敏感信息不应硬编码在配置文件中。

使用环境变量

spring:
  datasource:
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}

启动时通过环境变量传入:

export DB_USERNAME=root
export DB_PASSWORD=secret
java -jar myapp.jar

使用加密配置

配合 Spring Cloud Config 或 Jasypt 进行配置加密。

Jasypt 示例:

<dependency>
    <groupId>com.github.ulisesbocchio</groupId>
    <artifactId>jasypt-spring-boot-starter</artifactId>
    <version>3.0.5</version>
</dependency>
spring:
  datasource:
    password: ENC(encrypted-password-value)

十、常见问题排查

配置不生效

检查配置来源的优先级顺序,高优先级的配置会覆盖低优先级。命令行参数 > 环境变量 > JVM 系统属性 > 外部配置文件 > 内部配置文件 > 默认值。

加载顺序确认

在启动类中添加日志打印:

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
        ConfigurableEnvironment environment = context.getEnvironment();
        // 打印所有配置属性
        System.out.println(environment.getProperty("app.name"));
    }
}

属性来源查看

启用 debug 模式查看配置加载详情:

debug: true

或启动时添加 --debug 参数,Spring Boot 会输出条件评估报告,显示每个自动配置类是否生效以及原因。

十一、总结

外部化配置是 Spring Boot 支持多环境部署的基础能力。掌握配置的优先级顺序、Profile 机制和配置绑定方式,就能灵活管理不同环境的配置,保证应用在不同部署环境中正确运行。生产环境中结合环境变量和加密方案管理敏感信息,避免将敏感信息泄露到代码仓库中。