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. The pom.xml here is without teh spring-boot-starter.

pom.xml
....
  <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 include the test dependency.

pom.xml
...
   <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.

GraphQlDataSource
@Component
@Profile("graphQl")
public class GraphQlDataSource implements DataSource {

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

  //Logic to fetch data from API
  @Override
  public String getName() {
    return "RestDataSource";
  }
}
ProfilesConfig
@Configuration
@ComponentScan("com.jsbd.profiles")
public class ProfilesConfig {
}
DataSource
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.

test/TestRestProfile
@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());
  }
}
test/TestGraphQlProfile
@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);
  }
}
test/TestDefaultProfile
@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.

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

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

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

  @Bean
  public ConnectionPoolDataSource prodConnectionPool() {
    //prod database connection pool
  }
}
DevConnnectionPoolConfig
@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.

Java
@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:

We’ve explored the powerful feature of profiles in Spring, which allow us to conditionally register beans based on the active profile. While we’ve covered the most commonly used approaches, such as using annotations and XML configuration, it’s important to note that there are other mechanisms available as well, such as configuring profiles through Maven/Gradle build configurations or external properties files.

It’s worth mentioning that this article focuses solely on Spring-core features, and we’ll delve into Spring Boot capabilities in a separate article. By understanding profiles in Spring, readers gain insight into when and how to use them effectively in their applications to manage different environments and configurations.

To further reinforce your understanding and experiment with the concepts discussed, you can download the provided code examples from GitHub. If you have any questions or want to engage in further discussion, feel free to connect with us on Gitter. We’re here to support your learning journey and help you make the most out of Spring’s powerful features.


By |Last Updated: April 2nd, 2024|Categories: Spring Framework|

Table of Contents