Spring "Spring. Profiles. Include" Overrides
My Intention Is to Have Two Profiles in a Spring Boot Application - Development and Production One. Development Profile Is Meant Just to Override Some...
My intention is to have two profiles in a spring boot application - development and production one. Development profile is meant just to override some variables of production profile (like in-memory database instead of database in the cloud). As I expect some changes to be done to production profile in the future, duplicating variables in development profile doesn't seem to be a solution.
So, in Spring Reference I read that spring.profiles.include is supposed to only add properties from referenced profile.
Sometimes, it is useful to have profile-specific properties that add to the active profiles rather than replace them. The
spring.profiles.includeproperty can be used to unconditionally add active profiles.
However, from what I've checked it rather overrides it. So, when having two profiles foo and bar, in separate yaml files:
application-foo.yaml:
myproperty: 44
application-bar.yaml:
spring:
profiles:
include: foo
active: bar,foo
myproperty: 55
And setting -Dspring.profiles.active=bar variable in IDE, the runtime value of myproperty is 44. That means that bar, is overriden with foo which was supposed to only add properties, but not to override them. When starting the application, I get:
The following profiles are active: foo,bar
I added spring.profiles.active=bar to application-bar.yaml as suggested by this answer, in another question, but it has no effect - there is no difference when property is there or not (I also tried using dash listing instead of comma separated values).
My question is, is it how it is supposed to work (then Spring Reference is misleading)? If so, are there any solutions for that?
Adding a link to the application source code on a github.
5 Answers
We implemented the Spring active profiles in a slightly different way. Let's say the default properties file, application.yml, contains all default values which is same in both production and development environments.
Create separate properties for production and development files named application-prd.yml and application-dev.yml respectively. These files may contain extra properties or override some of the default properties.
During application startup, we pass the spring.profiles.active as an environment variable. For example,
-Dspring.profiles.active=prd
will pick up application-prd.yml along with application.yml
or
-Dspring.profiles.active=dev
will pick up application-dev.yml along with application.yml
According to the spring boot documentation here, spring.profiles.include is used to add the properties from other profiles. It will add the property from other profiles if the property is not present in active one. But if it is present, then it will overwrite and the last one to be applied wins
Sometimes, it is useful to have profile-specific properties that add to the active profiles rather than replace them. The
spring.profiles.includeproperty can be used to unconditionally add active profiles.
You could add a new profile in the application-bar.yaml:
spring.profiles.include: foo,foo-override
myproperty: 33
---
spring.profiles: foo-override
myproperty: 55
The order is: 33 in bar overridden by 44 in foo overridden by 55 in foo-override.
Spring Boot 2.4 changes the mechanism for including multiple profiles to use a new profile groups feature, rather than using spring.profiles.include within the profile-specific document. This means that your configuration is no longer valid for new versions of Spring Boot, and would need to be changed.
That said, your use case does not seem to be a great fit for profile groups, as it's not really combining two profiles so much as overriding defaults. Therefore, I recommend using the approach suggested in another answer of putting the common and default properties in a shared application.yaml file, and only including the environment-specific values & overrides in the profile-specific documents.
application.yaml
spring:
myproperty: 44 # Default value
application-bar.yaml
spring:
myproperty: 55 # Override default
Note that Spring Boot supports multi-document files, so these can be combined into a single application.yaml file if desired:
spring:
myproperty: 44 # Default value
---
spring.config.activate.on-profile: bar # These configs apply to the bar profile
spring:
myproperty: 55 # Override default
Must Read
Relevant 2.4 Changes
It is no longer possible to use spring.profiles.include within a profile-specific document as of Spring Boot 2.4, unless legacy mode is enabled using spring.config.use-legacy-processing=true. Per the 2.4 Spring Boot Config Data Migration Guide:
you can still use the spring.profiles.include property, but only in non profile-specific documents.
This approach has been replaced by the profile groups feature. Per the migration guide:
As discussed above, it’s no longer possible to use
spring.profiles.includein a profile-specific document so this file isn’t valid.Since this use-case is quite common, we’ve tried to provide another way to support it. In Spring Boot 2.4 you can use the “profile groups” feature.
This feature is documented in the Profile Groups section of the Spring Boot reference guide:
A profile group allows you to define a logical name for a related group of profiles.
For example, we can create a
productiongroup that consists of ourproddbandprodmqprofiles.spring: profiles: group: production: - "proddb" - "prodmq"Our application can now be started using
--spring.profiles.active=productionto active theproduction,proddbandprodmqprofiles in one hit.
The migration guide points out that the spring.profile.group property cannot be used in profile-specific documents.
The
spring.profile.groupproperty cannot be used in profile-specific documents.
Given:
- The files:
application-default.yml, application-foo.yml, application-bar.yml myproperty: defaultin application-default.ymlmyproperty: fooin application-foo.ymlmyproperty: barin application-bar.yml
I think these 2 use cases of using profiles are a little bit opposite in the meaning:
In the most common case (
-Dspring.profiles.activebut nospring.profiles.include):- When profile foo or boo are activated the properties from application-foo.yml (or application-bar.yml) will add/override the ones from application-default.yml.
- When profiles foo,bar are activated then properties from bar will add/override those from application-foo.yml and then those from application-default.yml.
E.g:
-Dspring.profiles.active=foo,barthe property from application-bar.yml wins (overrides) ->myproperty: barIn the second case (
spring.profiles.includeis used)- The properties from the include statement add/overrides the properties from the application-*.yml files which uses
spring.profiles.include
I.e.: If
application-boo.ymlcontains thespring.profiles.include=foothen properties fromapplication-foo.bar adds/override properties from from application-bar.ymlwhich add/override those from application-default.yml.On the other hand (I suppose) if application-boo.yml includes the
spring.profiles.include=default,foothen properties fromapplication-foo.ymlwill add/override those fromapplication-default.ymlwhich add/overrides those fromapplication-bar.yml. Somyproperty: bar. I wouldn't recommend the usage ofdefaultin combination withspring.profiles.includebecause this way it mixes the two cases and the override strategy is counterintuitive consideringapplication-default.ymlhas a special treatment in springboot.- The properties from the include statement add/overrides the properties from the application-*.yml files which uses
I also admit I am not at all a fan of the spring.profiles.active usage in application-*.yml files. I prefer to activate the profiles with system properties (maven included) or env variables. IMO it makes the whole profiles thing clearer to me.
If with my (herein above)reasoning I am on the wrong path please let me know.