Adjust order of property sources

@PropertySources *can* and should be added in the slot
after the application.properties (code that is part of the
application should have lower precedence than external
configuration).

Fixes gh-1044
This commit is contained in:
Dave Syer 2014-06-07 08:52:50 +01:00
parent e81e94924c
commit b75578d99c
11 changed files with 62 additions and 32 deletions

View File

@ -207,11 +207,11 @@ sensible overriding of values, properties are considered in the the following or
. OS environment variables. . OS environment variables.
. JNDI attributes from `java:comp/env` . JNDI attributes from `java:comp/env`
. A `RandomValuePropertySource` that only has properties in `random.*`. . A `RandomValuePropertySource` that only has properties in `random.*`.
. `@PropertySource` annotations on your `@Configuration` classes.
. Application properties outside of your packaged jar (`application.properties` . Application properties outside of your packaged jar (`application.properties`
including YAML and profile variants). including YAML and profile variants).
. Application properties packaged inside your jar (`application.properties` . Application properties packaged inside your jar (`application.properties`
including YAML and profile variants). including YAML and profile variants).
. `@PropertySource` annotations on your `@Configuration` classes.
. Default properties (specified using `SpringApplication.setDefaultProperties`). . Default properties (specified using `SpringApplication.setDefaultProperties`).
To provide a concrete example, suppose you develop a `@Component` that uses a To provide a concrete example, suppose you develop a `@Component` that uses a

View File

@ -497,19 +497,22 @@ public class ConfigFileApplicationListener implements
public static void finishAndRelocate(MutablePropertySources propertySources) { public static void finishAndRelocate(MutablePropertySources propertySources) {
ConfigurationPropertySources removed = (ConfigurationPropertySources) propertySources ConfigurationPropertySources removed = (ConfigurationPropertySources) propertySources
.remove(ConfigurationPropertySources.NAME); .get(ConfigurationPropertySources.NAME);
String name = ConfigurationPropertySources.NAME;
if (removed != null) { if (removed != null) {
for (PropertySource<?> propertySource : removed.sources) { for (PropertySource<?> propertySource : removed.sources) {
if (propertySource instanceof EnumerableCompositePropertySource) { if (propertySource instanceof EnumerableCompositePropertySource) {
EnumerableCompositePropertySource composite = (EnumerableCompositePropertySource) propertySource; EnumerableCompositePropertySource composite = (EnumerableCompositePropertySource) propertySource;
for (PropertySource<?> nested : composite.getSource()) { for (PropertySource<?> nested : composite.getSource()) {
propertySources.addLast(nested); propertySources.addAfter(name, nested);
name = nested.getName();
} }
} }
else { else {
propertySources.addLast(propertySource); propertySources.addAfter(name, propertySource);
} }
} }
propertySources.remove(ConfigurationPropertySources.NAME);
} }
} }

View File

