When using nested classes in @ConfigurationProperties
annotated classes, it's normally possible to override that value by passing a system property.
Now with 3.5.0-RC1 it does not work anymore.
System properties for the non-nested properties are being picked up normally, the issue seems to be only with nested properties.
@Getter
@Setter
@ConfigurationProperties(prefix="demo.greeting")
public class GreetingProperties {
private String message = "Default Message";
private GreetingDetailsProperties details = new GreetingDetailsProperties();
@Setter
@Getter
static class GreetingDetailsProperties {
String salutation = "Default Salutation";
}
}
When the system property demo.greeting.details.salutation
is passed with some custom value, it's not being picked up and the "salutation" will always be "Default Salutation".
A small and isolated reproduction case can be found in this repository: https://github.com/coduinix/gh-45639-repro-configuration-properties-issue
Comment From: coduinix
Looking at the git history, it might be related to commit d9d206a
Comment From: wilkinsona
Thanks for the report and the reproducer. Interestingly the problem only occurs in servlet-based web applications. WebFlux and non-web apps are not affected.
Comment From: nosan
With the changes from https://github.com/spring-projects/spring-boot/compare/main...nosan:spring-boot:45639 applied, everything appears to be working correctly, but honestly, I am not sure; this area is quite complex.
Comment From: philwebb
Thanks for looking @nosan, that helped me track down the problem. I've gone for a slightly different fix because I remember that I was worried we shouldn't reuse previously cached descendants.
Comment From: nosan
Happy to help, @philwebb!
I've gone for a slightly different fix...
//recalculation
for (String propertyName : propertyNames) {
addParents(descendants, reverseMappings.get(propertyName));
}
This seems equivalent to:
Set<ConfigurationPropertyName> descendants = (!this.captureDescendants) ? null : cloneOrCreate((data != null) ? data.descendants() : null, size);
At least, I couldn't spot any meaningful difference between the two.
Did I overlook something?
Comment From: philwebb
I think the data.descendants()
value might have stale entries in it, ones that were previously in the property source but have since been removed. Using for (String propertyName : propertyNames)
should (I hope) mean we get only descendants for the current property names.