Spring Profiles application properties order - spring

We have many environments that have multiple active Spring profiles, but what is the precedence of the application-{profile}.yml files?
If I have spring.profiles.active=test-us-west-2-p1, test-us-west-2, test
In what order do the files application-test.yml, application-test-us-west-2.yml, application-test-us-west-2-p1.yml get loaded? If I have the same property in each file, which "wins"?
Also, has this changed from Spring-Boot 1.5.x to 2.x? It seems like it may have.

The profile's properties are loaded in the same order as you specify them, and if the same property is defined in different profiles the last one wins.
This behavior applies to both Spring Boot versions 1.5.x and 2.x, and if I recall correctly, it applies to all versions of Spring.
Spring always loads appication.yml. And afterwards, if some profile is specified, it will load that profile's property file. And if after that profile another profile is specified, it will load that profile's propperty file. Always overriding current properties's value with the new one.
So, let's say you have profile1 and profile2. And you have these property files:
application.yml:
property1: bob
property2: alice
property3: eve
application-profile1.yml:
property2: alice1
property3: eve1
application-profile2.yml:
property3: eve2
And you start your application with: spring.profiles.active=profile1, profile2
Your will get:
property1: bob
property2: alice1
property3: eve2

First of all, we need to find out the final set of all active profiles. There are ways of setting/replacing active profiles and adding active profiles on top of existing active ones. For instance, active profiles set with the spring.profiles.active property are replaced with the -Dspring.profiles.active command line option. (And this can get really complex.)
On the other hand, the SpringApplicationBuilder's profiles method adds to the existing active profiles. We can use the following code to figure out the final set of active profiles:
#Autowired
private Environment environment;
...
System.out.println("Active profiles: " +
Arrays.toString(environment.getActiveProfiles()));
Now we have to consider what Spring documentation calls last-wins strategy.
If several profiles are specified, a last-wins strategy applies.
So, if we have the following code and all other options excluded:
new SpringApplicationBuilder(Application.class)
.profiles("dev", "prod")
.run(args);
both application-dev.properties and application-prod.properties files are loaded and the keys with the same name in the latter one (the production) override the former one.

I posted important notice on profiles order handling. See how jar resources files are handled in this process here.

Related

Spring boot tests - run tests 2 time with different application-xxx.properties each time

