In Spring Framework, profiles are logical groups of bean definitions that are registered only if the given profile is active. It is possible to assign a bean with multiple Profiles, separated with logical operations like not (!), and (&) and or (|) etc. Profiles in Spring are specified with the @Profile annotation in Spring.

It is possible to use and activate @Profile annotations in several ways, we will explore each of them in detail.

You only need the Spring-core and Spring-context dependencies to make this example work.

  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
    </dependency>
  </dependencies>

Optionally you can specify the test dependencies

   <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-api</artifactId>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.junit.platform</groupId>
      <artifactId>junit-platform-runner</artifactId>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <scope>test</scope>
    </dependency>

When to use Spring @Profile annotation

Before learning to use, let us understand why you need the annotation.

  • Lets say you may want to load different set of configurations based on the environment. e.g. In memory datasource for Testing, MySql for Production. So you can load different dataSource beans based on the profile values.
  • Another common example is to load different application.properties files based on the environemnt. e.g. application-dev.properties for dev, application-test.properties for test and so on.

There are several ways in which @Profile annotation can be used and several ways in which a specific profile can be activated.

1. Use @Profile with stereotypes on class level

Consider a use case when you have two dataSources, RestDataSource and GraphQlDataSource. You want to activate only one of them based on the profile value.

You can see in the below code, the GraphQlDataSource has the profile graphQl. But the RestDataSource has 2 profile values rest and default. The default profile is activated when no profile value is set in the application.

@Component
@Profile("graphQl")
public class GraphQlDataSource implements DataSource {
  @Override
  public String getName() {
    return "GraphQlDataSource";
  }
}
@Component
@Profile({"rest", "default"}) //multiple profiles
public class RestDataSource implements DataSource {

  //Logic to fetch data from API
  @Override
  public String getName() {
    return "RestDataSource";
  }
}
@Configuration
@ComponentScan("com.jsbd.profiles")
public class ProfilesConfig {
}
public interface DataSource {
  public String getName();
}

There are different ways we can set the profile value to test the above code.

a. Use @ActiveProfiles in spring-test to set a profile

We can use the @ActiveProfiles annotation in the JUnit test class to set a specific profile. Remember, this is only available in test classes. The below example demonstrates how to activate a specific profile. When you don’t mention any the default profile gets activated.

@SpringJUnitConfig(ProfilesConfig.class)
@ActiveProfiles("rest")
public class TestRestProfile {

  @Autowired
  private DataSource dataSource;

  @Test
  public void testDataSource(){
    Assert.assertNotNull(dataSource);
    Assert.assertEquals(dataSource.getClass(), RestDataSource.class);
    System.out.println(dataSource.getClass());
  }
}
@SpringJUnitConfig(ProfilesConfig.class)
@ActiveProfiles("graphQl")
public class TestGraphQlProfile {

  @Autowired
  private DataSource dataSource;

  @Test
  public void testGraphQlProfile() {
    System.out.println(dataSource);
    Assert.assertNotNull(dataSource);
    Assert.assertEquals(dataSource.getClass(), GraphQlDataSource.class);
  }
}
@SpringJUnitConfig(ProfilesConfig.class)
public class TestDefaultProfile {
  @Autowired
  private DataSource dataSource;

  @Test
  public void testDefaultProfile() {
    Assert.assertNotNull(dataSource);
    Assert.assertEquals(dataSource.getClass(), RestDataSource.class);
  }
}

b. Activate a profile using JVM parameter

You can also activate a specific profile in your application using the JVM parameter spring.profiles.active as shown below.

$ java -jar -Dspring.profiles.active=graphQl target/spring-profile-app-1.0.jar

c. Set the profile value Programmatically

Programmatically you can set a specific profile using the Environment API. You can either @Autowire the Environment interface or obtain it from the ApplicationContext.

public class ProfilesApp {
  public static void main(String[] args) {

    AnnotationConfigApplicationContext context
        = new AnnotationConfigApplicationContext();
    context.getEnvironment().setActiveProfiles("graphQl");
    context.register(ProfilesConfig.class);
    context.refresh();
    DataSource  dataSource = context.getBean(DataSource.class);
    System.out.println(dataSource.getClass());
  }
}

Alternatively, you can inject the profile value through ConfigurableEnvironment property as shown below.

@Autowired
private ConfigurableEnvironment env;
...
env.setActiveProfiles("rest");

2. Use @Profile with @Configuration

Just like how you use @Profile with @Component, @Repository or @Service, it is also possible to specify the profiles for Configurations. The below example shows the use of profiles with @Configuration annotation.

@Configuration
@Profile("production")
public class ProdConnnectionPoolConfig {

  @Bean
  public ConnectionPoolDataSource prodConnectionPool() {
    //prod database connection pool
  }
}
@Configuration
@Profile("development")
public class DevConnnectionPoolConfig {

  @Bean
  public ConnectionPoolDataSource devConnectionPool() {
    //dev database connection pool
  }
}

3. Use @Profile with @Bean annotation

The @Profile annotation is also used with @Bean to dynamically activate the specific bean based on the profile value. You can see in the below example, a specific ConnectionPoolDataSource is set based on profile.

@Configuration
public class ConnnectionPoolConfig {

  @Bean
  @Profile("production")
  public ConnectionPoolDataSource prodConnectionPool() {
    //prod database connection pool
  }

  @Bean
  @Profile("development")
  public ConnectionPoolDataSource devConnectionPool() {
    //dev database connection pool
  }
}

Conclusion:

There are also other mechanisms to set the profile value using Maven/Gradle build configurations, using external properties files, etc. I have only discussed the most used approaches in this article. This article only talks about Spring-core features, I will cover the Spring-boot capabilities in a separate article. I hope you have got a good understanding of profiles in Spring and when to use them. You can also download the code examples from GitHub.