Commit 5f8c1e77 authored by Andy Wilkinson's avatar Andy Wilkinson

Merge config from custom locations with default configuration

Previously, when one or more custom locations were specified on
@ConfigurationProperties, the configuration loaded from those locations
was used in isolation from the default configuration provided by the
environment. Users have been surprised by this behaviour. For example,
it means that a placeholder used in the custom configuration will not
be resolved against the system properties.

This commit adds a new attribute, merge, to @ConfigurationProperties,
that defaults to true. When merge is true the default property sources
are appended to those that are loaded from the custom locations. When
set to false the custom configuration is used in isolation.

Closes #1301
parent 1e51c5db
......@@ -78,10 +78,20 @@ public @interface ConfigurationProperties {
boolean exceptionIfInvalid() default true;
/**
* Optionally provide an explicit resource locations to bind to instead of using the
* default environment.
* Optionally provide explicit resource locations to bind to. By default the
* configuration at these specified locations will be merged with the default
* configuration.
* @return the path (or paths) of resources to bind to
* @see #merge()
*/
String[] locations() default {};
/**
* Flag to indicate that configuration loaded from the specified locations should be
* merged with the default configuration.
* @return the flag value (default true)
* @see #locations()
*/
boolean merge() default true;
}
......@@ -262,7 +262,8 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc
PropertiesConfigurationFactory<Object> factory = new PropertiesConfigurationFactory<Object>(
target);
if (annotation != null && annotation.locations().length != 0) {
factory.setPropertySources(loadPropertySources(annotation.locations()));
factory.setPropertySources(loadPropertySources(annotation.locations(),
annotation.merge()));
}
else {
factory.setPropertySources(this.propertySources);
......@@ -301,7 +302,8 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc
return this.validator;
}
private PropertySources loadPropertySources(String[] locations) {
private PropertySources loadPropertySources(String[] locations,
boolean mergeDefaultSources) {
try {
PropertySourcesLoader loader = new PropertySourcesLoader();
for (String location : locations) {
......@@ -314,7 +316,14 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc
}
loader.load(resource);
}
return loader.getPropertySources();
MutablePropertySources loaded = loader.getPropertySources();
if (mergeDefaultSources) {
for (PropertySource<?> propertySource : this.propertySources) {
loaded.addLast(propertySource);
}
}
return loaded;
}
catch (IOException ex) {
throw new IllegalStateException(ex);
......
......@@ -141,6 +141,26 @@ public class ConfigurationPropertiesBindingPostProcessorTests {
equalTo("foo"));
}
@Test
public void placeholderResolutionWithCustomLocation() throws Exception {
this.context = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(this.context, "fooValue:bar");
this.context.register(CustomConfigurationLocation.class);
this.context.refresh();
assertThat(this.context.getBean(CustomConfigurationLocation.class).getFoo(),
equalTo("bar"));
}
@Test
public void placeholderResolutionWithUnmergedCustomLocation() throws Exception {
this.context = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(this.context, "fooValue:bar");
this.context.register(UnmergedCustomConfigurationLocation.class);
this.context.refresh();
assertThat(this.context.getBean(UnmergedCustomConfigurationLocation.class)
.getFoo(), equalTo("${fooValue}"));
}
@Configuration
@EnableConfigurationProperties
public static class TestConfigurationWithValidatingSetter {
......@@ -299,4 +319,36 @@ public class ConfigurationPropertiesBindingPostProcessorTests {
}
@EnableConfigurationProperties
@ConfigurationProperties(locations = "custom-location.yml")
public static class CustomConfigurationLocation {
private String foo;
public String getFoo() {
return this.foo;
}
public void setFoo(String foo) {
this.foo = foo;
}
}
@EnableConfigurationProperties
@ConfigurationProperties(locations = "custom-location.yml", merge = false)
public static class UnmergedCustomConfigurationLocation {
private String foo;
public String getFoo() {
return this.foo;
}
public void setFoo(String foo) {
this.foo = foo;
}
}
}
foo: ${fooValue}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment