You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot中如何在初始化时验证并转换ConfigurationProperties值?

解决方案:配置属性的ISO8601格式验证与自动转换

核心说明

首先需要明确:ISO8601周期分为两类——Duration(时间量,格式以PT开头,如PT1H代表1小时)和Period(日期量,格式以P开头,如P1M代表1个月)。Duration.parse()不支持P1M这类可变长度的日期单位(月/年的时长不固定),如果你的配置是P1M,建议改用Period类型再按需转换为Duration;如果是时间量需求,直接用PT开头的格式即可。


方案1:利用Spring Boot自动类型转换(最简洁)

直接将属性类型改为Duration,Spring Boot会自动完成字符串到Duration的转换,格式错误时会在应用启动阶段抛出IllegalArgumentException,自动完成验证。

@ConfigurationProperties(prefix = "x.retention")
@Slf4j
@ConstructorBinding
@AllNonNullByDefault
@Validated
public class RetentionProperties {

  private final Duration retentionDuration;

  // 构造函数接收Duration类型,Spring自动绑定配置文件中的ISO8601字符串
  public RetentionProperties(Duration retentionDuration) {
    this.retentionDuration = retentionDuration;
    log.info("Retention duration initialized: {}", retentionDuration);
  }

  public Duration getRetentionDuration() {
    return retentionDuration;
  }
}

配置示例(application.yml):

x:
  retention:
    retention-duration: PT1H

方案2:构造函数手动解析+启动时验证

如果需要保留原字符串属性,同时解析为Duration,可以在构造函数中手动处理,解析失败直接抛出异常终止启动:

@ConfigurationProperties(prefix = "x.retention")
@Slf4j
@ConstructorBinding
@AllNonNullByDefault
@Validated
public class RetentionProperties {

  private final String isoPeriod;
  private final Duration duration;

  public RetentionProperties(String isoPeriod) {
    this.isoPeriod = isoPeriod;
    try {
      this.duration = Duration.parse(isoPeriod);
      log.info("Successfully parsed isoPeriod [{}] to Duration [{}]", isoPeriod, duration);
    } catch (IllegalArgumentException e) {
      log.error("Invalid ISO8601 duration format: {}", isoPeriod, e);
      throw new IllegalArgumentException("x.retention.iso-period must be a valid ISO8601 duration (e.g. PT1H)", e);
    }
  }

  public String getIsoPeriod() {
    return isoPeriod;
  }

  public Duration getDuration() {
    return duration;
  }
}

方案3:自定义JSR-380验证注解(贴合@Validated规范)

如果需要更规范的验证逻辑,可以自定义验证注解,配合@Validated完成格式校验:

  1. 自定义验证注解
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = IsoDurationValidator.class)
public @interface ValidIsoDuration {
  String message() default "Invalid ISO8601 duration format (required: PTxxH/PTxxM etc.)";
  Class<?>[] groups() default {};
  Class<? extends Payload>[] payload() default {};
}
  1. 实现验证器
public class IsoDurationValidator implements ConstraintValidator<ValidIsoDuration, String> {
  @Override
  public boolean isValid(String value, ConstraintValidatorContext context) {
    try {
      Duration.parse(value);
      return true;
    } catch (IllegalArgumentException e) {
      return false;
    }
  }
}
  1. 修改配置类
@ConfigurationProperties(prefix = "x.retention")
@Slf4j
@ConstructorBinding
@AllNonNullByDefault
@Validated
public class RetentionProperties {

  @ValidIsoDuration
  private final String isoPeriod;
  private final Duration duration;

  public RetentionProperties(@ValidIsoDuration String isoPeriod) {
    this.isoPeriod = isoPeriod;
    // 已通过验证,可安全解析
    this.duration = Duration.parse(isoPeriod);
    log.info("Retention initialized with duration: {}", duration);
  }

  public String getIsoPeriod() {
    return isoPeriod;
  }

  public Duration getDuration() {
    return duration;
  }
}

针对P1M这类Period格式的处理

如果你的配置是P1M(1个月)这类日期周期,需要改用Period类型,再按需转换为Duration(注意:月的时长不固定,转换会基于当前日期计算近似值):

@ConfigurationProperties(prefix = "x.retention")
@Slf4j
@ConstructorBinding
@AllNonNullByDefault
@Validated
public class RetentionProperties {

  private final Period retentionPeriod;

  public RetentionProperties(Period retentionPeriod) {
    this.retentionPeriod = retentionPeriod;
    log.info("Retention period initialized: {}", retentionPeriod);
  }

  // 基于当前时间转换为Duration
  public Duration toDuration() {
    return Duration.between(LocalDateTime.now(), LocalDateTime.now().plus(retentionPeriod));
  }

  public Period getRetentionPeriod() {
    return retentionPeriod;
  }
}

配置示例:

x:
  retention:
    retention-period: P1M

内容的提问来源于stack exchange,提问作者chujudzvin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 07:28:15