I have a spring boot app that has tests for database stuff and I'm supporting mysql and mssql.
I have src/text/resources/application-mysql.properties and src/text/resources/application-mssql.properties
What environment variable can I set when I run my tests to tell Spring which test properties file to use?
Property files in the format application-*.properties are activated using Spring Profiles. Same thing for YAML files, by the way! It is important to know that application.properties is still loaded first and any profile-specific properties will overwrite previously loaded properties (kind of the whole point of Spring Profiles).
There are multiple ways to enable profiles:
To answer your question, you can set the SPRING_PROFILES_ACTIVE environment variable to enable profiles. For example, export SPRING_PROFILES_ACTIVE=mysql. You can also specify multiple profiles (and they are loaded in the same order) by separating them with a comma: export SPRING_PROFILES_ACTIVE=localdefaults,local.
You can also use the JVM parameter, spring.profiles.active. The value follows the same format as that of the environment variable. For example, -Dspring.profiles.active=mysql.
You can use the #ActiveProfiles annotation on your test class. For example:
// Other annotations...
#ActiveProfiles("mysql")
public class MyTest {
If you want to enable profiles during a build, you can set the spring.profiles.active property in Maven. For example:
<profiles>
<profile>
<id>mysql</id>
<properties>
<spring.profiles.active>mysql</spring.profiles.active>
</properties>
</profile>
...
</profiles>
Here's a weird one I recently learned. You can also set active profiles with the spring.profiles.active in a properties file. I imagine this has its uses, but have never used this approach.
Read more about everything I have covered:
https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto.properties-and-configuration
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/context/ActiveProfiles.html

How to disable auto configuration when test profile in #SpringBootApplication?

Are there elegant ways to prevent AutoConfigure configuration classes when certain profile (e.g. test) in use? I've all configs in my tests, and don't wanna mark every config in main folder with #Profile("!test")
there also is: #SpringBootApplication(exclude=****Configuration.class) possibility but it's for class, not for profile.
What I did (but not for test) its to disable the AutoConfigure via properties file when a certain profile is enabled.
For instance, I don't want Redis to be auto-configured when profile "withoutRedis" is enabled (dev purpose).
I created a property file "application-withoutRedis.properties" and its content :
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration

Profile-specific spring.config.additional-location?

I have specified an external properties file to a Spring Boot app by setting spring.config.additional-location in the SpringApplicationBuilder.
new SpringApplicationBuilder(MyApplication)
.properties(['spring.config.additional-location': myExternalProperties])
.run(myArgs)
This works insofar as it allows me to override properties in the application.properties file using myExternalProperties.
However, myExternalProperties are in turn overridden by any profile-specific properties, e.g. application-myProfile.properties.
I understand this to be consistent with Spring's prioritization of Externalized Configuration, but I want myExternalProperties to override even profile-specific properties.
How can I achieve that order of priority?
I do not control the file name or location of myExternalProperties. This variable is a System property that is preset in the environment.
I have been looking at Profile-specific Properties and in particular this quote.
If you have specified any files in spring.config.location, profile-specific variants of those files are not considered. Use directories in spring.config.location if you want to also use profile-specific properties.
I assume this note applies equally to spring.config.additional-location, but without control over the property file name or location I don't think that helps me.
I'm afraid it cannot be achieved in a non-hacky way.
The docs state:
Profile-specific files always overriding the non-specific ones.
Also:
If several profiles are specified, a last-wins strategy applies. For example, profiles specified by the spring.profiles.active property are added after those configured through the SpringApplication API and therefore take precedence.
Could you consider using profile based configuration for myExternalProperties?
You can even use env vars as placeholders.
Hope this helps: Env vars in Spring boot properties

Multiple properties file for a single spring profile

We are using spring boot 2.0.0. We have three environments dev, staging, production. Our current config structure
dev
application-dev.yml
application-dev.properties
Likewise, we have a yml and properties file for each environment. After a year of development now the single yml file for a profile become a large monolithic config.
is it possible to have a multiple config files for a profile like below?
application-dev.yml
application-dev-sqs.yml
application-dev-redis.yml
I think there are 2 ways you can achieve this requirement.
spring.profiles.active accepts a comma-separated list of active profiles, so you can always provide dev,dev-sqs,dev-redis as the value.
Another approach is by making use of #PropertySource and a custom PropertySourceFactory to achieve this requirement. You can find an implementation which takes the value from spring.profiles.active to load one corresponding YAML file in the article below. It should be super easy to adapt the implementation to load multiple files by looking for the profile id in the name of the YAML files.
[How-to] Read profile-based YAML configurations with #PropertySource
I was dealing with a similar problem and I'd recommend using yaml configuration.
Let's describe .properties file:
Initital approach
One can use it like this:
#Component
#PropertySources({
#PropertySource("classpath:application.properties"),
#PropertySource("classpath:application-${spring.profiles.active}.properties")
})
public class AppProperties {
}
This is very easy to configure. Limitation is, that you cannot combine profiles. I mean, that when you want to use profile as dev,local where local just alters some config properties for dev profile, Spring will try to load application-dev,local.properties file, which is very likely not what you want.
Btw, this is what Spring will do for you automatically, this is useful for topics as you described.
There is no way to configure it per profile (and not for whole list). Other possibility would be, that one can specify the list in spring.config.name which is not the case at the moment.
Better approach
In short, use:
#Profile("dev")
#Configuration
#PropertySources({
#PropertySource("classpath:topic1-dev.properties"),
#PropertySource("classpath:topic2-dev.properties")
})
public class AppPropertiesDev {
}
Disadvantage is, you have to have several such config classes (dev, staging), but know you have the topics. Also you can use mutliple profiles, which are (as of my testing) loaded in order you specified. That way, your developer can easily use dev configuration and alter just what's needed for his/her testing.
Yaml approach
You can see the approach with yaml in question I asked earlier - Property resolving for multiple Spring profiles (yaml configuration), benefit is smaller amount of files - yaml has all the profiles in one file, which may or may not be what you want.
Yes, it's possible. spring.config.location is used to externalize the config file location in Spring boot applications. This can be used to provide a location of the file in the filesystem or even in the classpath. Based on how you want to provide your application access to the files, you can choose the URI.
Doing it programmatically:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = new SpringApplicationBuilder(Application.class)
.properties("spring.config.location:classpath:/application-dev.yml,classpath:/application-dev-sqs.yml,classpath:/application-dev-redis.yml")
.build()
.run(args);
}
}
Doing it via environment variables:
set SPRING_CONFIG_LOCATION=classpath:/application-dev.yml, \
classpath:/application-dev-sqs.yml, \
classpath:/application-dev-redis.yml
So, you can provide your files as comma-separated values.
I've used classpath here, it can also be a location in the file system:
/home/springboot-app/properties/application-dev.yml,/home/springboot-app/properties/application-sqs.yml,/home/springboot-app/properties/application-redis.yml
Have you tried including profiles yet ?
Example with profile default, you want to load additional properties for redis and db. Within application.properties file, add:
spring.profiles.include=redis, db
This will load files application-redis.properties and application-db.properties respectively

Spring boot, use profiles to load files

I have used spring-boot profiles to change property values for different environments, now I want to use the same approach to load different resource files, ie example I have dev_queries and prod_queries.xml with sql queries.
How can I make spring-boot load dev_queries.xml if active profile is dev and prod_queries.xml otherwise. I know that I can check the active profile but my idea is to do not add specific logic for handle this situation.
Would it help to externalize the filename as a custom property (docs, especially 24.4)? So that in your properties you would use:
# application-dev.properties
myapp.query-source=dev_queries.xml
# application-production.properties
myapp.query-source=prod_queries.xml
In your application beans this setting can be accessed by using the #Value annotation:
#Value("${myapp.query-source}")
private String querySource; // will be dev_queries.xml or prod_queries.xml
That way in the code where you are loading the xml file you don't have to conditionally check for the currently active profiles but can externalize that setting to the properties.

Resources