Hacking SPR-9989: Multiple PropertyplaceHolderConfigurer with default values

Multiple property place holder configurators in spring application context, is going to (with a probability of 0.99) screw up all property values having default value (${placeHolder:defaultValue}). This is a bug in Spring release 3.1.3 and above.

I found multiple misleading articles on internet, explaining the bug. In this blog, I am putting together the summary of multiple hours I spent figuring out the bug and my approaches to deal with it.

PropertyPlaceholderConfigurer

PropertyPlaceHolderConfigurer implements BeanFactoryPostProcessor. Hence it is called after loading all the bean definitions, and before instantiation of any of the beans. Upon calling, it resolves property place holders ${placeHolder:defaultValue} and adds corresponding values to bean definitions.

Deep Diving: How does it work?

You may (should not) define any number of PropertyPlaceholderConfigurer beans in your application context. Each of them will create a singleton bean (by default) and try to resolve ALL the property place holders (${placeHolder:defaultValue}) available in your application context.

propertyplaceholderconfig

As per this UML, resolver function checks if the value attribute contains placeholder prefix (${ is default). If it does, it resolves the same and embeds the resolved value into bean definition.

As BeanDefinitions are global to your application context, so for any consecutive PropertyPlaceholderConfigurer bean, the bean properties will be the ones injected the resolver invoked before itself.

This results in arbitrary setting default/prop file defined values (example below).

config1.properties
randomProp=123

config2.properties
declaredProp=declared_123
Instantiated attachmentClien bean
attachmentClient
 |____ declaredProp = defaultValue1
 |____ undeclaredProp = defaultValue2

Even after declaring declaredProp in config2.properties, the value once resolved by configurer1 is used by attachmentClient bean.

NOTE: ignoreUnresolvablePlaceholders is set to true for a reason. Try to figure that out.

Left with no choice? [Approach 1]

As per the above explanation, you should always avoid multiple propertyplaceHolderConfigurer(s) in an application context. Though I had to provide a bean via an in-house utility framework used for creating web services using spring.

Here’s a very simple hack which I used to overcome this bug, till SPR-9989 is resolved.

We know, the default placeholder signature is ${placeHolder:defaultValue}. So if we provide a custom signature to the bean having defaultValue (which is getting set with arbitrary values), and a custom propertyplaceHolderConfigurer which understands this signature — Boom! We are done. Because we indirectly are ensuring, that no propertyplaceHolderConfigurer other than our custom one, can resolve our placeholder.

This is how it will look:

Using the same config files, the resolved values in bean will be

attachmentClient
 |____ declaredProp = declared_123
 |____ undeclaredProp = defaultValue2

Next Hack — Use at your own risk! [Approach 2]

PropertyResouceConfigurer has a priority order, which by default is LOWEST_PRECEDENCE (i.e. non-ordered). Tweaking this, and providing a higher priority to your propertyPlaceholderConfigurer, will invoke it at the beginning and as an outcome of the same bug, we will get expected results.

Maintaining the order of invocation of such configurers might be an issue.

Note: -2147483648 (minima of int) is the highest priority that can be provided to any post processor bean.

PS: I am still checking out the consequences of this approach. As this gives my placeholder configurer a higher priority compared to spring’s own configurer, which is used to resolve default properties of spring.

PPS: In my case, both approaches work perfectly fine.

The probability of 0.99?

The second approach boils down to this topic. I haven’t calculated the probability (will try to do that); but here’s the catch. There is a chance that you may get things working, as expected, without any of the above hacks. The reason being, by default configurers are ordered with LOWEST_PRECEDENCE (i.e. non-ordered).

So you never know, which configurer will first try to resolve the properties. If your lucky configurer happens to be the first one, your application is going to behave as expected.

Note: I will put a working example code for this at my github account soon.

References:

  1. Spring Bug: https://jira.spring.io/browse/SPR-9989
Show your support

Clapping shows how much you appreciated Arpit Jain’s story.