Reputation: 71
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
For more context, I originally asked this question on Spring Boot's Github here.
Upvotes: 3
Views: 3638
Reputation: 71
Thanks to the Spring Boot folks. Pointed me to Spring Cloud Context
http://projects.spring.io/spring-cloud/spring-cloud.html#customizing-bootstrap-property-sources
Looks like this will do the trick.
Update:
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:
@ReloadableProperties
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; @Autowired 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.
Upvotes: 2