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

@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.

@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

@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.

# application-dev.properties
env=In Dev 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.

Defining active 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

#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

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

java -jar demo-application.jar --spring.profiles.active=qa

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

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

<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

new SpringApplicationBuilder(DemoApplication.class).
    profiles("dev").
    run(args);

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

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

@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

@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

@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,

@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.

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.