In any set of development frameworks, multi-environment management is usually one of the important core features, of course, in the Spring framework is no exception, here we call the Spring Profiles. This feature is simple to say, but it is easy to accidentally mess up the implementation, this article I intend to properly sort out some of the concepts to understand, the management up to not mess up.
Create sample applications
-
Quickly building a project using the Spring Boot CLI
1
spring init --dependencies=web --groupId=com.duotify sbprofile1
You can also use Spring Initializr to create
Open the project using Visual Studio Code.
1
code sbprofile1
-
Add a
HomeController
controllerFile path:
src/main/java/com/duotify/sbprofile1/controllers/HomeController.java
.1 2 3 4 5 6 7 8 9 10 11 12
package com.duotify.sbprofile1.controllers; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HomeController { @GetMapping("/") public String home() { return "Hello World"; } }
-
Run the application
1
mvn clean spring-boot:run
Addendum: You can add a new
<defaultGoal>spring-boot:run</defaultGoal>
setting under the<build>
node inpom.xml
, then you can just typemvn
and it will automatically start Spring Boot running in the future! 👍 -
Requesting applications using cURL
Understand the true meaning of Profiles
Since the Spring framework has a bunch of abstract concepts, there are a lot of devilish details that you can’t understand if you don’t take the time to study them. The Spring Profiles mentioned in this article are originally a very simple concept, but there are so many variations when writing Spring Boot that you can get your head in a knot.
Let’s start with the simplest and most abstract concept.
The so-called Profile usually has a name (Profile Name), and this name represents a set of application configurations. You can quickly switch between application configurations with a simple Profile Name, it’s that simple!
The application configuration contains two layers of meaning.
- Configuration is actually a property definition file like
src/main/resources/application.properties
. - Components combination means which “components” of the application are enabled, and you can decide what Profile to use to start the application during the running period with simple parameters.
The most common example of using a Profile to manage application configuration is for “multi-environment” deployments, such as when you have an internal “test environment” and a customer-provided “official environment”. The configuration of the two is usually different, but there are some similarities. At this point, we can manage these differences through multiple profiles. After abstraction, we can switch between the different environments as long as we know the Profile Name.
How to use Application Properties
Before you can understand how to manage multiple profiles, you should understand how Application Properties should be used.
The steps of the experiment are as follows.
-
edit the
src/main/resources/application.properties
property file to add amy.profile
property value1
my.profile=dev
-
Adjust the
HomeController
, add a Private Field, and inject amy.profile
attribute value via the@Value
annotationFile path:
src/main/java/com/duotify/sbprofile1/controllers/HomeController.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
package com.duotify.sbprofile1.controllers; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HomeController { @Value("${my.profile}") private String myProfile; @GetMapping("/") public String home() { return "Hello World: " + this.myProfile; } }
-
Testing
1
mvn clean spring-boot:run
Testing with cURL.
How to pass in attribute values via Maven, command line parameters, environment variables, .env
Sometimes we want to add property values to the application during the “compilation period” via Maven, and then we need a way to pass the properties defined by Maven’s pom.xml
into the src/main/resources/application.properties
property file.
The steps of the experiment are as follows.
-
edit the
src/main/resources/application.properties
property file.Add a
my.profile
property value.1
my.profile=@my.profile@
-
Adjust the
pom.xml
file and add a<my.profile>
attribute to<properties>
1
<my.profile>dev2</my.profile>
-
Testing
1
mvn clean spring-boot:run
Testing with cURL
The @my.profile@
syntax in the application.properties
properties file is unique in that it declares that you will read the property values from outside, and if Maven has defined properties, by default they will be added as default values when the project is compiled. This syntax also has the advantage of allowing you to Assign Value by various methods only during execution. For example.
-
Importing parameters directly from command line parameters
1
mvn clean spring-boot:run -Dmy.profile=dev3
-
Passing in attribute values directly from environment variables
Here is the syntax for setting environment variables in Bash.
1
my_profile=dev4 mvn clean spring-boot:run
When the environment variable encounters an attribute name with a decimal point (
.
), remember to convert to underscores (_
). -
First package it as a JAR file, and when run by
java -jar
you can also pass in parameters via command line arguments.1
mvn clean package
1
java -Dmy.profile=dev5 -jar target/sbprofile1-0.0.1-SNAPSHOT.jar
This
-Dmy.profile=dev5
parameter is passed to the JVM as a system parameter.Remember that
-Dmy.profile=dev5
must be set in front of-jar
! -
First package it as a JAR file, and when run by
java -jar
you can also pass in parameters via environment variables.1
mvn clean package
1
my_profile=dev6 java -jar target/sbprofile1-0.0.1-SNAPSHOT.jar
Remember that
-Dmy.profile=dev5
must be set in front of-jar
! -
Pass in the parameters directly from the environment variables defined by the
.env
fileFirst, add a
.env
file to the project root directory with the following contents.1
my_profile=dev7
Create a
.vscode/launch.json
startup configuration file (VSCode).The focus here is on the
envFile
setting.Press
F5
to start the project, and you can read the settings!
How to use Spring Profiles
After understanding how to use and configure Properties files, we can finally get to the main point of this article, which is how to define Spring Profiles profiles.
Here are the steps of the experiment.
-
edit
src/main/resources/application.properties
property fileAdd a
spring.profiles.active
attribute value.1
spring.profiles.active=@spring.profiles.active@
The
spring.profiles.active
on the left here is the name of the property that the Spring framework will use, while the@spring.profiles.active@
on the right is a property that can be passed in from external. -
Adjust Maven’s
pom.xml
configuration file and add the<profiles>
section settings.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
<profiles> <profile> <id>default</id> <activation> <activeByDefault>true</activeByDefault> </activation> <properties> <spring.profiles.active>default</spring.profiles.active> </properties> </profile> <profile> <id>dev8</id> <properties> <spring.profiles.active>dev8</spring.profiles.active> </properties> </profile> </profiles>
What is special about this configuration is that we define
2
Profiles, one is ourdefault
profile and the other is thedev8
profile. However, each of the different profiles has a specialspring.profiles.active
attribute defined (you can define multiple attributes), which is used specifically to give Spring applications a reference as to who the currently enabled profiles are.Remember: The properties you define in
pom.xml
(<properties>
) are not directly referenced by Java programs, they are related as follows.1
Java raw file <-- Application properties file (.properties/.yml) <-- External incoming properties (Maven properties / environment variables / command line parameters)
-
Modify the
@Value
annotation ofHomeController
to inject thespring.profiles.active
property instead1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
package com.duotify.sbprofile1.controllers; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HomeController { @Value("${spring.profiles.active}") private String myProfile; @GetMapping("/") public String home() { return "Hello World: " + this.myProfile; } }
-
Testing
Remember that we now have two profiles,
default
anddev8
. When we start the application withmvn spring-boot:run
, we can use-P
plus aProfileName
to enable the profile.1
mvn clean spring-boot:run -Pdev8
Note: The
P
in-P
here must be capitalized, and the name that follows is the<id>
element value inpom.xml
!Testing with cURL
If you try to pass in a non-existent
dev9
profile name, you will get the default profile.1
mvn clean spring-boot:run -Pdev9
Note: It is possible to have two Profiles enabled at the same time, separated by a comma with
-P
. For example, you can test the enabled Profile name with the following command:mvn help:active-profiles -Pdev,prod
Switching different application property files via Spring Profiles
Another advantage of using the Spring framework’s Profiles feature is that instead of setting the properties in Maven’s pom.xml
file, you can set the application properties in different .properties
files by using a special naming convention. Please see the comments for the following file name specifications.
|
|
Next we will experiment with the application of multiple profile.
-
Let’s add a
dev9
profile to thepom.xml
file.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
<profiles> <profile> <id>default</id> <activation> <activeByDefault>true</activeByDefault> </activation> <properties> <spring.profiles.active>default</spring.profiles.active> </properties> </profile> <profile> <id>dev8</id> <properties> <spring.profiles.active>dev8</spring.profiles.active> </properties> </profile> <profile> <id>dev9</id> <properties> <spring.profiles.active>dev9</spring.profiles.active> </properties> </profile> </profiles>
-
In addition to
application.properties
, we create two additional application property filesFile 1:
src/main/resources/application.properties
(add amy.name
attribute)File 2:
src/main/resources/application-dev8.properties
(add amy.name
attribute)1
my.name=John
File 3:
src/main/resources/application-dev9.properties
(empty content) -
Modify the
@Value
annotation ofHomeController
to inject thespring.profiles.active
property instead1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
package com.duotify.sbprofile1.controllers; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HomeController { @Value("${spring.profiles.active}") private String myProfile; @Value("${my.name}") private String myName; @GetMapping("/") public String home() { return "Hello World: " + this.myName; } }
-
Testing
Remember that we now have
3
profiles,default
,dev8
anddev9
.First, try not specifying a Profile.
1
mvn clean spring-boot:run
Then, try to specify Profile
dev8
.1
mvn clean spring-boot:run -Pdev8
Finally, try to specify Profile
dev9
.1
mvn clean spring-boot:run -Pdev9
Loading different dependent packages via Spring Profiles
The configuration of Spring Profiles not only allows you to set “attributes”, but also allows you to load different <dependencies>
dependent packages according to different profiles, such as loading different versions of the same package (testing old and new versions), or the same interface but different packages (different database drivers). This is really great! 👍
-
The following are examples of settings for different versions of the same package.
1 2 3 4 5 6 7 8 9 10 11 12 13
<profile> <id>dev8</id> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.7.0</version> </dependency> </dependencies> <properties> <spring.profiles.active>dev8</spring.profiles.active> </properties> </profile>
If you want to see the dependent package information after applying a different Profile, you can run the following command.
1
mvn dependency:tree -Pdev8
-
The following are examples of settings for same interface with different packages.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
<profiles> <profile> <id>Local</id> <dependencies> <dependency> <groupId>org.hsqldb</groupId> <artifactId>hsqldb</artifactId> <version>2.3.3</version> <classifier>jdk5</classifier> </dependency> </dependencies> <properties> <jdbc.url>jdbc:hsqldb:file:databaseName</jdbc.url> <jdbc.username>a</jdbc.username> <jdbc.password></jdbc.password> <jdbc.driver>org.hsqldb.jdbcDriver</jdbc.driver> </properties> </profile> <profile> <id>MySQL</id> <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.38</version> </dependency> </dependencies> <properties> <jdbc.url>jdbc:mysql://mysql.website.ac.uk:3306</jdbc.url> <jdbc.username>user</jdbc.username> <jdbc.password>1234</jdbc.password> <jdbc.driver>com.mysql.jdbc.Driver</jdbc.driver> </properties> </profile> </profiles>
Loading different Beans components via Spring Profiles
Under the Spring framework, all classes annotated with @Component
are registered as Beans components, including, of course, classes annotated with @Configuration
, since they inherit from the @Component
interface.
However, you can simply add the @Profile
annotation to the class to declare that Spring is going to load the class under a specific Profile.
Here is an example of how to use it.
-
Create a
UserService
class -
Create a
UserServiceDev
class1 2 3 4 5 6 7 8 9 10 11 12 13 14
package com.duotify.sbprofile1.services; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Component; @Component @Profile("dev") public class UserServiceDev { @Bean public UserService getUserService() { return new UserService("Dev"); } }
This
UserServiceDev
will only be executed by Spring when thedev
profile is enabled. -
Create a
UserServiceProd
class1 2 3 4 5 6 7 8 9 10 11 12 13 14
package com.duotify.sbprofile1.services; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Component; @Component @Profile("!dev") public class UserServiceProd { @Bean public UserService getUserService() { return new UserService("Prod"); } }
This
UserServiceDev
will only be executed by Spring when the non-dev
profile is enabled. -
Modify the
HomeController
and inject theUserService
service via “constructive”1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
package com.duotify.sbprofile1.controllers; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import com.duotify.sbprofile1.services.UserService; @RestController public class HomeController { @Value("${spring.profiles.active}") private String myProfile; @Value("${my.name}") private String myName; private UserService svc; public HomeController(UserService svc) { this.svc = svc; } @GetMapping("/") public String home() { return "Hello World: " + this.svc.getName(); } }
-
Modify
pom.xml
and add two more<profile>
definitions -
Testing
Remember that we now have
5
profiles, namelydefault
,dev8
,dev9
,dev
andprod
.Try to specify Profile
dev
.1
mvn clean spring-boot:run -Pdev
Try to specify Profile
prod
.1
mvn clean spring-boot:run -Pprod
Another practical example can be found in the example provided here.
Because Maven Profiles are so extensive, there are many more uses that can be found in the Guide to Maven Profiles.
Reference https://blog.miniasp.com/post/2022/09/21/Mastering-Spring-Boot-Profiles