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 = defaultname = 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;@Configurationpublic class SpringConfig {@Autowiredprivate Environment env;@PostConstructpublic 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;@Configurationpublic class SpringConfig {@Autowiredprivate Environment env;@Value("${name}")private String firstName;@PostConstructpublic 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 = defaultname = Ferdinand#Fake DB Propertiesdb.url = localhostdb.username = admindb.password = securedb.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 & Setterspublic 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@Overridepublic 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;@Configurationpublic class SpringConfig {@Autowiredprivate Environment env;@Value("${name}")private String firstName;@Autowiredprivate DatabaseConfig dbConfig;@PostConstructpublic void onStartUp(){String propertyFile = env.getProperty("properties.file");System.out.println("Propertyfile being used is: " + propertyFile);System.out.println("My name is: " + firstName);//DB configurationSystem.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 = localname = Ferdinand#unique local propertyenvironment = 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 = defaultname = Ferdinand#Fake DB Propertiesdb.url = localhostdb.username = admindb.password = securedb.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.