Jonathan Martin Jonathan Martin - 1 year ago 155
Java Question

How to build custom PropertySource for binding to @ConfigurationProperties

We're creating a new

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
to so we can include validation as well as use the application-{profile} naming conventions.

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

private final String OVERRIDEN_VALUE = "overriden value";

public void before() {

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

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


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:
applicationConfig: [classpath:/path/to/my/application.yml]

MutablePropertySources value: overriden value
@ConfigurationProperties value: default value

For more context, I originally asked this question on Spring Boot's Github here.

Answer Source

Thanks to the Spring Boot folks. Pointed me to Spring Cloud Context

Looks like this will do the trick.


Since we already had the additional database-backed PropertySource written we really only needed to refresh the @ConfigurationProperties at runtime. At the same time we didn't want to refresh all @ConfigurationProperties. To accomplish the refresh we did the following:

  1. Created an annotation called @ReloadableProperties
  2. Created the following utility bean which utilizes Spring Boot's ConfigurationPropertiesBindingPostProcessor
 * Helper bean to reload {@code @ConfigurationProperties}
 * if the {@code @ConfigurationProperties} bean is annotated 
 * with {@code @ReloadableProperties}.
 * @author Jonathan Martin
 * @since 2.0.0
 * @see ReloadableProperties
 * @see ConfigurationPropertiesBindingPostProcessor
public class ConfigurationPropertiesReloader {

    private final ApplicationContext context;

    private final ConfigurationPropertiesBindingPostProcessor processor;

    public ConfigurationPropertiesReloader(ApplicationContext context,  ConfigurationPropertiesBindingPostProcessor processor) {
        this.context = context;
        this.processor = processor;

     * Reload all {@code @ConfigurationProperties}
     * annotated with {@code @ReloadableProperties}.
    public void reload() {
        Map beans = context.getBeansWithAnnotation(ReloadableProperties.class);
        for (Map.Entry entry : beans.entrySet()) {

            String beanName = entry.getKey();
            Object bean = entry.getValue();

            ConfigurationProperties annotation = AnnotationUtils.findAnnotation(bean.getClass(), ConfigurationProperties.class);

            // Only reload the bean if it's an @ConfigurationProperties
            // Can't check for instance of ConfigurationPropertiesHolder 
            // because it uses package scope.
            if (annotation != null) {
                processor.postProcessBeforeInitialization(bean, beanName);



Now if I inject the utility bean into my test and invoke reload, the test passes.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download