Description
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.
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
- PropertySourcesPlaceholderConfigurer ignores ignoreUnresolvablePlaceholders flag #27947
- Escaped placeholders are evaluated if the PropertySource has placeholder support #34326
- Allow
CompositePropertySource
to be constructed from existing property sources #34862 - Introduce Spring property for the default property placeholder escape character #34865
- Introduce support for escaping the escape character for property placeholders #34315