From 8eae37243367ad057b23179c08ea26003dca3c69 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Sat, 28 Oct 2017 12:33:52 +0100 Subject: [PATCH] Change handling of spring.config.location so it replaces defaults Previously, spring.config.name was handled in such a way that its value would replace the defaults. By constrast, spring.config.location would add to the defaults. Update the handling of spring.config.location so that it replaces the defaults. This aligns its behaviour with spring.config.name. To allow users to add additional locations a new property, spring.config.additional-location, has been introduced. It behaves as spring.config.location did prior to this change. Closes gh-10595 --- .../main/asciidoc/spring-boot-features.adoc | 20 +++++-- .../config/ConfigFileApplicationListener.java | 25 ++++++-- ...itional-spring-configuration-metadata.json | 8 ++- .../ConfigFileApplicationListenerTests.java | 60 ++++++++++++++++++- 4 files changed, 99 insertions(+), 14 deletions(-) diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc index 9366840804..9eef44d046 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc @@ -555,17 +555,27 @@ before being loaded, including profile-specific file names). Files specified in and are overridden by any profile-specific properties. Config locations are searched in reverse order. By default, the configured locations are -`classpath:/,classpath:/config/,file:./,file:./config/`. The resulting search order is: +`classpath:/,classpath:/config/,file:./,file:./config/`. The resulting search order is the +following: . `file:./config/` . `file:./` . `classpath:/config/` . `classpath:/` -When custom config locations are configured, they are used in addition to the default -locations. Custom locations are searched before the default locations. For example, if -custom locations of `classpath:/custom-config/,file:./custom-config/` are configured, the -search order becomes: +When custom config locations are configured using `spring.config.location` they replace +the default locations. For example, if `spring.config.location` is configured with the +value `classpath:/custom-config/,file:./custom-config/`, the search order becomes the +following: + +. `file:./custom-config/` +. `classpath:custom-config/` + +Alternatively, when custom config locations are configured using +`spring.config.addition-location`, they are used in addition to the default locations. +Additional locations are search before the default locations. For example, if +additional locations of `classpath:/custom-config/,file:./custom-config/` are configured, +the search order becomes the following: . `file:./custom-config/` . `classpath:custom-config/` diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigFileApplicationListener.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigFileApplicationListener.java index 0b1e839859..a3fbcc59be 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigFileApplicationListener.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigFileApplicationListener.java @@ -121,6 +121,11 @@ public class ConfigFileApplicationListener */ public static final String CONFIG_LOCATION_PROPERTY = "spring.config.location"; + /** + * The "config additional location" property name. + */ + public static final String CONFIG_ADDITIONAL_LOCATION_PROPERTY = "spring.config.additional-location"; + /** * The default order for the processor. */ @@ -563,11 +568,22 @@ public class ConfigFileApplicationListener } private Set getSearchLocations() { - Set locations = new LinkedHashSet<>(); - // User-configured settings take precedence, so we do them first if (this.environment.containsProperty(CONFIG_LOCATION_PROPERTY)) { + return getSearchLocations(CONFIG_LOCATION_PROPERTY); + } + Set locations = getSearchLocations( + CONFIG_ADDITIONAL_LOCATION_PROPERTY); + locations.addAll( + asResolvedSet(ConfigFileApplicationListener.this.searchLocations, + DEFAULT_SEARCH_LOCATIONS)); + return locations; + } + + private Set getSearchLocations(String propertyName) { + Set locations = new LinkedHashSet<>(); + if (this.environment.containsProperty(propertyName)) { for (String path : asResolvedSet( - this.environment.getProperty(CONFIG_LOCATION_PROPERTY), null)) { + this.environment.getProperty(propertyName), null)) { if (!path.contains("$")) { path = StringUtils.cleanPath(path); if (!ResourceUtils.isUrl(path)) { @@ -577,9 +593,6 @@ public class ConfigFileApplicationListener locations.add(path); } } - locations.addAll( - asResolvedSet(ConfigFileApplicationListener.this.searchLocations, - DEFAULT_SEARCH_LOCATIONS)); return locations; } diff --git a/spring-boot-project/spring-boot/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-project/spring-boot/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 04dabaef52..72661f5926 100644 --- a/spring-boot-project/spring-boot/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-boot-project/spring-boot/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -132,6 +132,12 @@ "description": "Skip search of BeanInfo classes.", "defaultValue": true }, + { + "name": "spring.config.additional-location", + "type": "java.lang.String", + "sourceType": "org.springframework.boot.context.config.ConfigFileApplicationListener", + "description": "Config file locations used in addition to the defaults." + }, { "name": "spring.config.name", "type": "java.lang.String", @@ -143,7 +149,7 @@ "name": "spring.config.location", "type": "java.lang.String", "sourceType": "org.springframework.boot.context.config.ConfigFileApplicationListener", - "description": "Config file locations." + "description": "Config file locations that replace the defaults." }, { "name": "spring.jta.atomikos.properties.console-log-level", diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigFileApplicationListenerTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigFileApplicationListenerTests.java index 23786e5741..75dc8aca81 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigFileApplicationListenerTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigFileApplicationListenerTests.java @@ -154,8 +154,20 @@ public class ConfigFileApplicationListenerTests { @Test public void loadTwoPropertiesFilesWithProfiles() throws Exception { TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.environment, - "spring.config.location=" - + "classpath:enableprofile.properties,classpath:enableother.properties"); + "spring.config.location=classpath:enableprofile.properties," + + "classpath:enableother.properties"); + this.initializer.postProcessEnvironment(this.environment, this.application); + assertThat(this.environment.getActiveProfiles()).containsExactly("other"); + String property = this.environment.getProperty("my.property"); + assertThat(property).isEqualTo("fromenableotherpropertiesfile"); + } + + @Test + public void loadTwoPropertiesFilesWithProfilesUsingAdditionalLocation() + throws Exception { + TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.environment, + "spring.config.additional-location=classpath:enableprofile.properties," + + "classpath:enableother.properties"); this.initializer.postProcessEnvironment(this.environment, this.application); assertThat(this.environment.getActiveProfiles()).containsExactly("other"); String property = this.environment.getProperty("my.property"); @@ -564,6 +576,22 @@ public class ConfigFileApplicationListenerTests { this.initializer.postProcessEnvironment(this.environment, this.application); String property = this.environment.getProperty("the.property"); assertThat(property).isEqualTo("fromspecificlocation"); + assertThat(this.environment).has(matchingPropertySource( + "applicationConfig: " + "[classpath:specificlocation.properties]")); + // The default property source is not there + assertThat(this.environment).doesNotHave(matchingPropertySource( + "applicationConfig: " + "[classpath:/application.properties]")); + assertThat(this.environment.getProperty("foo")).isNull(); + } + + @Test + public void specificResourceFromAdditionalLocation() throws Exception { + String additionalLocation = "classpath:specificlocation.properties"; + TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.environment, + "spring.config.additional-location=" + additionalLocation); + this.initializer.postProcessEnvironment(this.environment, this.application); + String property = this.environment.getProperty("the.property"); + assertThat(property).isEqualTo("fromspecificlocation"); assertThat(this.environment).has(matchingPropertySource( "applicationConfig: " + "[classpath:specificlocation.properties]")); // The default property source is still there @@ -816,6 +844,34 @@ public class ConfigFileApplicationListenerTests { .containsExactly("testPropertySource"); } + @Test + public void additionalLocationTakesPrecedenceOverDefaultLocation() throws Exception { + TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.environment, + "spring.config.additional-location=classpath:override.properties"); + this.initializer.postProcessEnvironment(this.environment, this.application); + assertThat(this.environment.getProperty("foo")).isEqualTo("bar"); + assertThat(this.environment.getProperty("value")).isEqualTo("1234"); + } + + @Test + public void lastAdditionalLocationWins() throws Exception { + TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.environment, + "spring.config.additional-location=classpath:override.properties," + + "classpath:some.properties"); + this.initializer.postProcessEnvironment(this.environment, this.application); + assertThat(this.environment.getProperty("foo")).isEqualTo("spam"); + assertThat(this.environment.getProperty("value")).isEqualTo("1234"); + } + + @Test + public void locationReplaceDefaultLocation() throws Exception { + TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.environment, + "spring.config.location=classpath:override.properties"); + this.initializer.postProcessEnvironment(this.environment, this.application); + assertThat(this.environment.getProperty("foo")).isEqualTo("bar"); + assertThat(this.environment.getProperty("value")).isNull(); + } + private Condition matchingPropertySource( final String sourceName) { return new Condition(