Overview
In summary, the implementation of the placeholder resolution algorithm in PropertySourcesPlaceholderConfigurer fails in several scenarios, and the root cause for this category of failures has existed since PropertySourcesPlaceholderConfigurer was introduced in Spring Framework 3.1.
|
if (this.environment != null) { |
|
this.propertySources.addLast( |
|
new PropertySource<Environment>(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, this.environment) { |
|
@Override |
|
public String getProperty(String key) { |
|
return this.source.getProperty(key); |
|
} |
|
} |
|
); |
|
} |
In the original code above, we see that a PropertySource is implemented as an anonymous inner class which delegates to Environment#getProperty, which in turn delegates to an internal PropertySourcesPropertyResolver, which in turn performs placeholder resolution.
And... that anonymous PropertySource is added to the MutablePropertySources managed by PropertySourcesPlaceholderConfigurer, which wraps those MutablePropertySources in its own PropertySourcesPropertyResolver, which in turn performs placeholder resolution.
Thus, we effectively have always had a top-level PropertySourcesPropertyResolver that indirectly delegates to a nested PropertySourcesPropertyResolver, which results in double placeholder parsing and resolution.
And that is what leads to a whole category of bugs.
In #27947, I realized that we were lacking proper support for ignoreUnresolvablePlaceholders in the nested PropertySourcesPropertyResolver, and due to #34315 and #34326 it became apparent that we are currently (as of Spring Framework 6.2.6) lacking support for the configured escapeCharacter in the nested PropertySourcesPropertyResolver as well.
Analysis
My research into #34315 and #34326 led me to realize that the reported bug for escaped placeholders being evaluated despite the escaping was in fact due to the same underlying flaw in the core logic of PropertySourcesPlaceholderConfigurer.
Namely, we should never have indirectly used or directly created a nested PropertySourcesPropertyResolver.
Instead, properties from property sources from the Environment should be accessed directly without duplicate/nested placeholder resolution, since the top-level PropertySourcesPropertyResolver already handles placeholder resolution.
Related Issues
Overview
In summary, the implementation of the placeholder resolution algorithm in
PropertySourcesPlaceholderConfigurerfails in several scenarios, and the root cause for this category of failures has existed sincePropertySourcesPlaceholderConfigurerwas introduced in Spring Framework 3.1.spring-framework/org.springframework.context/src/main/java/org/springframework/context/support/PropertySourcesPlaceholderConfigurer.java
Lines 126 to 135 in 7a7df66
In the original code above, we see that a
PropertySourceis implemented as an anonymous inner class which delegates toEnvironment#getProperty, which in turn delegates to an internalPropertySourcesPropertyResolver, which in turn performs placeholder resolution.And... that anonymous
PropertySourceis added to theMutablePropertySourcesmanaged byPropertySourcesPlaceholderConfigurer, which wraps thoseMutablePropertySourcesin its ownPropertySourcesPropertyResolver, which in turn performs placeholder resolution.Thus, we effectively have always had a top-level
PropertySourcesPropertyResolverthat indirectly delegates to a nestedPropertySourcesPropertyResolver, which results in double placeholder parsing and resolution.And that is what leads to a whole category of bugs.
In #27947, I realized that we were lacking proper support for
ignoreUnresolvablePlaceholdersin the nestedPropertySourcesPropertyResolver, and due to #34315 and #34326 it became apparent that we are currently (as of Spring Framework 6.2.6) lacking support for the configuredescapeCharacterin the nestedPropertySourcesPropertyResolveras well.Analysis
My research into #34315 and #34326 led me to realize that the reported bug for escaped placeholders being evaluated despite the escaping was in fact due to the same underlying flaw in the core logic of
PropertySourcesPlaceholderConfigurer.Namely, we should never have indirectly used or directly created a nested
PropertySourcesPropertyResolver.Instead, properties from property sources from the
Environmentshould be accessed directly without duplicate/nested placeholder resolution, since the top-levelPropertySourcesPropertyResolveralready handles placeholder resolution.Related Issues
CompositePropertySourceto be constructed from existing property sources #34862