Skip to content

Ferdinand Agyei-Yeboah

Externalizing Spring Configuration With Property Files

August 23, 2018

Software applications may need different configuration values depending on where they are running. For example, an API that interacts with a database will need production credentials when accessing the production database but test credentials in a test environment.

In Spring, property files can be used to store external configuration that is environment or application specific and then the corresponding property file can be loaded as needed.

Creating property files

Creating property files in SpringBoot (or Spring) is quite easy. After you create a SpringBoot application (using the initializer or including the parent pom manually), simply create an application.properties in the src/main/resources folder that is provided by Maven.

By default, Spring will load any properties that are in application.properties when the application starts up. We will see how to incorporate other property files later.

This is the application.properties file we will be using for this tutorial.

properties.file = default
name = Ferdinand

Properties are stored as key-value pairs and so you can see there are two keys and values stored, properties.file which resolves to default (representing which property file is being used), and name which resolves to Ferdinand.

Differing ways of reading and using property files

Environment Bean

Properties can be read in by autowiring the Spring provided Environment bean and then calling its getProperty method. The getProperty method will return null if the property is not present but getRequiredProperty can be used if you wish an exception to be thrown if the property is absent.

We will create a Spring configuration class and read in the values as shown below. You will be able to see the properties.file key is correctly read in with the value default.

package com.ferdinand.blog.tutorials;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import javax.annotation.PostConstruct;
@Configuration
public class SpringConfig {
@Autowired
private Environment env;
@PostConstruct
public void onStartUp(){
String propertyFile = env.getProperty("properties.file");
System.out.println("Propertyfile being used is: " + propertyFile);
}
}

@Value

Properties can also be read in using the @Value annotation which directly injects the property value into the annotated variable. We will use the @Value annotation to read in the name property into the firstName variable as shown below.

package com.ferdinand.blog.tutorials;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import javax.annotation.PostConstruct;
@Configuration
public class SpringConfig {
@Autowired
private Environment env;
@Value("${name}")
private String firstName;
@PostConstruct
public void onStartUp(){
String propertyFile = env.getProperty("properties.file");
System.out.println("Propertyfile being used is: " + propertyFile);
System.out.println("My name is: " + firstName);
}
}

@ConfigurationProperties

Using Environment or @Value are relativity quick ways to read property files but my favorite way to read properties is by typesafe-ly binding them to an object using @ConfigurationProperties. First we are going to add some example database properties to our property file namely a url, username, password, and connection pool size. application.properties now looks like so:

properties.file = default
name = Ferdinand
#Fake DB Properties
db.url = localhost
db.username = admin
db.password = secure
db.connection-pool = 5

We are going to bind all our database related properties to a java object using a plain Java class and SpringBoot’s @ConfigurationProperties annotation. In this case we will specify the object to only read in the database properties by passing a prefix reader to the annotation so only properties that start with the “db” will be bound to the java object. We create a DatabaseConfig.java similar to the following:

package com.ferdinand.blog.tutorials;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "db")
public class DatabaseConfig {
private String url;
private String username;
private String password;
private Integer connectionPool;
//Getters & Setters
public String getUrl() {
return url;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public Integer getConnectionPool() {
return connectionPool;
}
public void setUrl(String url) {
this.url = url;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setConnectionPool(Integer connectionPool) {
this.connectionPool = connectionPool;
}
//Create toString to allow printing object
@Override
public String toString() {
return "DatabaseConfig{" +
"url='" + url + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
", connectionPool=" + connectionPool +
'}';
}
}

Notice that the field names in the object match the property names (minus the prefix). By default, SpringBoot uses a relaxed binding so capitalization is irrevelant. Connectionpool, CONnecTIonPoOL, connectionpool as field names in the object will all resolve to the db.connection-pool property in the property file.

Since we created the DatabaseConfig object as a Spring Bean using @Component, we can autowire it into our main configuration file and extract the properties as needed, or log them as in the case below:

package com.ferdinand.blog.tutorials;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import javax.annotation.PostConstruct;
@Configuration
public class SpringConfig {
@Autowired
private Environment env;
@Value("${name}")
private String firstName;
@Autowired
private DatabaseConfig dbConfig;
@PostConstruct
public void onStartUp(){
String propertyFile = env.getProperty("properties.file");
System.out.println("Propertyfile being used is: " + propertyFile);
System.out.println("My name is: " + firstName);
//DB configuration
System.out.println(dbConfig);
}
}

Referencing values in other property files

Environment specific property files can be created in Spring. These environment specific property files are called profiles. To create a profile specific property file simply create a file named application-{profile}.properties (where profile is a name, i.e dev, qa, prod). Properties declared in profile specific property files override those in application.properties, but inherit any undeclared properties that are application.properties. To run a SpringBoot application using a specific profile pass the spring.profiles.active property during runtime.

We will create a application-local.properties as shown below

properties.file = local
name = Ferdinand
#unique local property
environment = localhost:3000

Given the maven spring boot plugin is included as a dependency, we can run this profile with the following command specifying the active profile mvn spring-boot:run -Dspring.profiles.active=local.

When we run the application we see that properties.file key is overriden and now prints local while the DB configuration (which was not specified in local properties) reflects the application.properties values.

One last thing. It should be noted that one can cross reference values across property files by wrapping the desired property in Spring’s extrapolation format ${}. For example, application.properties can be updated to reference the environment variable in application-local.properties as such.

properties.file = default
name = Ferdinand
#Fake DB Properties
db.url = localhost
db.username = admin
db.password = secure
db.connection-pool = 5
#Reference another property. Does not have to be in same file.
message = My environment is ${environment}

When running the local profile, you will see application.properties picked up the environment property and the message printed is My environment is localhost:3000.

That’s all for now!

Note that SpEL can be used in property files when more advanced manipulation is needed.


Software Engineering Tutorials & Best Practices