Spring Profiles

Spring Profiles

1 Comment

Enterprise Spring applications typically need to run in multiple environments. For example, development, testing, staging, and production. Each such environment has its own set of settings. For example, you might be using an embedded H2 database for development but an enterprise-grade Oracle or MySQL database for production. So the data source and connection configurations for them will differ when you run the application during development vs during production.

Spring applications typically have a large set of such configurations that vary across environments. In such scenarios, you can use Spring profiles. Spring profiles provide a way to segregate parts of your application configuration and make it be available only in certain environments.

In this post, we will learn how to use Spring profile.

Creating Profiles

Spring Boot by default comes with a property file, named application.properties. To segregate the configuration based on the environments, we will create multiple property files. One for each environment we are targeting.

We will create three property files for the dev, test, and prod environments. Note the naming convention.
application-dev.properties
application-test.properties
application-prod.properties

The application.properties file will be the master of all properties. Here we will specify which profile is active by using the property spring.profiles.active.

The property files are as follows:

This is the application.properties file.

spring.application.name = Spring Profiles
spring.profiles.active = test
app.message = This is the Primary Application Property

In the preceding code, the spring.profiles.active property tells Spring which profile to use. Here we have set the test profile as active.

In test, we will be using MySQL database.
The application-test.properties file is this.

app.message = This is the TEST Environment property file
spring.datasource.url=jdbc:mysql://localhost:3306/testDB
spring.datasource.username=root
spring.datasource.password=root123
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect

In dev, we will be using H2, an in-memory database. The application-dev.properties file is this.

app.message = This is the DEV Environment Property file
spring.h2.console.enabled=true
spring.h2.console.path=/h2
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:db
spring.datasource.userName=sa
spring.datasource.password=sa

The application-prod.properties file is this.

app.message = This is the PRODUCTION Environment property file
spring.datasource.url=jdbc:mysql://localhost:3306/prodDB
spring.datasource.userName=root
spring.datasource.password=root123
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect

Binding Externalized Configurations to Structured Objects

We have externalized configurations for different profiles to properties files. Now, we need to bind those configurations to objects of our application.

Spring provides the @ConfigurationProperties annotation to bind and validate some external configurations defined in .properties file.
You can do it in two ways. Add the @ConfigurationProperties annotation to a class definition.
Alternative, you can add it to a @Bean method in a @Configuration class.

Let us create DBConfiguration class annotated with @ConfigurationProperties to represent our external configuration.

package guru.springframework.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@ConfigurationProperties("spring.datasource")
public class DBConfiguration {
private String driverClassName;
private String url;
private String userName;
private String password;
public String getDriverClassName() {
return driverClassName;
}
public void setDriverClassName(String driverClassName) {
this.driverClassName = driverClassName;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}

The DBConfiguration class above is marked with the @ConfigurationProperties("spring.datasource") annotation. Here spring.datasource represents configuration properties defined in the external configuration files.
The DBConfiguration class contains the driverClassName, url, userName, and password properties along with the getter and setter methods. Each of these fields represent properties defined in the properties file.
Next, we need to register the preceding DBConfiguration class using the @EnableConfigurationProperties annotation.

package guru.springframework.config;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
@Configuration
@EnableConfigurationProperties(DBConfiguration.class)
public class DBConnectionConfiguration {
private DBConfiguration dbConfiguration;
public DBConnectionConfiguration(DBConfiguration dbConfiguration) {
this.dbConfiguration = dbConfiguration;
}
@Profile("dev")
@Bean
public String devDBCcnnection() {
System.out.println("DB Connection for Dev");
System.out.println(dbConfiguration.getUrl());
System.out.println(dbConfiguration.getUserName());
System.out.println(dbConfiguration.getPassword());
System.out.println(dbConfiguration.getDriverClassName());
return "DB Connection for Dev";
}

@Profile("test")
@Bean
public String devTestCcnnection() {
System.out.println("DB Connection for Test");
System.out.println(dbConfiguration.getUrl());
System.out.println(dbConfiguration.getUserName());
System.out.println(dbConfiguration.getPassword());
System.out.println(dbConfiguration.getDriverClassName());
return "DB Connection for Test";
}

@Profile("prod")
@Bean
public String devProdCcnnection() {
System.out.println("DB Connection for Prod");
System.out.println(dbConfiguration.getUrl());
System.out.println(dbConfiguration.getUserName());
System.out.println(dbConfiguration.getPassword());
System.out.println(dbConfiguration.getDriverClassName());
return "DB Connection for Prod";
}
}

In the preceding code, we have created a configuration class DBConnectionConfiguration marked with the @Configuration annotation. The @EnableConfigurationProperties(DBConfiguration.class) annotation registers the @ConfigurationProperties bean, DBConfiguration in this configuration class.
For the purpose of demonstration, we have created three beans that prints the database configurations. We used the @Profile annotation on each bean for the dev, test, and prod profiles.

The code for the main class is this.

package guru.springframework;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;

@SpringBootApplication
@EnableConfigurationProperties
public class SpringProfilesDemoApplication {

public static void main(String[] args) {
SpringApplication.run(SpringProfilesDemoApplication.class, args);
}
}

The output on running the main class will be this.

 
DB Connection for Test
jdbc:mysql://localhost:3306/testDB
root
root123
com.mysql.cj.jdbc.Driver

As we can see, the applications prints out the properties of the test environment as the active profile is set to test.

Change the active profile to dev in the application.properties and run the application again. The output is this.

 
DB Connection for Dev
jdbc:h2:mem:db
sa
sa
org.h2.Driver

Summary

In this post, we saw how to use environment-specific properties files using Spring profiles. We can also mark any @Component or @Configuration class with @Profile to limit when it is loaded. I am leaving this for another post.

Another way to set the active profile is by using the command line switch --spring.profiles.active.

The spring.profiles.active property follows the same ordering rules as other properties. This means you can replace the active profile in application.properties by using the command line switch.

Sometimes, we might want to add profile-specific properties to the active profiles rather than replace them. To add active profiles, use the spring.profiles.include property.

The source code for this post can be found here on GitHub.

About SFG Contributor

Staff writer account for Spring Framework Guru

    You May Also Like

    One comment

    1. February 15, 2020 at 2:49 pm

      Why would you even create a separate database profile for test and property file for test?
      You can just put @AutoConfigureTestDatabase above your test class…..

      Reply

    Leave a Reply

    Your email address will not be published. Required fields are marked *

    This site uses Akismet to reduce spam. Learn how your comment data is processed.