Skip to content

SpringApplication additional profiles and active profiles ordering changed with Spring Boot 2.4 #26189

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
mzeijen opened this issue Apr 21, 2021 · 5 comments
Assignees
Labels
type: bug A general bug
Milestone

Comments

@mzeijen
Copy link
Contributor

mzeijen commented Apr 21, 2021

Spring Boot version: 2.4

After upgrading to Spring Boot 2.4 from Spring Boot 2.3 I noticed that the ordering has changed of how SpringApplication.additionalProfiles are merged with the ConfigurableEnvironment.getActiveProfiles().

With Spring Boot 2.3 the ordering is: additional profiles, active profiles
With Spring Boot 2.4 the ordering is: active profiles, additional profiles

When running the test application below with Spring Boot 2.3 and Spring Boot 2.4 you can see this easily in the logging:

Spring Boot 2.3.10.RELEASE profiles ordering: additional, active
Spring Boot 2.4.5 profiles ordering: active, additional

This ordering change also affects the @ActiveProfiles test annotation. The profiles activated via the annotation now also have a lesser priority then the additional profiles (if you are wondering how we are setting additional profiles in combination with running tests, this is because we are actually using a ApplicationStartingEvent ApplicationListener, registered via the spring.factories to call the SpringApplication.setAdditionalProfiles() with an environment specific profile).

This change has the effect that our application-{profile}.yml property sources are now getting different precedence, which caused issues for us when running tests, because property values where not being overridden in the expected order anymore.

I wonder if this change in ordering was an intentional one or that it is a mistake. I couldn't find any remarks regarding the change in the changelog. I have also read the https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-Config-Data-Migration-Guide but I couldn't distill this information from that document as well. If the ordering change is intentional then I recommend adding a note in the migration guide.

Test code:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.env.AbstractEnvironment;
import org.springframework.core.env.Environment;

@SpringBootApplication
public class AdditionalProfilesReproductionApplication implements CommandLineRunner {

    public static void main(String[] args) {
        System.setProperty(AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME, "active");

        SpringApplication app = new SpringApplication(AdditionalProfilesReproductionApplication.class);
        app.setAdditionalProfiles("additional");
        app.run(args);
    }

    @Autowired
    Environment environment;

    @Override
    public void run(String... args) throws Exception {
        System.out.println("Profiles: " + String.join(", ", environment.getActiveProfiles()));
    }
}
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Apr 21, 2021
@nguyensach
Copy link
Contributor

@mzeijen
When I did this PR, I realized this issue like you.
But I don't know whether this ordering change was an intentional one or that it is a mistake.

You can fix this issue by using legacy processing

@wilkinsona
I think that this ordering change is due to bellow.

  • With Spring Boot 2.4 without using legacy processing

this.activeProfiles = expandProfiles(getActivatedProfiles(environment, binder, additionalProfiles));

  • With Spring Boot 2.3 or Spring Boot 2.4 using legacy processing

Set<Profile> activatedViaProperty = getProfiles(binder, ACTIVE_PROFILES_PROPERTY);
Set<Profile> includedViaProperty = getProfiles(binder, INCLUDE_PROFILES_PROPERTY);
List<Profile> otherActiveProfiles = getOtherActiveProfiles(activatedViaProperty, includedViaProperty);
this.profiles.addAll(otherActiveProfiles);
// Any pre-existing active profiles set via property sources (e.g.
// System properties) take precedence over those added in config files.
this.profiles.addAll(includedViaProperty);
addActiveProfiles(activatedViaProperty);

How would you think about this issue?

@mzeijen
Copy link
Contributor Author

mzeijen commented Apr 22, 2021

@nguyensach
I tried activating legacy processing (by setting spring.config.use-legacy-processing to true in the application.properties or via the System properties before starting the application) but that didn't restore the old behaviour.

From what I can see the ordering of the additional profiles and the active profiles happens in the SpringApplication, and I don't see that the legacy processing has any influence on it.

In Spring Boot 2.3 it is done in the configureProfiles method:

protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}

In Spring Boot 2.4 it is done in the configureAdditionalProfiles method:

private void configureAdditionalProfiles(ConfigurableEnvironment environment) {
if (!CollectionUtils.isEmpty(this.additionalProfiles)) {
Set<String> profiles = new LinkedHashSet<>(Arrays.asList(environment.getActiveProfiles()));
if (!profiles.containsAll(this.additionalProfiles)) {
profiles.addAll(this.additionalProfiles);
environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}
}
}

The difference is that in Spring Boot 2.3 the list of profiles is created with this.additionalProfiles as initial fill where in Spring Boot 2.4 the environment.getActiveProfiles() is used as initial fill.

@nguyensach
Copy link
Contributor

@mzeijen I'm so sorry!

According to the below test case, it seems this ordering change was an intentional one.

@Test
void addProfilesOrder() {
SpringApplication application = new SpringApplication(ExampleConfig.class);
application.setWebApplicationType(WebApplicationType.NONE);
application.setAdditionalProfiles("foo");
ConfigurableEnvironment environment = new StandardEnvironment();
application.setEnvironment(environment);
this.context = application.run("--spring.profiles.active=bar,spam");
// Since Boot 2.4 additional should always be last
assertThat(environment.getActiveProfiles()).containsExactly("bar", "spam", "foo");
}

@nguyensach
Copy link
Contributor

@mzeijen

Although since Spring Boot 2.4 this ordering change was an intentional one, it needs to restore the old behavior when using legacy processing.

If my PR #25817 is merged, it will restore the old behavior when using legacy processing as the bellow test case.

https://github.com/spring-projects/spring-boot/pull/25817/files#diff-82a4d071283a2194fc5c0e8cffbdddbeda4e53d4e5676138f93b46fcaa16e516R1184-R1193

@mzeijen
Copy link
Contributor Author

mzeijen commented Apr 23, 2021

@nguyensach
Thanks for clearing that up.
Can you also make sure the intentional ordering change is documented in the changelog so that it doesn't surprise others like me?
Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug A general bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants