Spring boot profiles

Profiles in spring boot enable defining different set of configurations for different environments, such as development, test, and production.
Suppose you have have different database configurations for each environment, or different logging settings.
One way to switch between these configurations is to change the values manually when running the application.
Another way is using spring profiles, which can be switched according to the environment, without any code changes required and we will see how to do that in this article.

Defining beans

With spring boot profiles, you can define beans specific to a profile. This means that a bean will only be loaded if it belongs to a particular profile and that profile is active.

To make a bean part of a profile, add @Profile annotation with a string as shown below

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
@Component
@Profile("dev")
public class DevComponent {
@Value("${env}")
String environment;
@PostConstruct
public void log() {
System.out.println(environment);
}
}
@Component @Profile("dev") public class DevComponent { @Value("${env}") String environment; @PostConstruct public void log() { System.out.println(environment); } }
@Component
@Profile("dev")
public class DevComponent {

  @Value("${env}")
  String environment;
  @PostConstruct
  public void log() {
    System.out.println(environment);
  }
}

This means that this bean will only be loaded when dev profile is active.
You can say that profile is a way of grouping beans.

Let’s define another bean with qa profile.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
@Component
@Profile("qa")
public class QAComponent {
@Value("${env}")
String environment; @PostConstruct
public void log() {
System.out.println(environment);
}
}
@Component @Profile("qa") public class QAComponent { @Value("${env}") String environment; @PostConstruct public void log() { System.out.println(environment); } }
@Component
@Profile("qa")
public class QAComponent {

  @Value("${env}")
  String environment;   @PostConstruct
  public void log() {
    System.out.println(environment);
  }
}

You can also associate a bean with more than one profiles by providing profile names in an array as shown below

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
@Component
@Profile({"qa", "dev"})
public class QAComponent {
@Value("${env}")
String environment;
@PostConstruct
public void log() {
System.out.println(environment);
}
}
@Component @Profile({"qa", "dev"}) public class QAComponent { @Value("${env}") String environment; @PostConstruct public void log() { System.out.println(environment); } }
@Component 
@Profile({"qa", "dev"}) 
public class QAComponent { 
  @Value("${env}") 
  String environment;   

  @PostConstruct 
  public void log() { 
    System.out.println(environment); 
  } 
}

Profile specific properties

With Spring profile, you can not only register beans specific to active profile, but also load properties that belong to active profile.
Convention to define properties file is application-<profileName>.properties.

That is, we can create separate properties file for dev and qa profile as shown below.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
# application-dev.properties
env=In Dev environment
# application-dev.properties env=In Dev environment
# application-dev.properties
env=In Dev environment
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
# application-qa.properties
env=In QA environment
# application-qa.properties env=In QA environment
# application-qa.properties
env=In QA environment

Now, if dev profile is active,
1. DevComponent bean will be loaded.
2. application-dev.properties will be loaded.

Activating Profiles

To activate a profile, spring.profiles.active property is used, with its value as the name of profile that should be active.
Remember that you can activate more than one profiles at a time by providing the names of all the profiles separated by a comma.

There are multiple ways to set spring.profiles.active property, which are covered next.

1. In application.properties file

You can define spring.profiles.active property in application.properties file of spring boot application as shown below

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
#application.properties
server.port=8085
spring.profiles.active=qa
#application.properties server.port=8085 spring.profiles.active=qa
#application.properties

server.port=8085
spring.profiles.active=qa

When you will start the application with this configuration, following message will be printed

 

In QA environment

which shows that application-qa.properties file was loaded and QAComponent bean was registered in spring application context.

2. Using system property

You can set spring.profiles.active property using System.setProperty() method.
It should be set before starting spring boot application so that it should know which profile needs to be loaded as shown below

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
System.setProperty("spring.profiles.active", "qa");
SpringApplication.run(DemoApplication.class, args);
System.setProperty("spring.profiles.active", "qa"); SpringApplication.run(DemoApplication.class, args);
System.setProperty("spring.profiles.active", "qa");
SpringApplication.run(DemoApplication.class, args);

3. While running jar

You can also provide active profile while running spring boot application jar at the command line as shown below

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
java -jar demo-application.jar --spring.profiles.active=qa
java -jar demo-application.jar --spring.profiles.active=qa
java -jar demo-application.jar --spring.profiles.active=qa

You can also set active profile from command line with -D flag as below

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
java -Dspring.profiles.active=prod -jar demo-application.jar
java -Dspring.profiles.active=prod -jar demo-application.jar
java -Dspring.profiles.active=prod -jar demo-application.jar

4. In web.xml

