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