Spring Boot – Managing Profiles with @Profile Annotation Example
Spring Profiles provides a way to segregate parts of your application configuration and make it only available in certain environments. This is particularly useful when configuring different environments like development and/or production. In the following tutorial we demonstrate how to manage spring profiles with @Profile annotation.
Project Structure
Our application has the following project structure. Note: we have four different application-${env}.yml
property files. Each file represents properties for a different environment, except the application-common.yml
which is included in every property file.
Maven Dependencies
We use Apache Maven to manage our project’s dependencies. As we are writing JUnit tests,
we also included the org.springframework.boot:spring-boot-starter-test
dependency.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.memorynotfound.springboot</groupId>
<artifactId>profiles</artifactId>
<version>1.0.0-SNAPSHOT</version>
<url>https://memorynotfound.com</url>
<name>Spring Boot - ${project.artifactId}</name>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>l;spring-boot-profile-example</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Application Environment Properties
We are configuring three environments: default
, dev
and prod
. Each of these environments will include the application-common.yml
properties file. We use different environment profiles to illustrate how to manage spring profiles using @Profile
annotation.
The application-common.yml
is used for common application properties.
app:
name: common-profile-name
The application-default.yml
has the following content.
spring:
profiles:
active:
- default
include: common
app:
email: [email protected]
The application-dev.yml
has the following content.
spring:
profiles:
active:
- dev
include: common
app:
email: [email protected]
The application-prod.yml
has the following content.
spring:
profiles:
active:
- prod
include: common
app:
email: [email protected]
Configurable Application Properties
Depending on the active spring profile usually set by spring.profiles.active
,
spring will load different configuration properties.
package com.memorynotfound.springboot;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("app")
public class ApplicationProperties {
private String name;
private String email;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "ApplicationProperties{" +
"name='" + name + '\'' +
", email='" + email + '\'' +
'}';
}
}
Spring Boot – @Profile Example
We can also manage different spring components using the @Profile
annotation.
package com.memorynotfound.springboot;
public interface Configuration {
String getName();
}
The @Profile
annotation indicates which component’ll be injected. Here we added two profiles: dev and default.
package com.memorynotfound.springboot;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
@Service
@Profile({"dev", "default"})
public class DevelopmentConfiguration implements Configuration {
@Override
public String getName() {
return "development profile";
}
}
The @Profile
annotation indicates which component’ll be injected. Here we added one profile: prod.
package com.memorynotfound.springboot;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
@Service
@Profile({"prod"})
public class ProductionConfiguration implements Configuration {
@Override
public String getName() {
return "production profile";
}
}
Bootstrap Spring Boot Application
We wrote a simple spring boot application that’ll print the properties configured in the active profile.
package com.memorynotfound.springboot;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import javax.annotation.PostConstruct;
@SpringBootApplication
@EnableConfigurationProperties(ApplicationProperties.class)
public class Application {
private static Logger logger = LoggerFactory.getLogger(Application.class);
@Autowired
private ApplicationProperties properties;
@Autowired
private Configuration configuration;
public static void main(String... args) throws Exception {
SpringApplication.run(Application.class, args);
}
@PostConstruct
private void init(){
logger.info("Spring Boot - active profile: " + configuration.getName());
logger.info("Spring Boot - Choosing Your Profile and @Profile annotation example");
logger.info(properties.toString());
}
}
Programatically Setting Profiles
If you want, you can set an additional spring profile programatically.
public static void main(String... args) throws Exception {
SpringApplication app = new SpringApplication(Application.class);
app.setAdditionalProfiles("common");
app.run(args);
}
Building and Running Spring Boot Application
### Building application
mvn clean package
### Running with maven
mvn spring-boot:run -Drun.jvmArguments="-Dspring.profiles.active=prod"
### Running with java
java -jar spring-boot-profile-example.jar --spring.profiles.active=prod
Console Output
The previous application run with the argument spring.profiles.active=prod
prints the following output to the console.
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.5.3.RELEASE)
2017-05-30 13:48:26.808 INFO 26463 --- [ main] c.memorynotfound.springboot.Application : The following profiles are active: common,dev
2017-05-30 13:48:27.624 INFO 26463 --- [ main] c.memorynotfound.springboot.Application : Spring Boot - active profile: production profile
2017-05-30 13:48:27.624 INFO 26463 --- [ main] c.memorynotfound.springboot.Application : Spring Boot - Choosing Your Profile and @Profile annotation example
2017-05-30 13:48:27.624 INFO 26463 --- [ main] c.memorynotfound.springboot.Application : ApplicationProperties{name='common-profile-name', email='[email protected]'}
Unit Testing Spring Profiles with JUnit
We previously saw how to use spring profiles to manage environment properties. In the following section we’ll write unit tests using JUnit and spring-boot-test.
Spring Boot – JUnit profiles @ActiveProfiles
We can annotate the class using @ActiveProfiles
to register the default active profile.
package com.memorynotfound.springboot.test;
import com.memorynotfound.springboot.Configuration;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest
@ActiveProfiles("dev")
@RunWith(SpringRunner.class)
public class ConfigurationTest {
@Autowired
Configuration configuration;
@Test
public void testDevelopmentProfile(){
String name = configuration.getName();
assertThat(name).contains("development");
}
}
Spring Boot – JUnit Application
Running the application we can pass the spring.profiles.active
using different methods. In the fist test, we don’t provide any profile. Spring’ll automatically use the default
profile, when no other profile is set. In the next test, we set the spring.profiles.active
property using a system variable. In the last test we pass the --spring.profiles.active=prod
argument to the main
method of the application.
package com.memorynotfound.springboot.test;
import com.memorynotfound.springboot.Application;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.boot.test.rule.OutputCapture;
import static org.assertj.core.api.Assertions.assertThat;
public class ApplicationTest {
@Rule
public OutputCapture outputCapture = new OutputCapture();
@Before
public void before() {
System.clearProperty("spring.profiles.active");
}
@Test
public void testDefaultProfile() throws Exception {
Application.main();
assertThat(getConsoleOutput())
.contains("[email protected]")
.contains("common-profile-name");
}
@Test
public void testDevelopmentProfile() throws Exception {
System.setProperty("spring.profiles.active", "dev");
Application.main();
assertThat(getConsoleOutput())
.contains("[email protected]")
.contains("common-profile-name");
}
@Test
public void testProductionProfile() throws Exception {
Application.main("--spring.profiles.active=prod");
assertThat(getConsoleOutput())
.contains("[email protected]")
.contains("common-profile-name");
}
private String getConsoleOutput(){
return outputCapture.toString();
}
}
great