If your application is a traditional web application that uses web.xml, then you can set the active profile as a context parameter as shown below

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<context-param>
<param-name>spring.profiles.active</param-name>
<param-value>dev</param-value>
</context-param>
<context-param> <param-name>spring.profiles.active</param-name> <param-value>dev</param-value> </context-param>
<context-param>
    <param-name>spring.profiles.active</param-name>
    <param-value>dev</param-value>
</context-param>

Activate profile with SpringApplicationBuilder

There is another way to set active profile, using SpringApplicationBuilder class.
This class is used to start a spring boot application and you can specify active profiles with its profiles() method as below

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
new SpringApplicationBuilder(DemoApplication.class).
profiles("dev").
run(args);
new SpringApplicationBuilder(DemoApplication.class). profiles("dev"). run(args);
new SpringApplicationBuilder(DemoApplication.class).
    profiles("dev").
    run(args);

You can activate multiple profiles by providing their names separated by a comma in profiles() method.

Activating Multiple Profiles

You can activate multiple profiles in Spring Boot at the same time.
Here are several ways to do this:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
--- Using application.properties ---
spring.profiles.active=dev,test
--- Using application.yml ---
spring:
profiles:
active: dev,test
--- Using command-line arguments --
java -jar your-application.jar --spring.profiles.active=dev,test
--- Using environment variables ---
export SPRING_PROFILES_ACTIVE=dev,test
--- Using application.properties --- spring.profiles.active=dev,test --- Using application.yml --- spring: profiles: active: dev,test --- Using command-line arguments -- java -jar your-application.jar --spring.profiles.active=dev,test --- Using environment variables --- export SPRING_PROFILES_ACTIVE=dev,test
--- Using application.properties ---
spring.profiles.active=dev,test

--- Using application.yml ---
spring:
  profiles:
    active: dev,test

--- Using command-line arguments --
java -jar your-application.jar --spring.profiles.active=dev,test

--- Using environment variables ---
export SPRING_PROFILES_ACTIVE=dev,test

When multiple profiles are active, Spring Boot will combine all the properties and beans from all active profiles.
If there are conflicts, the last profile in the list takes precedence.

So, if you run the application with both dev and test profiles active, it will:
1. Load the default properties from application.properties
2. Load and override properties from application-dev.properties
3. Load and override properties from application-test.properties

Getting active profile

If you want to know, which profile is active at any moment, then there are multiple ways for that too.

1. Using spring.profiles.active property

This is the same property, which is used to set the active profile.
You can get its value to determine which profile is active as shown below

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
@Component
public class Profiler {
@Value("${spring.profiles.active}")
String activeProfiles
}
@Component public class Profiler { @Value("${spring.profiles.active}") String activeProfiles }
@Component
public class Profiler {
  @Value("${spring.profiles.active}")
  String activeProfiles
}

Here, the variable activeProfiles will contain the name of profile.
If there are more than one profiles active, then they will be comma separated.

In the above example, if active profile has not been set, the  @Value will throw IllegalArgumentException and the application will not start.
To prevent this, we need to initialize this property with a blank value, if it is not available as below

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
@Component
public class Profiler {
@Value("${spring.profiles.active:}")
String activeProfiles
}
@Component public class Profiler { @Value("${spring.profiles.active:}") String activeProfiles }
@Component
public class Profiler { 
  @Value("${spring.profiles.active:}") 
  String activeProfiles 
}

2. Using environment

org.springframework.core.env.Environment class has a getActiveProfiles() method, which returns a string array containing the profiles, which are active for the current application.
It can be used as below

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
@Component
public class Profiler {
@Autowired
Environment e;
void printActiveProfiles() {
for(String p : e.getActiveProfiles()) {
// p contains active profile
}
}
}
@Component public class Profiler { @Autowired Environment e; void printActiveProfiles() { for(String p : e.getActiveProfiles()) { // p contains active profile } } }
@Component 
public class Profiler { 

  @Autowired
  Environment e;
  
