Reputation: 1697
Using spring-boot v3.2.5 I've got configuration that deserializes to this class:
@Getter
// Intentionally Validated and not jakarata.validation.Valid because Valid does not trigger
// validation for configuration properties, and I don't know why.
@Validated
@ConfigurationProperties(prefix = "pfx")
public class TenantsConfiguration {
@NotNull private final Map<String, TenantConfiguration> tenants;
@ConstructorBinding
public TenantsConfiguration(Map<String, TenantConfiguration> tenants) {
this.tenants = tenants;
}
@Getter
public static class TenantConfiguration {
@NotNull private final Optional<String> field1;
@NotNull private final Optional<String> field2;
@ConstructorBinding
public TenantConfiguration(
Optional<String> field1, Optional<String> field2) {
this.field1 = field1;
this.field2 = field2;
}
}
}
And a config that looks like:
pfx:
tenants:
t1:
field1: abc
The application fails to start because field2
is null instead of Optional.empty()
:
Binding to target TenantsConfiguration failed:
Property: pfx.tenants.t1.field2
Value: "null"
Reason: field2 must not be null
During deserialization from api requests, spring handles null -> Optional conversion just fine. I tried converting TenantConfiguration to a record, but that did not change anything. How do I get the normal null -> Optional conversion when I'm loading configuration in spring-boot?
Upvotes: 1
Views: 152
Reputation: 177
Optional is usually not recommended for ConfigurationProperties. This is a known behaviour which spring-boot marked as 'not a bug, but a feature'.
Source : https://github.com/spring-projects/spring-boot/issues/21868
Checkout these comments specifically :
So a work around for your problem would be :
@Getter
public static class TenantConfiguration {
@NotNull private Optional<String> field1;
@NotNull private Optional<String> field2;
@ConstructorBinding
public TenantConfiguration(String field1, String field2) {
this.field1 = Optional.ofNullable(field1);
this.field2 = Optional.ofNullable(field2);
}
}
Upvotes: 4