Beware of multiple Spring PropertyPlaceholderConfigurers and default values

I’ve seen quite a lot of Spring projects over the years that use multiple PropertyPlaceholderConfigurer instances in the same bean factory.
Consider a project where maven module A depends on maven module B in the same maven project. The idea is that module A is self-sustaining and loads its own property files, and module B does as well. This philosophy is good, but can lead to a horrible mess when used in combination with default values.

The problem setup

You have a Spring project with two PropertyPlaceholderConfigurer instances, each loading their own property file, and one Spring Bean that has a @Value injection with a default value.

Furthermore, you have the two property files where the first property file is missing the property and the second property file actually has it.

application.properties

application1.properties

The symptoms

The bean receives the default value "defaultValue", although property placeholder 2 actually has a value “value2” for this property.

The cause

PropertyPlaceholderConfigurer instances don’t know about each other’s existence. What happened here is:

  1. Property placeholder 1 tries to resolve the value of myKey. It doesn’t have a value for that, but “fortunately” the bean specifies a default value, so it uses that.
  2. Property placeholder 2 sees that the bean already has received a value (set by Property placeholder 1), so it ignores it.

Using ignoreUnresovablePlaceholders doesn’t help because the placeholder configurers will still use the default value if encountered on a bean.

To make matters even worse, Spring shows undeterministic behaviour here, because it uses unordered hashmaps internally that result in property placeholder 2 sometimes being fired before property placeholder 1. The Spring Bean then actually receives value “value2” which is what we would expect. I’ve seen two log files where starting the Spring container a second time reverses the order in which the property placeholder configurers are loaded.

I guess that propertyplaceholder configurers not knowing about each other is by design, but the above can be a big gotcha.

I’ve posted a sample project on GitHub that reproduces the problem. If you run the unit test multiple times it will inevitably fail at some point.

The solution alternatives

  1. Only use one PropertyPlaceholderConfigurer for your deployable unit (a WAR, for example), at the top level. I would recommend this solution first.
  2. Do not use defaults in your @Value statements. Instead, explicitly define all property values once and use this in combination with ignoreUnresovablePlaceholders on all property placeholder configurers. I consider this a brittle solution at best.

The Spring issue tracker describes the problem here. As of Spring 4.3.5 it is still unresolved.

This entry was posted in Coding and tagged . Bookmark the permalink.

Leave a Reply

Your email address will not be published.