  void printActiveProfiles() {
    for(String p : e.getActiveProfiles()) {
      // p contains active profile
    }     
  }
}

Default profile

When no profile is set, a default profile is active.
All the beans that do not have @Profile annotation, belong to the default profile.

A default profile can be also be set using spring.profiles.default property. This profile will be active, when no profile is set with spring.profiles.active property.

It is different from spring’s default profile. spring.profiles.default=test says that use test profile, when no other profile is active.

Logical operators in profiles

You can use following logical operators with @Profile to load beans conditionally as per profiles.

1. ! operator

@Profile("!qa") means that this bean will not be loaded if qa profile is active. Thus, it reverses the bean association with a profile.

2. && operator

@Profile("dev && qa") means that this bean will be registered only when both qa and dev profiles are active.
Two profiles can be activated as

spring.profiles.active=dev,qa

Similarly, @Profile("dev && !qa") means that this bean is loaded, when the at least dev profile is active and qa is not one of the active profiles.

3. || operator

@Profile("dev || qa") means that a bean with annotation will be registered, when one of the active profiles is either dev or qa.

Spring profile: Practical Example

Most applications have different databases configured for various environments such as production, development and qa.
Instead of changing database parameters every time, use spring profiles for switching database connections by creating data sources separately corresponding to each environment. Example,

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
@Profile("dev")
@Configuration
public class DevDBConfig {
@Value("${spring.datasource.url}")
private String url;
@Value("${spring.datasource.username}")
private String username;
@Value("${spring.datasource.password}")
private String password;
@Bean
public DataSource dataSource() {
return DataSourceBuilder
.create()
.url(url)
.username(username)
.password(password)
.build();
}
}
@Profile("prod")
@Configuration
public class ProdDBConfig {
@Value("${spring.datasource.url}")
private String url;
@Value("${spring.datasource.username}")
private String username;
@Value("${spring.datasource.password}")
private String password;
@Bean
public DataSource dataSource() {
return DataSourceBuilder
.create()
.url(url)
.username(username)
.password(password)
.build();
}
}
@Profile("dev") @Configuration public class DevDBConfig { @Value("${spring.datasource.url}") private String url; @Value("${spring.datasource.username}") private String username; @Value("${spring.datasource.password}") private String password; @Bean public DataSource dataSource() { return DataSourceBuilder .create() .url(url) .username(username) .password(password) .build(); } } @Profile("prod") @Configuration public class ProdDBConfig { @Value("${spring.datasource.url}") private String url; @Value("${spring.datasource.username}") private String username; @Value("${spring.datasource.password}") private String password; @Bean public DataSource dataSource() { return DataSourceBuilder .create() .url(url) .username(username) .password(password) .build(); } }
@Profile("dev")
@Configuration
public class DevDBConfig {

  @Value("${spring.datasource.url}")
  private String url;
  @Value("${spring.datasource.username}")
  private String username;
  @Value("${spring.datasource.password}")
  private String password;
  @Bean
  public DataSource dataSource() {
    return DataSourceBuilder
      .create()
      .url(url)
      .username(username)
      .password(password)
      .build();
  }
}

@Profile("prod")
@Configuration
public class ProdDBConfig {

  @Value("${spring.datasource.url}")
  private String url;
  @Value("${spring.datasource.username}")
  private String username;
  @Value("${spring.datasource.password}")
  private String password;
  @Bean
  public DataSource dataSource() {
    return DataSourceBuilder
      .create()
      .url(url)
      .username(username)
      .password(password)
      .build();
  }
}

Create application-prod.properties and application-dev.properties file having above db connection properties.
Simply set active profile and a datasource corresponding to it.

Profile Annotation For Test Cases

When writing test cases, profile annotations become particularly useful for managing different test configurations and scenarios.

You can write unit test classes for different environments by using @Profile annotation corresponding to the environment as shown below.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
@Profile({"test", "dev"})
class DevelopmentTestService {
// Implementation for both test and dev
}
@Profile("!production")
class NonProductionServiceTest {
// Implementation for non-production environments
}
@Profile("uat")
class UATServiceTest {
// Implementation for UAT environments
}
@Profile({"test", "dev"}) class DevelopmentTestService { // Implementation for both test and dev } @Profile("!production") class NonProductionServiceTest { // Implementation for non-production environments } @Profile("uat") class UATServiceTest { // Implementation for UAT environments }
@Profile({"test", "dev"})
class DevelopmentTestService {
// Implementation for both test and dev
}

@Profile("!production")
class NonProductionServiceTest {
// Implementation for non-production environments
}

@Profile("uat")
class UATServiceTest {
// Implementation for UAT environments
}

As shown in the above example, you can write unit tests for each environment that will be activated for that profile.

Important notes

1. If their are common keys in application.properties and application-profile.properties, then profile specific values will overwrite the main property values.
2. If there are common keys in all the profiles and multiple profiles are loaded comma-separated, then the last property value will overwrite previous values.
3. Beans that do not have @Profile annotation will be loaded with all profiles, even with a default profile.
4. Beans having @Profile annotation, will be loaded only when the corresponding profile is active.

In conclusion, profiles in spring boot provide a powerful way to configure your application for different environments, and make it easy to switch between configurations.
Hope the article was useful.

Categorized in:

Spring Boot,