Spring Boot @ConfigurationProperties: Binding external configurations to POJO classes
Spring BootAugust 31, 20183 mins readExternal configurations allow you to work with the same code in different environments. They also provide you the flexibility to tune your application from a single place.
In this article, you’ll learn how to define and use external configurations in Spring Boot with a very simple annotation based API called @ConfigurationProperties
.
@ConfigurationProperties
bind external configurations to a strongly typed bean in your application code. You can inject and use this bean throughout your application code just like any other spring bean.
Let the exploring begin!
Creating the Application
Let’s create a sample application to learn how to define and use external configurations in spring boot.
We’ll use Spring Boot CLI to initialize the project. Fire up your terminal and type the following command to generate the project -
spring init --dependencies=web,validation --name=config-properties-demo --package-name=com.example.demo config-properties-demo
Alternatively, You may also bootstrap the application from Spring Initializr website by following the instructions below -
- Go to http://start.spring.io
- Enter
config-properties-demo
in the Artifact field. - Set Package name to
com.example.demo
- Add
Web
andValidation
in the dependencies section. - Click Generate Project to download the project.
Following is the directory structure of the complete application for your reference-
Defining Properties
Spring Boot application loads configuration properties from application.properties
file located in the classpath by default.
Open src/main/resources/application.properties
file and add the following properties to it -
## Top level app properties
app.name=ConfigurationPropertiesDemoApp
app.description=${app.name} is a spring boot app that demonstrates how to use external configuration properties
app.upload-dir=/uploads
app.connect-timeout=500ms
app.read-timeout=10s
## Nested Object Properties (security)
app.security.username=callicoder
app.security.password=123456
app.security.roles=USER,ADMIN,PARTNER # List Property
app.security.enabled=true
## Map Properties (permissions)
app.security.permissions.CAN_VIEW_POSTS=true
app.security.permissions.CAN_EDIT_POSTS=true
app.security.permissions.CAN_DELETE_POSTS=false
app.security.permissions.CAN_VIEW_USERS=true
app.security.permissions.CAN_EDIT_USERS=true
app.security.permissions.CAN_DELETE_USERS=false
Binding external properties to a POJO class using @ConfigurationProperties
Let’s now see how we can bind the properties defined in the application.properties
file to a POJO class using @ConfigurationProperties
.
The @ConfigurationProperties
annotation takes a prefix
parameter, and binds all the properties with the specified prefix to the POJO class -
package com.example.demo.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.convert.DurationUnit;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ConfigurationProperties(prefix = "app")
public class AppProperties {
private String name;
private String description;
private String uploadDir;
private Duration connectTimeout = Duration.ofMillis(1000);
@DurationUnit(ChronoUnit.SECONDS)
private Duration readTimeout = Duration.ofSeconds(30);
private final Security security = new Security();
// Getters and Setters (Omitted for brevity)
public static class Security {
private String username;
private String password;
private List<String> roles = new ArrayList<>();
private boolean enabled;
private Map<String, String> permissions = new HashMap<>();
// Getters and Setters (Omitted for brevity)
}
}
Let’s understand a few details about the binding:
Type-safe binding (List and Map)
Notice how the comma separated
roles
in the properties file are bound to aList
of roles and thepermissions
are bound to aMap
.Duration Support
Also, note how the duration properties are safely bound to the
Duration
types. This is so awesome. Spring Boot allows you to specify durations with the following units in theapplication.properties
files -ns
for nanosecondsus
for microsecondsms
for millisecondss
for secondsm
for minutesh
for hoursd
for daysThe default unit is
milliseconds
. So if you don’t specify any unit in the properties file, It will be considered asmilliseconds
.Note that, you can also override the unit using
@DurationUnit
annotation as we have done in the above POJO class.
Naming Convention
All the kebab case property names (ex: upload-dir) are bound to the corresponding camel case fields (ex: uploadDir) in the POJO class.
Note that the properties can also be specified in camel case. But using kebab case is recommended.
Enabling Configuration Properties
You need to explicitly register the properties classes using the @EnableConfigurationProperties
annotation as shown in the following example.
package com.example.demo;
import com.example.demo.config.AppProperties;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@SpringBootApplication
@EnableConfigurationProperties(AppProperties.class)
public class ConfigPropertiesDemoApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigPropertiesDemoApplication.class, args);
}
}
Alternatively, you could also add a @Component
annotation to the AppProperties
class and the binding would still work.
Injecting Configuration Properties in your Spring Beans
The @ConfigurationProperties
classes are regular spring beans, and you can inject them in the same way as any other bean.
In the following example, I’ve written a sample API that retrieves app details from configuration properties and returns them to the client -
package com.example.demo.controller;
import com.example.demo.config.AppProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
public class IndexController {
// Injecting ConfigurationProperties in your Beans
@Autowired
private AppProperties appProperties;
@GetMapping("/")
public Map<String, String> getAppDetails() {
Map<String, String> appDetails = new HashMap<>();
appDetails.put("name", appProperties.getName());
appDetails.put("description", appProperties.getDescription());
return appDetails;
}
}
@ConfigurationProperties Validation
You can validate configuration properties using javax.validation
constraints like @NotNull
, @NotEmpty
etc.
To enable validation, you just need to add Spring’s @Validated
annotation to the @ConfigurationProperties
class -
package com.example.demo.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.convert.DurationUnit;
import org.springframework.validation.annotation.Validated;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ConfigurationProperties(prefix = "app")
@Validated
public class AppProperties {
@NotNull
private String name;
private String description;
private String uploadDir;
private Duration connectTimeout = Duration.ofMillis(1000);
@DurationUnit(ChronoUnit.SECONDS)
private Duration readTimeout = Duration.ofSeconds(30);
@Valid
private final Security security = new Security();
// Getters and Setters (Omitted for brevity)
public static class Security {
private String username;
private String password;
@NotEmpty
private List<String> roles = new ArrayList<>();
private boolean enabled;
private Map<String, String> permissions = new HashMap<>();
// Getters and Setters (Omitted for brevity)
}
}
Now, The Spring Boot application will throw a validation exception on startup, if the name
property is null
or the security.roles
property is empty.
Environment based (Profile specific) Configuration Properties
In addition to the standard application.properties
file, Spring Boot also allows you to define profile-specific properties with the following naming convention -
application-{profile}.properties
The profile specific property files are loaded from the same location as the application.properties
file, with profile-specific properties always overriding the default ones.
To illustrate this, Let’s define some profile-specific property files, and override some of the default properties -
application-dev.properties
## Override properties for dev environment app.name=ConfigurationPropertiesDemoApp-DEVELOPMENT app.security.username=callicoder-dev
application-staging.properties
## Override properties for staging environment app.name=ConfigurationPropertiesDemoApp-STAGING app.security.username=callicoder-staging app.security.password=C@ll1C0d3r
application-prod.properties
## Override properties for prod environment app.name=ConfigurationPropertiesDemoApp-PRODUCTION app.security.username=callicoder-prod app.security.password=C@ll1C0d3r
Now, we need to activate a spring profile so that the corresponding properties file is loaded by spring boot.
Activating a Spring Profile
You can set active profiles in many ways -
Using application properties
The default
application.properties
file is always loaded by Spring Boot. You can set active profiles in theapplication.properties
file by adding the following property -spring.profiles.active=staging
After adding the above property, Spring Boot will load
application-staging.properties
file as well, and override properties with the new values specified there.Using command line argument
You can set active profiles on startup by providing the
spring.profiles.active
command line argument like this -# Packaging the app mvn clean package -Dspring.profiles.active=staging # Running the packaged jar with `spring.profiles.active` argument java -jar -Dspring.profiles.active=staging target/config-properties-demo-0.0.1-SNAPSHOT.jar
Moreover, Here is how you can set active profiles while running the application with
spring-boot:run
command -mvn spring-boot:run -Dspring.profiles.active=dev
Using environment variable
Lastly, you can also set active profiles using the
SPRING_PROFILES_ACTIVE
environment variable-export SPRING_PROFILES_ACTIVE=prod
Running the application
Let’s run the application and access the sample Rest API to see configuration properties in action -
mvn spring-boot:run
$ curl http://localhost:8080
{"name":"ConfigurationPropertiesDemoApp","description":"ConfigurationPropertiesDemoApp is a spring boot app that demonstrates how to use external configuration properties"}
Running the application with active profiles set to prod
mvn spring-boot:run -Dspring.profiles.active=prod
$ curl http://localhost:8080
{"name":"ConfigurationPropertiesDemoApp-PRODUCTION","description":"ConfigurationPropertiesDemoApp-PRODUCTION is a spring boot app that demonstrates how to use external configuration properties"}
Conclusion
@ConfigurationProperties
are a really nice way to bind external configurations in a type-safe manner. I use this feature in almost all my projects. Moreover, Spring Boot’s auto-configurations also rely on configuration properties.
Let me know what do you think about this feature in the comment section below.
As always, You can find the complete demo project that we built in this article in the Github Repository.
Until next time…