The IntelliJ IDEA Blog

Spring Boot Configuration Management Best Practices

8.5内容质量

TL;DR · AI 摘要

Spring Boot配置管理应分类存储,使用@ConfigurationProperties绑定属性,敏感信息需通过专用系统管理。

核心要点

  • 配置分三类:应用默认值、部署配置、敏感信息,分别存储于不同位置。
  • @ConfigurationProperties可集中管理相关属性,提升可维护性与验证能力。
  • 敏感信息必须通过专用密钥管理系统提供,禁止硬编码在代码中。

结构提纲

按章节快速跳转。

  1. Spring Boot支持多环境配置,需遵循最佳实践确保配置安全与可维护性。

  2. 应用默认值、部署配置和敏感信息应分层管理,避免硬编码。

  3. 通过@ConfigurationProperties绑定属性,提升配置验证与重构效率。

  4. 启动时验证必填配置存在性,防止缺失导致应用失败。

思维导图

用一张图看清主题之间的关系。

查看大纲文本(无障碍 / 无 JS 友好)
  • Spring Boot配置管理
    • 配置分类
      • 应用默认值
      • 部署配置
      • 敏感信息
    • 最佳实践
      • @ConfigurationProperties绑定
      • 启动时验证配置
    • 安全建议
      • 专用密钥管理系统

金句 / Highlights

值得收藏与分享的关键句。

#Spring Boot#Java#配置管理#最佳实践
打开原文

Spring Boot 配置管理最佳实践 - JetBrains 博客

IntelliJ IDEA

IntelliJ IDEA – Java 和 Kotlin 专业开发的领先 IDE

关注

  • 关注:
  • Linkedin Linkedin
  • Bluesky Bluesky
  • X X
  • Facebook Facebook
  • Youtube Youtube
  • RSS RSS

下载

IntelliJ IDEA

Java

Spring Boot 配置管理最佳实践

Siva Katamreddy

Spring Boot 提供了全面的外部化应用配置支持。它通过从各种来源(如)提供值,使一个应用构件能够在不同环境中运行:

  • 属性文件
  • 环境变量
  • 系统属性
  • 命令行参数

在本文中,我们将探讨管理 Spring Boot 应用配置的最佳实践。一个设计良好的配置策略应确保:

  • 配置与应用代码分离。
  • 当所需配置缺失或无效时,应用无法启动。
  • 每个部署环境都可以覆盖默认值。
  • 敏感值通过专用的密钥管理系统提供。

配置属性分类

通常,Spring Boot 应用配置可分为三类:

  • 应用默认值:安全且非敏感的值,如第三方服务 URL、超时时间和重试限制。将这些与应用一起存储。
  • 部署配置:标识环境的值,如数据库主机、队列名称和外部服务 URL。通过部署平台提供这些配置。
  • 密钥:密码、API 密钥、证书和私钥。在专用的密钥系统中存储这些信息。

例如,application.properties 可以提供应用默认配置属性:

code
app.promotion-service.base-url=http://localhost:8181
app.promotion-service.timeout=3s
app.promotion-service.retries=3
logging.level.com.jetbrains=DEBUG
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.open-in-view=false

默认值应在所有可能使用的环境中都是安全的。数据库 URL 和凭证等属性绝不要在应用代码中硬编码。如果某个必需值没有安全的默认值,请在启动时验证其存在性。

使用 @ConfigurationProperties 绑定应用属性

Spring 应用可以通过 Environment、@Value 或 @ConfigurationProperties 访问配置值。

当需要动态解析属性名称或基础设施代码需要直接访问属性源时,使用 Environment。

对于独立值,使用 @Value:

code
PromotionService(
  @Value("${app.promotion-service.base-url}") String baseUrl,
  @Value("${app.promotion-service.timeout}") Duration timeout,
  @Value("${app.promotion-service.retries}") int retries) {
    this.baseUrl = baseUrl;
    this.timeout = timeout;
    this.retries = retries;
}

分散的 @Value 表达式会使属性名称难以发现、验证和重构。使用 @ConfigurationProperties 定义的专用配置类型支持所有这些功能。

