Simple property values using same class more than once in their path seems ignored with @ConfigurationProperties.
@ConfigurationProperties("innerobjectproperties")
public class InnerObjectProperties {
private InnerObject innerobject;
// getter / setter
public static class InnerObject {
private InnerObject innerobject;
private String stringvalue;
// getter / setter
}
}
innerobjectproperties.innerobject.stringvalue=n1
innerobjectproperties.innerobject.innerobject.stringvalue=n2
Result :
- innerObjectProperties.getInnerobject().getStringvalue() = "n1"
- innerObjectProperties.getInnerobject().getInnerobject() = null but shouldn't
The similar use case using collection (not simple properties) doesn't have this issue.
See tests in attached demo.zip
spring-boot version : 2.7.5 / OpenJDK 64-Bit Server VM Temurin-17.0.3+7
Comment From: wilkinsona
This is intentional to prevent infinite recursion when performing Java bean binding with a property source that isn't enumerable. A non-enumerable property source means that the binder has to try to bind every property rather than being able to stop when it knows that no properties are available. With InnerObject nested within itself, the properties to be bound are never ending and, if the prevention was not in place, binding would never stop or would fail once the stack is exhausted.
Comment From: christophejan
Hi @wilkinsona,
Thank you very much for your answer.
I'm really sorry but I don't really get the point...
The similar use case with a collection work as I was initially expecting (see below). Does it mean that, with a collection, the property source become enumerable ? The recursion in the structure seems quite similar to me and I don't get the reason why both use cases can't be proceed the same way... With non collection properties, is there really no way to keep some kind of reference on classes already "proceed" to avoid infinite recursion ?
Please excuse me to bother you with that...
@ConfigurationProperties("innerlistproperties")
public class InnerListProperties {
private List<InnerList> innerlist;
// getter / setter
public static class InnerList {
private List<InnerList> innerlist;
private String stringvalue;
// getter / setter
}
}
innerlistproperties.innerlist[0].stringvalue=n1
innerlistproperties.innerlist[0].innerlist[0].stringvalue=n2
innerlistproperties.innerlist[0].innerlist[0].innerlist[0].stringvalue=n3
Result :
- innerListProperties.getInnerlist().getStringvalue() = "n1"
- innerListProperties.getInnerlist().getInnerlist().getStringvalue() = "n2"
- innerListProperties.getInnerlist().getInnerlist().getInnerlist().getStringvalue() = "n3"
Comment From: wilkinsona
Things are a little different with Lists. Once a property has been found in a particular PropertySource entries in the list are only bound using that specific source. If this source is enumerable, this allows us to relax the restrictions on recursive binding. When binding straight to an object, each property can be bound from any property source which requires more strict prevention of recursion.
With non collection properties, is there really no way to keep some kind of reference on classes already "proceed" to avoid infinite recursion ?
This already happens and is why innerObjectProperties.getInnerobject().getInnerobject() is null. The previous binding of innerObjectProperties.getInnerobject() has prevented the nested inner object from being bound.
Comment From: christophejan
@wilkinsona
Thank you for the explanation ! Much clearer to me now ! I'm sorry for the waste of time.