spring-boot: Declared defaults do not get property references replace
Assume you declare a configuration properties class like this:
@ConstructorBinding
@ConfigurationProperties("sample")
class SampleProperties {
SampleProperties(@DefaultValue("${user.home}/foo") String location) { … }
}
If the application now configures the property sample.location
to ${user.home}/bar
, the constructor is called with /Users/…/bar
, i.e. the dynamic reference to the user home folder has been replaced with the actual value. If the application does not configure an explicit value, the value handed into constructor is literally ${user.home}/foo
, i.e. the reference to the user’s home directory is not resolved.
About this issue
- Original URL
- State: open
- Created 2 years ago
- Comments: 15 (15 by maintainers)
We talked about this and concluded that we’d like to align the behaviour of
@DefaultValue
with what would happen if you had written the same value inapplication .properties
orapplication.YAML
. You get conversion at the moment (so, for example,"3w"
for aDuration
would become aDuration
of 3 weeks), but you don’t get placeholder replacement. For consistency, we’d like to get both.We have some concerns about backward-compatibility so it’ll have to wait for 3.x. We’ve reopened #23164 to improve the docs in the meantime.
As things stand, the desired behaviour can be achieved like this with setter-based binding:
The same can be achieved with immutable configuration properties like this:
The javadoc for
@DefaultValue
states that it “can be used to specify the default value when binding to an immutable property”. That’s not really accurate when you’re trying to turnSampleMutableProperties
into something immutable. You have to perform the defaulting manually in the constructor as shown above instead.Knowing that you’ve only got a string to work with, trying to use
@DefaultValue("${user.home}/example")
(particularly if you’re already familiar with Spring) seems quite logical to me but it won’t work as hoped as${user.home}
will be left as-is. If it worked as @odrotbohm expected and performed property placeholder resolution it would be more concise and also have the benefit over the field initializer of being able to document a sensible default value for the property automatically.That’s a bit inconvenient if the property is part of a library. Isn’t the point of a default value, that the user doesn’t have to provide it in the first place?