We're creating a new PropertySource that uses a database as it's repository. The idea is that we can update property values at runtime.

At the same time, we'd like to use @ConfigurationProperties to so we can include validation as well as use the application-{profile} naming conventions.

However, it appears that values of @ConfigurationProperties are only loaded from "applicationConfig: [path/to/config]" PropertySource's. For example, the following test:

...
    private final String OVERRIDEN_VALUE = "overriden value";

    @Before
    public void before() {

        LOGGER.debug("Property sources are: "); 
        for(Iterator<?> it = env.getPropertySources().iterator(); it.hasNext(); ) {
            PropertySource<?> propertySource = (PropertySource<?>) it.next();
            LOGGER.debug(propertySource.getName());
        }

        EnvironmentTestUtils.addEnvironment("integrationTest", env, "some.prefix.overridable-property=" + OVERRIDEN_VALUE);

    }

    @Test
    public void testOverridingDefaultProperties() {

        LOGGER.debug("MutablePropertySources value: {}", env.getProperty("some.prefix.overridable-property"));
        LOGGER.debug("@ConfigurationProperties value: {}", testProperties.getOverridableProperty());

        Assert.assertEquals(OVERRIDEN_VALUE, testProperties.getOverridableProperty());

    }
...

Produces this output:

Property sources are: 
    systemProperties
    systemEnvironment
    random
    integrationTest
    applicationConfig: [classpath:/path/to/my/application.yml]

MutablePropertySources value: overriden value
@ConfigurationProperties value: default value

Is this by design? If not, is this something you'd consider changing? Any help would be greatly appreciated.

Comment From: lucassaldanha

@jonathanamartin I believe that your bean which you are binding the properties is a @Component right? Spring Boot does the property binding after the bean instantiation using the ConfigurationPropertiesBindingPostProcessor class.

By default your bean will be a singleton. So once Spring created this bean instance and executed the post-processor, it won't update the properties blinded to it. I really don't know if it would be possible to change this behaviour, maybe @snicoll could give you more info about it.

Comment From: snicoll

@ConfigurationProperties are indeed bound at a very early stage of the context (and certainly not once a user bean has been initialized). You could listen for ApplicationPreparedEvent and add your PropertySource in the Environment at that stage but: 1. You need to register you initializer with the builder directly or via spring.factories 2. Your initializer can't obviously use any of the auto-configuration or autowire anything since it's not a bean (the context has not been refreshed/started yet)

I am puzzled by

The idea is that we can update property values at runtime.

How would you manage that with @ConfigurationProperties as it's a one-time binding thing (in Spring Boot at least)? You may want to have a look to the Spring Cloud config server that does all that (and much more actually).

By the way, this sort of question is better suited to StackOverflow.

Comment From: dsyer

You could use Spring Cloud Context (https://github.com/spring-cloud/spring-cloud-commons/tree/master/spring-cloud-context) to prepare the property source in a boot strap context (docs here: http://projects.spring.io/spring-cloud/spring-cloud.html#customizing-bootstrap-property-sources). It's pretty lightweight (only has Spring Boot dependencies).

You can also build your PropertySource on your own in a sufficiently early phase (which is what Spring Cloud id doing). You just need to get in there before the ConfigFileApplicationListener (so a listener of the same type with a lower order would do it). Then you have the problem of how to bootstrap the DataSource, but you always have that problem if you want to do something before the context is really alive yet.

Comment From: jonathanamartin

Thanks for the insights and quick responses. Though Cloud Config looks great, we're in a pretty locked down environment and don't think we'll be able to use it. I'll take a look at the Spring Cloud Context. Creating the property source isn't too bad, I'd just like to make the changes to that property source available via the @ConfigurationProperties at runtime.
Wonder if re-binding/refreshing the @ConfigurationProperties would be possible. In any case, I've posted this to Stack Overflow here.

Comment From: jonathanamartin

Just did some quick reading on the Spring Cloud Context:

You could use this to insert additional properties from a different server, or from a database, for instance.

and

The application will listen for an EnvironmentChangedEvent and react to the change in a couple of standard ways (additional ApplicationListeners can be added as @Beans by the user in the normal way). When an EnvironmentChangedEvent is observed it will have a list of key values that have changed, and the application will use those to:

Re-bind any @ConfigurationProperties beans in the context

Set the logger levels for any properties in logging.level.*

Looks like it may work for us. Again, much appreciated.

Comment From: Gunny133

You can also build your PropertySource on your own in a sufficiently early phase (which is what Spring Cloud id doing). You just need to get in there before the ConfigFileApplicationListener (so a listener of the same type with a lower order would do it). Then you have the problem of how to bootstrap the DataSource, but you always have that problem if you want to do something before the context is really alive yet.

@dsyer I'm facing the same problem how can i load my own properties from database from CustomPropertySourceLocator ?cause the DataSource not initialized yet? i used spring-data-jpa with hibernate

Comment From: tianmingxing

@jonathanamartin There is an answer to this question.

@Order
public class MyEnvironmentPostProcessor implements EnvironmentPostProcessor {
    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        if (!environment.getPropertySources().contains("MyPropertySource")) {
            MyPropertySource myPropertySource = new MyPropertySource();
            environment.getPropertySources().addFirst(myPropertySource);
        }
    }
}
org.springframework.boot.env.EnvironmentPostProcessor=\
com.tianmingxing.example.springboot.test.env.MyEnvironmentPostProcessor

org.springframework.boot.context.config.ConfigFileApplicationListener: SpringBoot How to build custom PropertySource for binding to @ConfigurationProperties

Unfortunately, I can't find a way to refresh the property values in the bean.