2

Is there a way to override Spring Boot's PropertySourcesPropertyResolver and extend Externalize configuration by adding additional property source?

What I'm trying to do is to add another property source in the current list, and be able to override this property with current mechanism. And to extend PropertySourcesPropertyResolver, so when Spring is mapping properties for classes annotated with @ConfigurationProperties and requesting for the key, it can check for key with two different prefixes.

For example, if I have properties from two different locations:

Properties on location 1: data.user=userName

Properties on location 2: service.data.user=serviceUserName

I want to be able to override the value of data.user with a value of service.data.user (if that property exists).

d-sauer
  • 1,485
  • 1
  • 15
  • 20

2 Answers2

3

Just to share with others, maybe someone finds this useful.

The Entire solution is related with bean which extends PropertySourcesPlaceholderConfigurer.

  1. Part is to add another property source to the existing list of property sources. In my case I needed application.property which is stored in some jar file.

For that purpose, we have JarPropertiesPropertySource:

public class JarPropertiesPropertySource extends MapPropertySource implements Logger {

    @SuppressWarnings({"unchecked", "rawtypes"})
    public JarPropertiesPropertySource(String name, Properties source) {
        super(name, (Map) source);
    }

    protected JarPropertiesPropertySource(String name, Map<String, Object> source) {
        super(name, source);
    }    
}

And the main logic is in CustomPropertySourcesPlaceholderConfigurer bean:

public class CustomPropertySourcesPlaceholderConfigurer extends PropertySourcesPlaceholderConfigurer implements Logger {

    @Override
    public PropertySources getAppliedPropertySources() throws IllegalStateException {
        final MutablePropertySources mps = new MutablePropertySources();

        // Get existing PropertySources
        PropertySources ps = super.getAppliedPropertySources();        
        ps.forEach(propSource -> mps.addLast(new PropertySourceProxy(propSource)));

        // Add JAR property source
        mps.addLast(new JarPropertiesPropertySource("jar", ....))

        return mps;
    }
}
  1. We need to support property key overloading. For example, if we have property 'serviceA|data.user', and if that property doesn't exist then try to find property 'data/user'. For that purpose, we need to proxy property sources, and each property from super.getAppliedPropertySources() wrap inside proxy PropertySourceProxy. So each spring call for getProperty will go through proxy method and check variations:

.

public class PropertySourceProxy extends PropertySource {

    public PropertySourceProxy(PropertySource propertySource) {
        super(propertySource.getName(), propertySource.getSource());
        Object o = propertySource.getSource();
        if (o instanceof StandardEnvironment) {
            ConfigurableEnvironment ce = (ConfigurableEnvironment) o;
            MutablePropertySources cemps = ce.getPropertySources();
            cemps.forEach(propSource -> {
                cemps.replace(propSource.getName(), new PropertySourceProxy(propSource));
            });
        }
    }

    @Override
    public Object getProperty(String name) {
        Object value = null;

        if (name != null) {
            int index = name.indexOf("|");
            if (index != -1) {
                String newName = name.substring(index + 1);
                value = super.getProperty(newName);
            }
        }

        if (value == null)
            value = super.getProperty(name);

        return value;
    }
}
Brad
  • 15,186
  • 11
  • 60
  • 74
d-sauer
  • 1,485
  • 1
  • 15
  • 20
0

from: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/config/PlaceholderConfigurerSupport.html#DEFAULT_VALUE_SEPARATOR

Example XML property with default value:

 <property name="url" value="jdbc:${dbname:defaultdb}"/>

However, there appear to be a few people who have been having problems with multiple property placeholders, so you might want to watch out for:

The other way you could do it is to provide a default value for your data.user property and let people override it as necessary.

Community
  • 1
  • 1
ninj
  • 1,529
  • 10
  • 10