对于相关配置属性,优先使用 @ConfigurationProperties。它提供:

  • 类型安全的绑定和转换
  • 属性名称与 Java 成员之间的宽松绑定
  • 组级别的验证
  • 通过生成的元数据实现 IDE 补全和导航

例如,如果我们正在与第三方 REST API 进行集成,可能需要配置服务基础 URL、超时时间和重试次数。

code
app.promotion-service.base-url=${PROMOTION_SERVICE_URL}
app.promotion-service.timeout=${PROMOTION_SERVICE_TIMEOUT:3s}
app.promotion-service.retries=3

在上述配置中,我们从环境变量 PROMOTION_SERVICE_URL 获取 base-url 的值,并从 PROMOTION_SERVICE_TIMEOUT 环境变量(默认值为 3 秒)获取 timeout 的值。

Spring Boot 支持基于 setter 的绑定。你可以通过以下方式将属性绑定到使用 setter 的类:

code
@ConfigurationProperties(prefix = "app.promotion-service")
public class PromotionSvcProperties {

    private String baseUrl;
    private Duration timeout;
    private int retries;

    // Setters and getters
}

使用 @ConfigurationPropertiesScan 注册配置类型:

code
@SpringBootApplication
@ConfigurationPropertiesScan
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

@ConfigurationPropertiesScan 注解会扫描带有 @ConfigurationProperties 注解的组件,并将其注册为 Spring Bean。

现在我们可以将 PromotionSvcProperties 注入到其他 Spring Bean 中,并访问属性值。

优先使用 Record 进行 @ConfigurationProperties 绑定

通常,配置在应用启动时建立,并在整个应用生命周期内保持不变。

对于大多数应用配置,Java Record 是首选方案。它开箱即提供不可变性,即使在基于类的绑定中意外调用 setter 方法修改值的情况下,也能避免这种错误:

code
@ConfigurationProperties(prefix = "app.promotion-service")
public record PromotionSvcProperties(
        String baseUrl,
        Duration timeout,
        int retries) {
}

Spring Boot 的宽松绑定会将规范的 kebab-case 名称(如 base-url)映射到 baseUrl 字段。

有时我们可能需要将属性绑定到第三方库提供的 Bean,而无法修改其源代码以添加 @ConfigurationProperties 注解。

要直接将配置属性绑定到第三方类,可以将其声明为 @Bean,并在 Bean 方法上添加 @ConfigurationProperties 注解:

code
@Configuration
public class ClientConfiguration {

    @Bean
    @ConfigurationProperties(prefix = "third-party.client")
    public ThirdPartyClientProperties clientProperties() {
        return new ThirdPartyClientProperties();
    }
}

可以按如下方式配置 third-party.client 属性:

code
third-party.client.base-url=https://api.example.com
third-party.client.connect-timeout=5s
third-party.client.read-timeout=30s

如果第三方类是不可变的或不支持 setter 绑定,请创建自己的属性类,并使用它来构造第三方对象:

code
@ConfigurationProperties(prefix = "third-party.client")
public record ClientProperties(
    URI baseUrl,
    Duration connectTimeout,
    Duration readTimeout
) {}

