Spring 当 application.properties 中没有相应的嵌套 key/value 对时,启动初始化嵌套配置绑定的新实例

Spring Boot initialize new instance for nested configuration binding when there are no corresponding nested key/value pairs in application.properties

我有如下配置 class。内部 class OptionalServiceConfigs 中的所有字段都有一个默认值,使用 @Value 注释,如下所示。

有时在我的 application.properties 文件中,它 没有 一个 service 前缀 属性。在这种情况下,我们希望加载一个 OptionalServiceConfigs 实例及其默认字段值。

@Configuration
@ConfigurationProperties(prefix = "myconf")
public class MyConfigs {

   // ... rest of my configs

   @Value("${service:?????}") // what to put here, or can I?
   private OptionalServiceConfigs service;   // this is null

   // In this class all fields have a default value.
   public static class OptionalServiceConfigs {

       @Value("${mode:local}")
       private String mode;

       @Value("${timeout:30000}")
       private long timeout;

       // ... rest of getter and setters
   }

   // ... rest of getter and setters
}

但不幸的是,service 字段在使用其 getter 方法访问时是 null。因为 spring boot 不会 在我的 application.properties 文件中找不到带有前缀 myconf.service.* 的 属性 键时初始化它的实例.

问题:

当属性文件中没有相应的前缀键时,如何使 service 字段连同其指定的默认字段值初始化为新实例?

我无法想象在 service 字段的注释 @Value("${service:?????}") 中放置一个值。 没有任何效果,试过,@Value("${service:}")@Value("${service:new")

你可以试试这样的结构,对我来说很管用:

@Data
@Validated
@ConfigurationProperties(prefix = "gateway.auth")
@Configuration
public class AuthProperties {

@NotNull
private URL apiUrl;

@Valid
@NotNull
private Authentication authentication;

@Data
public static class Authentication {
    @NotNull
    private Duration accessTokenTtl;
    @NotNull
    private String accessTokenUri;
    @NotNull
    private String clientId;
    @NotNull
    private String clientSecret;
    @NotNull
    private String username;
    @NotNull
    private String password;
    @Min(0)
    @NonNull
    private Integer retries = 0;
   }
}

重要的是要有 getter 和 setter 才能使 Spring 对 ConfigurationProperties 进行后处理,为此我正在使用 Lombok (@Data)。

详情请看这里:

Baeldung ConfigurationProperties Tutorial

根据@M. Deinum的建议,对配置做了一些修改class。我是 Spring 的新手,我似乎误解了 Spring 在幕后的工作方式。

  1. 首先,我删除了内部 class(即 OptionalServiceConfigs)中的所有 @Value 注释,以及 MyConfigs [=50 中的 service 字段=].
  2. 然后,使用内联的默认值初始化所有内部 class 字段。
  3. MyConfigs的构造函数中,我为字段service初始化了一个OptionalServiceConfigs的新实例。

通过这样做,每当我的 application.properties 中有 no service 相关键时,已经使用默认值创建了一个新实例。

当有 is/are service 相关 key/s 时,那么 Spring 确实会将我的默认值覆盖为 application.properties 中的指定值仅 我指定的字段。

我认为从 Spring 的角度来看,当 none 时,它无法提前知道引用字段(即 service 字段)与配置相关它的密钥存在于配置文件中。这一定是 Spring 不初始化它的原因。很公平。

完整解决方案:

@Configuration
@ConfigurationProperties(prefix = "myconf")
public class MyConfigs {

   // ... rest of my configs

   private OptionalServiceConfigs service;

   public static class OptionalServiceConfigs {

       private String mode = "local";

       private long timeout = 30000L;

       // ... rest of getter and setters
   }

   public MyConfigs() {
      service = new OptionalServiceConfigs();
   }

   // ... rest of getter and setters
}