With immutable configuration properties as described at https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-external-config-constructor-binding Optional
doesn't work as expected.
Using Spring Boot 2.3.0:
@ConfigurationProperties(prefix = "monitor")
@ConstructorBinding
public class MonitorConfiguration {
private final String environmentName;
private final Optional<String> managerUrl;
public MonitorConfiguration(final String environmentName,
final Optional<String> managerUrl) {
this.environmentName = environmentName;
this.managerUrl = managerUrl;
}
}
Using application.properties
:
monitor.environment-name=test
Expected:
monitorConfiguration.managerUrl
should be Optional.EMPTY
This behavior would match the non-immutable ConfigurationProperties behavior described in #5110
Actual:
monitorConfiguration.managerUrl
is null
Note that https://github.com/spring-projects/spring-boot/issues/18917 says this issue should have already been fixed in Spring Boot 2.3.0, but that's not what I'm seeing.
If I add @DefaultValue
to the Optional<String> managerUrl
parameter in the contructor, then I get the expected behavior - but IMHO, the @DefaultValue
should not be necessary.
Comment From: philwebb
The current behavior of injecting null
for the optional unless @DefaultValue
is used is consistent with other parameter types. Although it's highly unlikely that you want a null
Optional
, we think that it's best that we keep things consistent.
The use of Optional
in both fields and constructor arguments isn't something we recommend. Although it works, we generally think it's a bit of an anti-pattern. I've opened #21868 so that we can add a note about this to the reference documentation.
Comment From: dreis2211
I think IntelliJ even warns you about Optional
fields. It's considered an antipattern by many and wasn't the design goal. Brian Goetz once stated:
Of course, people will do what they want. But we did have a clear intention when adding this feature, and it was not to be a general purpose Maybe or Some type, as much as many people would have liked us to do so. Our intention was to provide a limited mechanism for library method return types where there needed to be a clear way to represent "no result", and using null for such was overwhelmingly likely to cause errors.
So it's mainly about having a more expressive way for returns with "no result". Since Optional isn't Serializable there is also the problem that it shouldn't be used in bean like classes.
Comment From: dreis2211
https://www.youtube.com/watch?v=Ej0sss6cq14 is a good talk by Stuart Marks about Optional in general. Particularly, the sections starting at minutes 40:40 & 54:03