@ -53,13 +53,23 @@ public class PropertySourcesBindingTests {
private Wrapper properties; private Wrapper properties;
@Test @Test
public void overridingOfPropertiesWorksAsExpected() { public void overridingOfPropertiesOrderOfAtPropertySources() {
assertThat(this.properties.getBar(), is("override"));
}
@Test
public void overridingOfPropertiesAndBindToAtValue() {
assertThat(this.foo, is(this.properties.getFoo())); assertThat(this.foo, is(this.properties.getFoo()));
} }
@Test
public void overridingOfPropertiesOrderOfApplicationProperties() {
assertThat(this.properties.getFoo(), is("bucket"));
}
@Import({ SomeConfig.class }) @Import({ SomeConfig.class })
@PropertySources({ @PropertySource("classpath:/override.properties"), @PropertySources({ @PropertySource("classpath:/some.properties"),
@PropertySource("classpath:/some.properties") }) @PropertySource("classpath:/override.properties") })
@Configuration @Configuration
@EnableConfigurationProperties(Wrapper.class) @EnableConfigurationProperties(Wrapper.class)
public static class TestConfig { public static class TestConfig {
@ -81,6 +91,16 @@ public class PropertySourcesBindingTests {
public static class Wrapper { public static class Wrapper {
private String foo; private String foo;
private String bar;
public String getBar() {
return this.bar;
}
public void setBar(String bar) {
this.bar = bar;
}
public String getFoo() { public String getFoo() {
return this.foo; return this.foo;
} }

View File

@ -82,7 +82,7 @@ public class ConfigFileApplicationListenerTests {
@After @After
public void cleanup() { public void cleanup() {
System.clearProperty("my.property"); System.clearProperty("the.property");
System.clearProperty("spring.config.location"); System.clearProperty("spring.config.location");
System.clearProperty("spring.main.showBanner"); System.clearProperty("spring.main.showBanner");
} }
@ -93,7 +93,7 @@ public class ConfigFileApplicationListenerTests {
@Override @Override
public Resource getResource(final String location) { public Resource getResource(final String location) {
if (location.equals("classpath:/custom.properties")) { if (location.equals("classpath:/custom.properties")) {
return new ByteArrayResource("my.property: fromcustom".getBytes(), return new ByteArrayResource("the.property: fromcustom".getBytes(),
location) { location) {
@Override @Override
public String getFilename() { public String getFilename() {
@ -111,7 +111,7 @@ public class ConfigFileApplicationListenerTests {
}); });
this.initializer.setSearchNames("custom"); this.initializer.setSearchNames("custom");
this.initializer.onApplicationEvent(this.event); this.initializer.onApplicationEvent(this.event);
String property = this.environment.getProperty("my.property"); String property = this.environment.getProperty("the.property");
assertThat(property, equalTo("fromcustom")); assertThat(property, equalTo("fromcustom"));
} }
@ -119,7 +119,7 @@ public class ConfigFileApplicationListenerTests {
public void loadPropertiesFile() throws Exception { public void loadPropertiesFile() throws Exception {
this.initializer.setSearchNames("testproperties"); this.initializer.setSearchNames("testproperties");
this.initializer.onApplicationEvent(this.event); this.initializer.onApplicationEvent(this.event);
String property = this.environment.getProperty("my.property"); String property = this.environment.getProperty("the.property");
assertThat(property, equalTo("frompropertiesfile")); assertThat(property, equalTo("frompropertiesfile"));
} }
@ -128,7 +128,7 @@ public class ConfigFileApplicationListenerTests {
EnvironmentTestUtils.addEnvironment(this.environment, "spring.config.location:" EnvironmentTestUtils.addEnvironment(this.environment, "spring.config.location:"
+ "classpath:application.properties,classpath:testproperties.properties"); + "classpath:application.properties,classpath:testproperties.properties");
this.initializer.onApplicationEvent(this.event); this.initializer.onApplicationEvent(this.event);
String property = this.environment.getProperty("my.property"); String property = this.environment.getProperty("the.property");
assertThat(property, equalTo("frompropertiesfile")); assertThat(property, equalTo("frompropertiesfile"));
} }
@ -152,7 +152,7 @@ public class ConfigFileApplicationListenerTests {
assertEquals("myprofile", assertEquals("myprofile",
StringUtils.arrayToCommaDelimitedString(this.environment StringUtils.arrayToCommaDelimitedString(this.environment
.getActiveProfiles())); .getActiveProfiles()));
String property = this.environment.getProperty("my.property"); String property = this.environment.getProperty("the.property");
// The value from the second file wins (no profile specific configuration is // The value from the second file wins (no profile specific configuration is
// actually loaded) // actually loaded)
assertThat(property, equalTo("frompropertiesfile")); assertThat(property, equalTo("frompropertiesfile"));
@ -168,7 +168,7 @@ public class ConfigFileApplicationListenerTests {
assertEquals("myprofile", assertEquals("myprofile",
StringUtils.arrayToCommaDelimitedString(this.environment StringUtils.arrayToCommaDelimitedString(this.environment
.getActiveProfiles())); .getActiveProfiles()));
String property = this.environment.getProperty("my.property"); String property = this.environment.getProperty("the.property");
// The value from the second file wins (no profile specific configuration is // The value from the second file wins (no profile specific configuration is
// actually loaded) // actually loaded)
assertThat(property, equalTo("frompropertiesfile")); assertThat(property, equalTo("frompropertiesfile"));
@ -180,7 +180,7 @@ public class ConfigFileApplicationListenerTests {
assertThat(localFile.exists(), equalTo(false)); assertThat(localFile.exists(), equalTo(false));
try { try {
Properties properties = new Properties(); Properties properties = new Properties();
properties.put("my.property", "fromlocalfile"); properties.put("the.property", "fromlocalfile");
OutputStream out = new FileOutputStream(localFile); OutputStream out = new FileOutputStream(localFile);
try { try {
properties.store(out, ""); properties.store(out, "");
@ -189,7 +189,7 @@ public class ConfigFileApplicationListenerTests {
out.close(); out.close();
} }
this.initializer.onApplicationEvent(this.event); this.initializer.onApplicationEvent(this.event);
String property = this.environment.getProperty("my.property"); String property = this.environment.getProperty("the.property");
assertThat(property, equalTo("fromlocalfile")); assertThat(property, equalTo("fromlocalfile"));
} }
finally { finally {
@ -213,7 +213,7 @@ public class ConfigFileApplicationListenerTests {
+ "classpath:testproperties.properties," + "classpath:testproperties.properties,"
+ "classpath:nonexistent.properties"); + "classpath:nonexistent.properties");
this.initializer.onApplicationEvent(this.event); this.initializer.onApplicationEvent(this.event);
String property = this.environment.getProperty("my.property"); String property = this.environment.getProperty("the.property");
assertThat(property, equalTo("frompropertiesfile")); assertThat(property, equalTo("frompropertiesfile"));
} }
@ -228,7 +228,7 @@ public class ConfigFileApplicationListenerTests {
public void loadTwoPropertiesFiles() throws Exception { public void loadTwoPropertiesFiles() throws Exception {
this.initializer.setSearchNames("moreproperties,testproperties"); this.initializer.setSearchNames("moreproperties,testproperties");
this.initializer.onApplicationEvent(this.event); this.initializer.onApplicationEvent(this.event);
String property = this.environment.getProperty("my.property"); String property = this.environment.getProperty("the.property");
// The search order has highest precedence last (like merging a map) // The search order has highest precedence last (like merging a map)
assertThat(property, equalTo("frompropertiesfile")); assertThat(property, equalTo("frompropertiesfile"));
} }
@ -246,19 +246,19 @@ public class ConfigFileApplicationListenerTests {
@Test @Test
public void commandLineWins() throws Exception { public void commandLineWins() throws Exception {
this.environment.getPropertySources().addFirst( this.environment.getPropertySources().addFirst(
new SimpleCommandLinePropertySource("--my.property=fromcommandline")); new SimpleCommandLinePropertySource("--the.property=fromcommandline"));
this.initializer.setSearchNames("testproperties"); this.initializer.setSearchNames("testproperties");
this.initializer.onApplicationEvent(this.event); this.initializer.onApplicationEvent(this.event);
String property = this.environment.getProperty("my.property"); String property = this.environment.getProperty("the.property");
assertThat(property, equalTo("fromcommandline")); assertThat(property, equalTo("fromcommandline"));
} }
@Test @Test
public void systemPropertyWins() throws Exception { public void systemPropertyWins() throws Exception {
System.setProperty("my.property", "fromsystem"); System.setProperty("the.property", "fromsystem");
this.initializer.setSearchNames("testproperties"); this.initializer.setSearchNames("testproperties");
this.initializer.onApplicationEvent(this.event); this.initializer.onApplicationEvent(this.event);
String property = this.environment.getProperty("my.property"); String property = this.environment.getProperty("the.property");
assertThat(property, equalTo("fromsystem")); assertThat(property, equalTo("fromsystem"));
} }
@ -285,7 +285,7 @@ public class ConfigFileApplicationListenerTests {
.singletonMap("spring.config.name", .singletonMap("spring.config.name",
(Object) "testproperties"))); (Object) "testproperties")));
this.initializer.onApplicationEvent(this.event); this.initializer.onApplicationEvent(this.event);
String property = this.environment.getProperty("my.property"); String property = this.environment.getProperty("the.property");
assertThat(property, equalTo("frompropertiesfile")); assertThat(property, equalTo("frompropertiesfile"));
} }
@ -318,7 +318,7 @@ public class ConfigFileApplicationListenerTests {
public void loadPropertiesThenProfilePropertiesActivatedInFirst() throws Exception { public void loadPropertiesThenProfilePropertiesActivatedInFirst() throws Exception {
this.initializer.setSearchNames("enableprofile"); this.initializer.setSearchNames("enableprofile");
this.initializer.onApplicationEvent(this.event); this.initializer.onApplicationEvent(this.event);
String property = this.environment.getProperty("my.property"); String property = this.environment.getProperty("the.property");
// The "myprofile" profile is activated in enableprofile.properties so its value // The "myprofile" profile is activated in enableprofile.properties so its value
// should show up here // should show up here
assertThat(property, equalTo("fromprofilepropertiesfile")); assertThat(property, equalTo("fromprofilepropertiesfile"));
@ -334,7 +334,7 @@ public class ConfigFileApplicationListenerTests {
String property = this.environment.getProperty("other.property"); String property = this.environment.getProperty("other.property");
// The "other" profile is activated before any processing starts // The "other" profile is activated before any processing starts
assertThat(property, equalTo("fromotherpropertiesfile")); assertThat(property, equalTo("fromotherpropertiesfile"));
property = this.environment.getProperty("my.property"); property = this.environment.getProperty("the.property");
// The "myprofile" profile is activated in enableprofile.properties and "other" // The "myprofile" profile is activated in enableprofile.properties and "other"
// was not activated by setting spring.profiles.active so "myprofile" should still // was not activated by setting spring.profiles.active so "myprofile" should still
// be activated // be activated
@ -428,7 +428,7 @@ public class ConfigFileApplicationListenerTests {
EnvironmentTestUtils.addEnvironment(this.environment, "spring.config.location:" EnvironmentTestUtils.addEnvironment(this.environment, "spring.config.location:"
+ location); + location);
this.initializer.onApplicationEvent(this.event); this.initializer.onApplicationEvent(this.event);
String property = this.environment.getProperty("my.property"); String property = this.environment.getProperty("the.property");
assertThat(property, equalTo("fromspecificlocation")); assertThat(property, equalTo("fromspecificlocation"));
assertThat(this.environment, containsPropertySource("applicationConfig: " assertThat(this.environment, containsPropertySource("applicationConfig: "
+ "[classpath:specificlocation.properties]")); + "[classpath:specificlocation.properties]"));
@ -463,7 +463,7 @@ public class ConfigFileApplicationListenerTests {
SpringApplication application = new SpringApplication(WithPropertySource.class); SpringApplication application = new SpringApplication(WithPropertySource.class);
application.setWebEnvironment(false); application.setWebEnvironment(false);
ConfigurableApplicationContext context = application.run(); ConfigurableApplicationContext context = application.run();
String property = context.getEnvironment().getProperty("my.property"); String property = context.getEnvironment().getProperty("the.property");
assertThat(property, equalTo("fromspecificlocation")); assertThat(property, equalTo("fromspecificlocation"));
assertThat(context.getEnvironment(), assertThat(context.getEnvironment(),
containsPropertySource("class path resource " containsPropertySource("class path resource "
@ -480,7 +480,7 @@ public class ConfigFileApplicationListenerTests {
application.setEnvironment(this.environment); application.setEnvironment(this.environment);
application.setWebEnvironment(false); application.setWebEnvironment(false);
ConfigurableApplicationContext context = application.run(); ConfigurableApplicationContext context = application.run();
String property = context.getEnvironment().getProperty("my.property"); String property = context.getEnvironment().getProperty("the.property");
assertThat(property, equalTo("fromspecificlocation")); assertThat(property, equalTo("fromspecificlocation"));
assertThat(context.getEnvironment(), assertThat(context.getEnvironment(),
containsPropertySource("class path resource " containsPropertySource("class path resource "
@ -494,7 +494,7 @@ public class ConfigFileApplicationListenerTests {
WithPropertySourceAndName.class); WithPropertySourceAndName.class);
application.setWebEnvironment(false); application.setWebEnvironment(false);
ConfigurableApplicationContext context = application.run(); ConfigurableApplicationContext context = application.run();
String property = context.getEnvironment().getProperty("my.property"); String property = context.getEnvironment().getProperty("the.property");
assertThat(property, equalTo("fromspecificlocation")); assertThat(property, equalTo("fromspecificlocation"));
assertThat(context.getEnvironment(), containsPropertySource("foo")); assertThat(context.getEnvironment(), containsPropertySource("foo"));
context.close(); context.close();
@ -507,7 +507,7 @@ public class ConfigFileApplicationListenerTests {
application.setWebEnvironment(false); application.setWebEnvironment(false);
ConfigurableApplicationContext context = application ConfigurableApplicationContext context = application
.run("--spring.profiles.active=myprofile"); .run("--spring.profiles.active=myprofile");
String property = context.getEnvironment().getProperty("my.property"); String property = context.getEnvironment().getProperty("the.property");
assertThat(property, equalTo("frompropertiesfile")); assertThat(property, equalTo("frompropertiesfile"));
assertThat(context.getEnvironment(), assertThat(context.getEnvironment(),
containsPropertySource("class path resource " containsPropertySource("class path resource "
@ -536,7 +536,7 @@ public class ConfigFileApplicationListenerTests {
WithPropertySourceMultipleLocations.class); WithPropertySourceMultipleLocations.class);
application.setWebEnvironment(false); application.setWebEnvironment(false);
ConfigurableApplicationContext context = application.run(); ConfigurableApplicationContext context = application.run();
String property = context.getEnvironment().getProperty("my.property"); String property = context.getEnvironment().getProperty("the.property");
assertThat(property, equalTo("frommorepropertiesfile")); assertThat(property, equalTo("frommorepropertiesfile"));
assertThat(context.getEnvironment(), assertThat(context.getEnvironment(),
containsPropertySource("class path resource " containsPropertySource("class path resource "
@ -550,7 +550,7 @@ public class ConfigFileApplicationListenerTests {
WithPropertySourceMultipleLocationsAndName.class); WithPropertySourceMultipleLocationsAndName.class);
application.setWebEnvironment(false); application.setWebEnvironment(false);
ConfigurableApplicationContext context = application.run(); ConfigurableApplicationContext context = application.run();
String property = context.getEnvironment().getProperty("my.property"); String property = context.getEnvironment().getProperty("the.property");
assertThat(property, equalTo("frommorepropertiesfile")); assertThat(property, equalTo("frommorepropertiesfile"));
assertThat(context.getEnvironment(), containsPropertySource("foo")); assertThat(context.getEnvironment(), containsPropertySource("foo"));
context.close(); context.close();

View File

@ -1 +1,2 @@
my.property=fromprofilepropertiesfile my.property=fromprofilepropertiesfile
the.property=fromprofilepropertiesfile

View File

@ -1,3 +1,4 @@
spring.profiles.active=myprofile spring.profiles.active=myprofile
my.property=frompropertiesfile my.property=frompropertiesfile
the.property=frompropertiesfile
one.more=${my.property} one.more=${my.property}

View File

@ -1 +1,2 @@
my.property=frommorepropertiesfile my.property=frommorepropertiesfile
the.property=frommorepropertiesfile

View File

@ -1 +1,2 @@
foo=bar foo=bar
bar=override

View File

@ -1 +1,2 @@
foo=spam foo=spam
bar=some

View File

@ -1 +1,2 @@
my.property=fromspecificlocation my.property=fromspecificlocation
the.property=fromspecificlocation

View File

@ -1 +1,2 @@
my.property=frompropertiesfile my.property=frompropertiesfile
the.property=frompropertiesfile