1. Introduction
One of the handy features of Spring Boot is externalized configuration and easy access to properties defined in properties files.
An earlier article described various ways in which this can be done and in this article, we’re going to explore the @ConfigurationProperties annotation in greater detail.
2. Setup
The setup for this article is fairly standard. We start by adding spring-boot-starter-parent as the parent in our pom.xml:
<!-- Inherit defaults from Spring Boot --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.2.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent>
The latest version of Spring Boot can be found on the maven central here.
In order to be able to validate properties defined in the file, we would need a JSR-303’s implementation. hibernate-validator is one of them. Let’s add it to our pom.xml as well:
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>5.4.1.Final</version> </dependency>
Depending on the type of application and the environment running, we might need to add one or more additional dependencies. The reference page has details about the same, and the latest version of hibernate-validator can be found here.
We shall take the example of a hypothetical class which has configuration properties related to a mail server to understand the full power of this annotation:
public class ConfigProperties { public static class Credentials { private String authMethod; private String username; private String password; // standard getters and setters } private String host; private int port; private String from; private Credentials credentials; private List<String> defaultRecipients; private Map<String, String> additionalHeaders; // standard getters and setters }
3. Binding the Configuration Properties
According to the official documentation, it is recommended to isolate the configuration properties into a separate POJO annotated with @ConfigurationProperties, so let’s start by doing that:
@Configuration @ConfigurationProperties public class ConfigProperties { // previous code }
We also have added the @Configuration annotation for Spring to be able to find this bean and make it a candidate for injection.
The annotation works best when we have hierarchical properties that all have the same prefix, so we mention the prefix too as a part of the annotation. We can also optionally define a custom source where we’re storing these properties, else the default location (
We can also optionally define a custom source where we’re storing these properties, else the default location (classpath:application.properties) is looked up. So we now add the above annotations to the existing properties class:
@Configuration @PropertySource("classpath:configprops.properties") @ConfigurationProperties(prefix = "mail") public class ConfigProperties { // previous code }
That’s it! Now any properties defined in the property file that has the prefix mail and the same name as one of the properties are automatically assigned to this object.
Also, by default, a relaxed binding scheme is adopted for the binding, so all of the following variations are bound to the property authMethod of the Credentials class:
mail.credentials.auth_method mail.credentials.auth-method mail.credentials_AUTH_METHOD mail.CREDENTIALS_AUTH_METHOD
Similarly, List and Map properties can also be bound. Here’s a sample properties file that binds correctly to our ConfigProperties object defined earlier:
#Simple properties mail.host=mailer@mail.com mail.port=9000 mail.from=mailer@mail.com #List properties mail.defaultRecipients[0]=admin@mail.com mail.defaultRecipients[1]=owner@mail.com #Map Properties mail.additionalHeaders.redelivery=true mail.additionalHeaders.secure=true #Object properties mail.credentials.username=john mail.credentials.password=password mail.credentials.authMethod=SHA1
4. Property Validation
One of the handy things that this annotation provides the is the validation of properties using the JSR-303 format. This allows for all sorts of neat things like checking that a property is not null:
@NotBlank private String host;
We can also check the minimum and maximum length of a String property:
@Length(max = 4, min = 1) private String authMethod;
Or enforce the minimum and maximum value of an Integer property:
@Min(1025) @Max(65536) private int port;
And finally, we can also make sure that a property matches a certain pattern by defining a regex for the same. This has been done for email, as an example:
@Pattern(regexp = "^[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]{2,6}$") private String from;
This helps us reduce a lot of if – else conditions in our code and makes it look much cleaner and concise.
If any of these validations fail then the main application would fail to start with an IllegalStateException till the incorrect property is corrected.
Also, it is important that we declare getters and setters for each of the properties as they’re used by the validator framework to access the concerned properties.
5. Conclusion
In this quick tutorial, we explored the @ConfigurationProperties annotation and also saw some of the handy features it provides like relaxed binding and Bean Validation.
As usual, the code is available over on Github.