diff --git a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc index 4310e0b937a..32c6f70da4f 100644 --- a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc +++ b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc @@ -374,6 +374,7 @@ specified by the `spring.active.profiles` property are added after those configu the `SpringApplication` API and therefore take precedence. + [[boot-features-external-config-placeholders-in-properties]] === Placeholders in properties The values in `application.properties` are filtered through the existing `Environment` diff --git a/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigFileApplicationListener.java b/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigFileApplicationListener.java index 4578f2367e4..fd3194eb700 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigFileApplicationListener.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigFileApplicationListener.java @@ -307,25 +307,14 @@ public class ConfigFileApplicationListener public void load() throws IOException { this.propertiesLoader = new PropertySourcesLoader(); - this.profiles = Collections.asLifoQueue(new LinkedList()); this.activatedProfiles = false; + this.profiles = Collections.asLifoQueue(new LinkedList()); - Set initialActiveProfiles = null; - if (this.environment.containsProperty(ACTIVE_PROFILES_PROPERTY)) { - // Any pre-existing active profiles set via property sources (e.g. System - // properties) take precedence over those added in config files. - initialActiveProfiles = maybeActivateProfiles( - this.environment.getProperty(ACTIVE_PROFILES_PROPERTY)); - } // Pre-existing active profiles set via Environment.setActiveProfiles() // are additional profiles and config files are allowed to add more if // they want to, so don't call addActiveProfiles() here. - List list = filterEnvironmentProfiles(initialActiveProfiles != null - ? initialActiveProfiles : Collections.emptySet()); - // Reverse them so the order is the same as from getProfilesForValue() - // (last one wins when properties are eventually resolved) - Collections.reverse(list); - this.profiles.addAll(list); + Set initialActiveProfiles = initializeActiveProfiles(); + this.profiles.addAll(getUnprocessedActiveProfiles(initialActiveProfiles)); // The default profile for these purposes is represented as null. We add it // last so that it is first out of the queue (active profiles will then @@ -351,6 +340,44 @@ public class ConfigFileApplicationListener addConfigurationProperties(this.propertiesLoader.getPropertySources()); } + private Set initializeActiveProfiles() { + if (!this.environment.containsProperty(ACTIVE_PROFILES_PROPERTY)) { + return Collections.emptySet(); + } + // Any pre-existing active profiles set via property sources (e.g. System + // properties) take precedence over those added in config files. + Set activeProfiles = getProfilesForValue( + this.environment.getProperty(ACTIVE_PROFILES_PROPERTY)); + maybeActivateProfiles(activeProfiles); + return activeProfiles; + } + + /** + * Return the active profiles that have not been processed yet. If a profile is + * enabled via both {@link #ACTIVE_PROFILES_PROPERTY} and + * {@link ConfigurableEnvironment#addActiveProfile(String)} it needs to be + * filtered so that the {@link #ACTIVE_PROFILES_PROPERTY} value takes precedence. + *

+ * Concretely, if the "cloud" profile is enabled via the environment, it will take + * less precedence that any profile set via the {@link #ACTIVE_PROFILES_PROPERTY}. + * @param initialActiveProfiles the profiles that have been enabled via + * {@link #ACTIVE_PROFILES_PROPERTY} + * @return the unprocessed active profiles from the environment to enable + */ + private List getUnprocessedActiveProfiles( + Set initialActiveProfiles) { + List unprocessedActiveProfiles = new ArrayList(); + for (String profile : this.environment.getActiveProfiles()) { + if (!initialActiveProfiles.contains(profile)) { + unprocessedActiveProfiles.add(profile); + } + } + // Reverse them so the order is the same as from getProfilesForValue() + // (last one wins when properties are eventually resolved) + Collections.reverse(unprocessedActiveProfiles); + return unprocessedActiveProfiles; + } + private void load(String location, String name, String profile) throws IOException { String group = "profile=" + (profile == null ? "" : profile); @@ -387,10 +414,7 @@ public class ConfigFileApplicationListener propertySource = this.propertiesLoader.load(resource, group, name, profile); if (propertySource != null) { - maybeActivateProfiles( - propertySource.getProperty(ACTIVE_PROFILES_PROPERTY)); - addIncludeProfiles( - propertySource.getProperty(INCLUDE_PROFILES_PROPERTY)); + handleProfileProperties(propertySource); } } StringBuilder msg = new StringBuilder(); @@ -407,58 +431,37 @@ public class ConfigFileApplicationListener return propertySource; } - /** - * Return the active profiles that have not been processed yet. - *

If a profile is enabled via both {@link #ACTIVE_PROFILES_PROPERTY} and - * {@link ConfigurableEnvironment#addActiveProfile(String)} it needs to be - * filtered so that the {@link #ACTIVE_PROFILES_PROPERTY} value takes - * precedence. - *

Concretely, if the "cloud" profile is enabled via the environment, - * it will take less precedence that any profile set via the - * {@link #ACTIVE_PROFILES_PROPERTY}. - * @param initialActiveProfiles the profiles that have been enabled via - * {@link #ACTIVE_PROFILES_PROPERTY} - * @return the additional profiles from the environment to enable - */ - private List filterEnvironmentProfiles(Set initialActiveProfiles) { - List additionalProfiles = new ArrayList(); - for (String profile : this.environment.getActiveProfiles()) { - if (!initialActiveProfiles.contains(profile)) { - additionalProfiles.add(profile); - } - } - return additionalProfiles; + private void handleProfileProperties(PropertySource propertySource) { + Set activeProfiles = getProfilesForValue( + propertySource.getProperty(ACTIVE_PROFILES_PROPERTY)); + maybeActivateProfiles(activeProfiles); + Set includeProfiles = getProfilesForValue( + propertySource.getProperty(INCLUDE_PROFILES_PROPERTY)); + addProfiles(includeProfiles); } - private Set maybeActivateProfiles(Object value) { + private void maybeActivateProfiles(Set profiles) { if (this.activatedProfiles) { - if (value != null) { - this.debug.add("Profiles already activated, '" + value + if (!profiles.isEmpty()) { + this.debug.add("Profiles already activated, '" + profiles + "' will not be applied"); } - return Collections.emptySet(); + return; } - - Set profiles = getProfilesForValue(value); - activateProfiles(profiles); if (profiles.size() > 0) { + addProfiles(profiles); this.debug.add("Activated profiles " + StringUtils.collectionToCommaDelimitedString(profiles)); this.activatedProfiles = true; } - return profiles; - } - - private void addIncludeProfiles(Object value) { - Set profiles = getProfilesForValue(value); - activateProfiles(profiles); } private Set getProfilesForValue(Object property) { - return asResolvedSet((property == null ? null : property.toString()), null); + String value = (property == null ? null : property.toString()); + return asResolvedSet(value, null); } - private void activateProfiles(Set profiles) { + private void addProfiles(Set profiles) { for (String profile : profiles) { this.profiles.add(profile); if (!this.environment.acceptsProfiles(profile)) {