In this post under Spring Core, I will show with example how to use “@Profile” annotation for “@Configuration” annotated classes.
In the real world scenario, whenever we develop an application, we develop with a goal that it should perform according to agreed functional requirements, regardless of whether the application is running in production or testing environment.
Internally we use different objects for production and different objects for testing environment but that is not known to the user. We cannot use same objects for both the environments.
To configure the application to perform exactly according to agreed functional requirements but using different objects based on the environment choosen, we take the help of “@Profile” annotation.
If the annotation value is production, we use different set of objects and if its value is testing, we use different set of objects.
“@Profile” annotation can be used “@Configuration”, “@Bean”, or “@Component” annotation.
The difference between them being
1) if they are multiple objects that change based on the environment we are using, and they are declared using “@Bean” annotation we can group them in one separate configuration class and in that class, we apply the “@Profile” annotation at the class level. In this case, the entire configuration class is ignored if the profile name applied doesn’t match with the profile name given at the start of the application
2) if they are one or two objects that change based on the environment we are using, and they are declared using “@Bean” annotation in configuration classes along with other “@Bean” annotated methods, then we apply “@Profile” at the bean level. In this case, the entire bean declaration is ignored if the profile name applied doesn’t match with the profile name given at the start of the application
3) if they are objects that change based on the environment we are using, and they are declared using “@Component” annotation then we apply “@Profile” at each objects class level. In this case, the entire bean declaration is ignored if the profile name applied doesn’t match with the profile name given at the start of the application
In this post I will discuss about the first option.
To show an example I will create a marker interface as shown below
ICalculator
package package14;
public interface ICalculator {
}
Now I will create two different implementations of “ICalculator” interface one for production and another for testing as shown below
ProdCalculator
package package14;
public class ProdCalculator implements ICalculator {
@Override
public String toString() {
return "Production Calculator";
}
}
TestCalculator
package package14;
public class TestCalculator implements ICalculator {
@Override
public String toString() {
return "Test Calculator";
}
}
Now I will create configuration classes one for production and another for testing as shown below
ProdConfiguration
package package14;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
@Configuration
@Profile({"prod", "default"})
public class ProdConfiguration {
@Bean
public ICalculator calculator() {
return new ProdCalculator();
}
}
In the above code snippet, I have created a production configuration class which will be processed only if the environment we are using is production. I have applied “@Profile” annotation at the class level and passed the environment name as argument. I also passed “default” as one of the arguments to inform the Spring framework that if the environment is not specified then process this class and not the other
class meant for testing.
TestConfiguration
package package14;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
@Configuration
@Profile("test")
public class TestConfiguration {
@Bean
public ICalculator calculator() {
return new TestCalculator();
}
}
Similar to previous snippet, I have created a testing configuration class which will be processed only if the environment we are using is testing. I have also applied “@Profile” annotation at the class level and passed “test” (the environment name) as its argument.
Now when the profile name given at the start of the application is “prod” or if the profile name is not given, then “ProdConfiguration” class is processed by Spring framework and only the instance of class “ProdCalculator” is created and not “TestCalculator”
If the profile name given at the start of the application is “test”, then “TestConfiguration” class is processed by Spring framework and only the instance of class “TestCalculator” is processed and not “ProdCalculator”.
We can specify the profile name at the start of the application by using the VM argument “-Dspring.profiles.active” as shown below.
-Dspring.profiles.active=test
Note the profile name passed as VM argument must match with one of the profile names passed as argument to “@Profile” annotation.
In this way we use “@Profile” annotation with “@Configuration” annotated classes.
Below is the complete main class for your reference
Main class
package package14;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Example14 {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(ProdConfiguration.class, TestConfiguration.class);
ICalculator calculator = applicationContext.getBean(ICalculator.class);
System.out.println(calculator);
}
}