diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java index c143ad5f3c..60a5d6dc76 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java @@ -372,7 +372,6 @@ public class SpringApplication { ConfigurationPropertySources.attach(environment); listeners.environmentPrepared(bootstrapContext, environment); DefaultPropertiesPropertySource.moveToEnd(environment); - configureAdditionalProfiles(environment); Assert.state(!environment.containsProperty("spring.main.environment-prefix"), "Environment prefix cannot be set via properties."); bindToSpringApplication(environment); @@ -556,16 +555,6 @@ public class SpringApplication { protected void configureProfiles(ConfigurableEnvironment environment, String[] args) { } - private void configureAdditionalProfiles(ConfigurableEnvironment environment) { - if (!CollectionUtils.isEmpty(this.additionalProfiles)) { - Set profiles = new LinkedHashSet<>(Arrays.asList(environment.getActiveProfiles())); - if (!profiles.containsAll(this.additionalProfiles)) { - profiles.addAll(this.additionalProfiles); - environment.setActiveProfiles(StringUtils.toStringArray(profiles)); - } - } - } - private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) { if (System.getProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME) == null) { Boolean ignore = environment.getProperty("spring.beaninfo.ignore", Boolean.class, Boolean.TRUE); diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessor.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessor.java index 2c11110fc1..7df92a2548 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessor.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessor.java @@ -19,6 +19,8 @@ package org.springframework.boot.context.config; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Set; import java.util.function.Supplier; import org.apache.commons.logging.Log; @@ -34,6 +36,8 @@ import org.springframework.core.env.Environment; import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.ResourceLoader; import org.springframework.core.log.LogMessage; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; /** * {@link EnvironmentPostProcessor} that loads and applies {@link ConfigData} to Spring's @@ -41,6 +45,7 @@ import org.springframework.core.log.LogMessage; * * @author Phillip Webb * @author Madhura Bhave + * @author Nguyen Bao Sach * @since 2.4.0 */ public class ConfigDataEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered { @@ -99,6 +104,7 @@ public class ConfigDataEnvironmentPostProcessor implements EnvironmentPostProces catch (UseLegacyConfigProcessingException ex) { this.logger.debug(LogMessage.format("Switching to legacy config file processing [%s]", ex.getConfigurationProperty())); + configureAdditionalProfiles(environment, additionalProfiles); postProcessUsingLegacyApplicationListener(environment, resourceLoader); } } @@ -109,6 +115,15 @@ public class ConfigDataEnvironmentPostProcessor implements EnvironmentPostProces additionalProfiles, this.environmentUpdateListener); } + private void configureAdditionalProfiles(ConfigurableEnvironment environment, + Collection additionalProfiles) { + if (!CollectionUtils.isEmpty(additionalProfiles)) { + Set profiles = new LinkedHashSet<>(additionalProfiles); + profiles.addAll(Arrays.asList(environment.getActiveProfiles())); + environment.setActiveProfiles(StringUtils.toStringArray(profiles)); + } + } + private void postProcessUsingLegacyApplicationListener(ConfigurableEnvironment environment, ResourceLoader resourceLoader) { getLegacyListener().addPropertySources(environment, resourceLoader); diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java index 0e931b933a..5688ad8182 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java @@ -146,6 +146,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; * @author Brian Clozel * @author Artsiom Yudovin * @author Marten Deinum + * @author Nguyen Bao Sach */ @ExtendWith(OutputCaptureExtension.class) class SpringApplicationTests { diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessorTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessorTests.java index 40135750e7..9fead93f55 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessorTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessorTests.java @@ -48,6 +48,7 @@ import static org.mockito.Mockito.verifyNoInteractions; * * @author Phillip Webb * @author Madhura Bhave + * @author Nguyen Bao Sach */ @ExtendWith(MockitoExtension.class) class ConfigDataEnvironmentPostProcessorTests { @@ -105,6 +106,15 @@ class ConfigDataEnvironmentPostProcessorTests { assertThat(this.additionalProfilesCaptor.getValue()).containsExactly("dev"); } + @Test + void postProcessEnvironmentWhenNoActiveProfiles() { + willReturn(this.configDataEnvironment).given(this.postProcessor).getConfigDataEnvironment(any(), any(), any()); + this.postProcessor.postProcessEnvironment(this.environment, this.application); + verify(this.postProcessor).getConfigDataEnvironment(any(), this.resourceLoaderCaptor.capture(), any()); + verify(this.configDataEnvironment).processAndApply(); + assertThat(this.environment.getActiveProfiles()).isEmpty(); + } + @Test void postProcessEnvironmentWhenUseLegacyProcessingSwitchesToLegacyMethod() { ConfigDataEnvironmentPostProcessor.LegacyConfigFileApplicationListener legacyListener = mock( @@ -115,6 +125,21 @@ class ConfigDataEnvironmentPostProcessorTests { this.postProcessor.postProcessEnvironment(this.environment, this.application); verifyNoInteractions(this.configDataEnvironment); verify(legacyListener).addPropertySources(eq(this.environment), any(DefaultResourceLoader.class)); + assertThat(this.environment.getActiveProfiles()).isEmpty(); + } + + @Test + void postProcessEnvironmentWhenHasAdditionalProfilesAndUseLegacyProcessing() { + this.application.setAdditionalProfiles("dev"); + ConfigDataEnvironmentPostProcessor.LegacyConfigFileApplicationListener legacyListener = mock( + ConfigDataEnvironmentPostProcessor.LegacyConfigFileApplicationListener.class); + willThrow(new UseLegacyConfigProcessingException(null)).given(this.postProcessor) + .getConfigDataEnvironment(any(), any(), any()); + willReturn(legacyListener).given(this.postProcessor).getLegacyListener(); + this.postProcessor.postProcessEnvironment(this.environment, this.application); + verifyNoInteractions(this.configDataEnvironment); + verify(legacyListener).addPropertySources(eq(this.environment), any(DefaultResourceLoader.class)); + assertThat(this.environment.getActiveProfiles()).containsExactly("dev"); } @Test diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigFileApplicationListenerLegacyReproTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigFileApplicationListenerLegacyReproTests.java index 9c27a0f61f..6c3b1cfa64 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigFileApplicationListenerLegacyReproTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigFileApplicationListenerLegacyReproTests.java @@ -33,6 +33,7 @@ import static org.assertj.core.api.Assertions.assertThat; * * @author Phillip Webb * @author Dave Syer + * @author Nguyen Bao Sach */ @ExtendWith(UseLegacyProcessing.class) class ConfigFileApplicationListenerLegacyReproTests { 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 f0ffcc0dc6..44ad34345e 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 @@ -70,6 +70,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException; * @author EddĂș MelĂ©ndez * @author Madhura Bhave * @author Scott Frederick + * @author Nguyen Bao Sach */ @Deprecated @ExtendWith({ OutputCaptureExtension.class, UseLegacyProcessing.class }) @@ -1150,6 +1151,25 @@ class ConfigFileApplicationListenerTests { assertThat(this.environment.getProperty("fourth.property")).isNull(); } + @Test + void additionalProfilesCanBeIncludedFromProgrammaticallySetting() { + // gh-25704 + SpringApplication application = new SpringApplication(Config.class); + application.setWebApplicationType(WebApplicationType.NONE); + application.setAdditionalProfiles("other", "dev"); + this.context = application.run(); + assertThat(this.context.getEnvironment().getProperty("my.property")).isEqualTo("fromdevpropertiesfile"); + } + + @Test + void activeProfilesShouldTakePrecedenceOverAdditionalProfiles() { + SpringApplication application = new SpringApplication(Config.class); + application.setWebApplicationType(WebApplicationType.NONE); + application.setAdditionalProfiles("foo"); + this.context = application.run("--spring.profiles.active=bar,spam"); + assertThat(this.context.getEnvironment().getActiveProfiles()).containsExactly("foo", "bar", "spam"); + } + private Condition matchingPropertySource(final String sourceName) { return new Condition("environment containing property source " + sourceName) {