What is the order of precedence when there are multiple Spring's environment profiles as set by spring.profiles.active
SpringSpring ProfilesSpring Problem Overview
I am just wondering what the order of precedence is when multiple Spring active profiles have been specified.
Say I want the default
profile to be active but the dev
profile to override it when there are several identical elements (beans for instance) to choose from but with different profiles...
Say for instance I have two PropertySourcesPlaceholderConfigurer
beans configured with "default"
and "dev"
values a environment profiles.
If I use the following profile activation: -Dspring.profiles.active="default,dev"
Will the dev
profile override the default
one?
If not how can the above behavior be achieved?
Spring Solutions
Solution 1 - Spring
The order of the profiles in the spring.profiles.active
system property doesn't matter. "Precedence" is defined by the declaration order of the beans, including beans specific to a profile, and the last bean definition wins.
Using your example, if -Dspring.profiles.active="default,dev"
is used, the props
bean in the default
profile would be used here, simply because it's the last active definition of that bean:
<beans profile="dev">
<bean id="props" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="location" value="classpath:META-INF/dev.properties"/>
</bean>
</beans>
<beans profile="default">
<bean id="props" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="location" value="classpath:META-INF/default.properties"/>
</bean>
</beans>
Invert the order of the beans, and then the dev
version would be used, regardless of how the profiles are ordered in spring.profiles.active
.
Notice that I did not use <context:property-placeholder/>
because it does not allow you to explicitly specify a bean id, and so I'm not sure what behavior it would exhibit if more than one is used. I imagine that the properties would be merged, so that properties defined by both would use the last definition, but properties specific to each file would remain intact.
Otherwise, in my experience, you would typically define beans in this order:
- "Default" bean definitions, not specific to a profile
- Overriding bean definitions in an environment-specific profile
- Overriding bean definitions in a test-specific profile
This way, test profile beans would win if used in combination with other profiles; else you would either use environment-specific beans or default beans based on the profile.
Solution 2 - Spring
The last definition wins. I keep it in mind but:
It is very important to remember that if you have some default content of application.properties inside jar resources, then this resource content will overwrite entries from external content of less important profiles (other profiles defined earlier in spring.profiles.active
).
Example profiles: spring.profiles.active=p1,p2,p3
Files in Jar resources: application-p1.properties
and application-p3.properties
External files: application-p1.properties
and application-p2.properties
Final order will be (last wins):
- resource
application.properties
- external
application.properties
- resource
application-p1.properties
- external
application-p1.properties
- external
application-p2.properties
- resource
application-p3.properties
- HERE IS THE TRICK! this will overwrite properties defined in external files for p1 and p2 with values from resource version of p3 - external
application-p3.properties
So keep in mind that last wins but also that resource goes just before external
Solution 3 - Spring
superEB is right the order of the profiles doesn't matter for beans, the declaration order is more important there, but keep in mind that the order is important if you use profile based configuration files!
Solution 4 - Spring
I had to experiment to convince myself.
Create the most simple spring boot application from Spring Initializr
Then added 3 properties files to resources dir (first one is already there but empty)
# application.properties
foo=foo in application.properties
bar=bar in application.properties
baz=baz in application.properties
# application-foobar.properties
foo=foo in foobar override properties
bar=bar in foobar override properties
# application-barbaz.properties
bar=bar in barbaz override properties
baz=bar in barbaz override properties
Then I added this @Config class to run at startup:
package com.example.profilesexperiment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
@Configuration
class StartupConfig {
@Autowired
private Environment environment;
@Value("${foo}")
private String foo;
@Value("${bar}")
private String bar;
@Value("${baz}")
private String baz;
@Bean
CommandLineRunner startup() {
return args -> {
System.err.println("Active profiles: " + String.join(", ", environment.getActiveProfiles()));
System.err.println("Foo = " + foo);
System.err.println("Bar = " + bar);
System.err.println("Baz = " + baz);
};
}
}
Then I ran it with different combinations of profiles. You can try that yourself, but here are the outputs of some:
just foobar
java -Dspring.profiles.active=foobar -jar target/profiles-experiment-0.0.1-SNAPSHOT.jar
Active profiles: foobar
Foo = foo in foobar override properties
Bar = bar in foobar override properties
Baz = baz in application.properties
foobar then barbaz
java -Dspring.profiles.active=foobar,barbaz -jar ...
Active profiles: foobar, barbaz
Foo = foo in foobar override properties
Bar = bar in barbaz override properties
Baz = bar in barbaz override properties
barbaz then foobar
java -Dspring.profiles.active=barbaz,foobar -jar ...
Active profiles: barbaz, foobar
Foo = foo in foobar override properties
Bar = bar in foobar override properties
Baz = bar in barbaz override properties
Verdict: clearly, last one wins!
Oh, and also: non-overridden properties merge to one big happy property set (which is the reason I came searching here)