@Configuration
@EnableConfigurationProperties(ClientProperties.class)
class ClientConfiguration {
code
@Bean
ThirdPartyClient thirdPartyClient(ClientProperties properties) {
    return new ThirdPartyClient(
            properties.baseUrl(),
            properties.connectTimeout(),
            properties.readTimeout()
    );
}

包装器方法通常更可取,因为它可以避免将应用程序配置直接耦合到第三方库的类结构中。

快速失败,尽早失败:启动时验证配置

应在应用启动时检测配置错误,如果配置缺失或无效则应快速失败。将 @Validated 注解添加到 @ConfigurationProperties Bean 上,并对其属性应用 Jakarta Bean Validation 约束。

code
@Validated
@ConfigurationProperties(prefix = "app.promotion-service")
public record PromotionSvcProperties(
 @NotBlank String baseUrl,
        @NotNull Duration timeout,
        @Min(1) @Max(5) int retries,
        @NotNull @Valid SyncProperties sync) {

    public record SyncProperties(@NotEmpty String cron) {
    }
}

当类路径中包含 spring-boot-starter-validation 时,绑定或验证失败会阻止应用启动。验证必需值、数值范围、嵌套组和其他应用级约束。

当需要区分缺失值与 Java 默认值时,应使用包装类型。例如,用 @NotNull 注解的 Integer 可以标识缺失值,而 int 默认值为 0。

还可以使用 @DefaultValue 指定默认值,如下所示:

code
@Validated
@ConfigurationProperties(prefix = "app.promotion-service")
public record PromotionSvcProperties(
 @NotBlank String baseUrl,
        @NotNull Duration timeout,
        @Min(1) @Max(5) @DefaultValue("3") Integer retries,
        @NotNull @Valid SyncProperties sync) {

    public record SyncProperties(@DefaultValue("0 0 * * * *") String cron) {
    }
}

在上述示例中,我们使用 @DefaultValueretriescron 属性指定了默认值,如果未配置这些属性值,将使用默认值。

理解属性优先级

Spring Boot 会合并多个属性源。当同一属性出现在多个源中时,优先级更高的源会提供有效值。

以下简化顺序展示了应用部署中最常用的属性源,按优先级从低到高排列:

code
application.properties/yaml (低优先级)
           ↓
特定配置文件
           ↓
操作系统环境变量
           ↓
Java 系统属性
           ↓
命令行参数    (高优先级)

当排查与预期配置值不一致的值时,Spring Boot 的配置加载优先级非常重要。

环境变量被操作系统、容器运行时和云平台广泛支持。Spring Boot 通过将规范属性名中的点替换为下划线、删除连字符并转换为大写,从规范属性名派生环境变量名:

code
app.payment-timeout     -> APP_PAYMENT_TIMEOUT
spring.datasource.url   -> SPRING_DATASOURCE_URL

确定属性的有效值可能具有挑战性,尤其当它在多个配置源中定义时。IntelliJ IDEA 可以通过编辑器内联提示显示解析后的配置值。选择提示可识别提供值的属性源,并指示该值是否被其他源(如环境变量或系统属性)覆盖。

IntelliJ IDEA 还支持在属性声明、@ConfigurationProperties 成员和属性使用之间进行导航。对于自定义配置属性,spring-boot-configuration-processor 生成的元数据会增强此功能。

在专用系统中存储密钥

不要在源代码控制中存储密码、API 密钥、证书或私钥。使用 HashiCorp Vault、AWS Secrets Manager、Google Cloud Secret Manager、Azure Key Vault 或等效平台服务等系统。

确保密钥不会包含在日志、错误信息、配置元数据和公开可访问的管理端点中。

注意:在非生产环境中,Actuator env 端点可以帮助识别有效属性的来源。由于配置可能包含敏感信息,因此不应公开暴露该端点。

推荐的配置管理

没有一种配置管理方法适用于所有应用程序。应根据应用程序的架构、部署环境和复杂性选择策略。

单体应用

对于单体应用,应在应用中保留共享默认值,仅在必要时使用特定配置文件,并通过环境变量提供部署特定的覆盖值。将敏感值存储在专用的密钥管理系统中。

容器化工作负载

对于在 Kubernetes 等容器平台运行的工作负载,应在应用中保留合理的默认值,并通过 ConfigMaps 提供部署特定的配置。将密钥单独存储在专用的密钥管理系统中。

微服务

对于微服务架构,可考虑使用 Spring Cloud Config Server 集中管理配置、治理和版本控制。继续通过专用的密钥管理系统管理密钥。

总结

有效的应用配置应从合理的默认值、类型安全的 @ConfigurationProperties 和启动验证开始。将环境特定值保留在应用之外,理解属性源优先级,并将密钥存储在专用的密钥管理系统中。

正确的配置策略应反映应用的架构和部署环境。

在本地开发和远程调试期间,IntelliJ IDEA 通过显示解析后的属性值及其来源、突出显示覆盖项,并提供在配置文件和绑定 Java 属性之间的导航,帮助揭示有效配置。

最佳实践

Spring Boot

  • 分享
  • Facebook
  • Twitter
  • LinkedIn

上一篇帖子

如何在 IntelliJ IDEA 中使用 ACP 的 AI 代理