Using @ConfigurationProperties in Spring Boot

Examples of reading and mapping external configurations from a Properties or YAML file to a Java Bean in Spring Boot applications.

Overview

With Spring Boot, we can easily externalize application-level configurations to a Properties or a YAML file. Moreover, Spring Boot provides excellent support for automatically reading this configuration and mapping them into a dedicated Java Bean instance.

In this tutorial, we’ll learn about mapping the properties file or yaml file into Java Bean using @ConfigurationProperties annotation.

@ConfigurationProperties Annotation

The @ConfigurationPropertis annotation is used on a class or a @Bean method to map external properties configurations into the class or bean.

The binding between the properties and the bean fields happens based on the setter methods in the java bean class. To use constructors for such bindings, we can additionally use @ConstructorBinding annotation.

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ConfigurationProperties {
  @AliasFor("prefix")
  String value() default "";

  @AliasFor("value")
  String prefix() default "";

  boolean ignoreInvalidFields() default false;

  boolean ignoreUnknownFields() default true;
}Code language: Java (java)

Attributes:

  • prefix: When properties are grouped under a common prefix, we can use the prefix attribute to load only the subset of the properties.
  • ignoreInvalidFields: It is false by default, but we can set it to true if we want data type mismatch errors should be ignored.
  • ignoreUnknownFields: Unknown fields are the fields in the properties file which does not have a respective field in the Java Bean. By default, Spring ignores such cases. However, we can set this attribute to false and let it throw an error.

Reading Simple Properties

Consider we have a simple set of properties in a yaml or a properties file. Each of the properties fields denotes a different configuration. We use YAML in our examples, but everything works the same even if you use Properties files instead.

application.yaml

default-username: default_user
default-password: password_default
connection-timeout: 2000Code language: YAML (yaml)

We want to read and use these properties in our application. To do that, we will create a class to hold these properties.

@Configuration
@ConfigurationProperties
public class SimpleProperties {
  private String defaultUsername;
  private String defaultPassword;
  private int connectionTimeout;

  // Constructor, Getter, and Setter methods

  @Override
  public String toString() {
    return "defaultUsername: " + defaultUsername
      + ",\ndefaultPassword: " + defaultPassword
      + ",\nconnectionTimeout" + connectionTimeout;
  }
}Code language: Java (java)

Note a few things here,

  • Our class is marked @Configuration. This is for the Spring Boot to find this class during scanning.
  • Field names are in standard Java camel case, while the properties are in kebab case. However, it automatically binds fields appearing in different cases, e.g. UPPER_CASE, kebab-case, camelCase, or underscore_notation.
  • Although we have provided a constructor, getter and setter methods, only setter methods are required for the binding.

Let’s print the Java bean upon startup using a @PostConstruct method.

* Simple Properties: 
defaultUsername: default_user,
defaultPassword: password_default,
connectionTimeout: 2000Code language: plaintext (plaintext)

The output shows that all the Properties fields are mapped correctly into the Java class.

Reading Properties using Prefix

Sometimes we can organize properties into different groups. This is done by using a prefix that helps identify a particular group.

For example, next is an application.yaml file that

login-service:
  login-url: https://login.example.com
  username: login_user
  password: password123
  
user-service:
  url: https://users.example.com
  username: user_name
  password: strong-passwordCode language: YAML (yaml)

Putting properties into groups makes them easily readable and manageable. In addition, using @ConfigurationProperties, we can read properties from a specific group or load different properties groups in different Java beans using prefix attributes.

We will create different properties classes to load properties based on their prefix. First, let’s create a Login Service Properties class.

@Configuration
@ConfigurationProperties(prefix = "login-service")
public class LoginServiceProperties {
  private String loginUrl;
  private String username;
  private String password;

  // Constructor, Getter, and Setter methods

  @Override
  public String toString() {
    return "loginUrl: " + loginUrl
      + ",\nusername: " + username
      + ",\npassword: " + password;
  }
}Code language: Java (java)

Next, create the UserServiceProperties class.

@Configuration
@ConfigurationProperties(prefix = "user-service")
public class UserServiceProperties {
  private String url;
  private String username;
  private String password;

  // Constructor, Getter, and Setter methods

  @Override
  public String toString() {
    return "url: " + url
      + ",\nusername: " + username
      + ",\npassword: " + password;
  }
}Code language: Java (java)

Note that, in both of the above classes, we have used @ConfiguratonProperties annotation with different prefix values. Having that, Spring will load a particular subset of the properties into the respective classes.

Let’s use a @PostConstruct-based method to print both instances on the console.

* Login Service Properties: 
loginUrl: https://login.example.com,
username: login_user,
password: password123
* User Service Properties
url: https://users.example.com,
username: user_name,
password: strong-passwordCode language: plaintext (plaintext)

We can see both of the Properties instances have received respective properties configurations.

Reading Properties with Setter Methods

Spring @ConfigurationProperties, by default, uses setter methods to set individual properties on the Java bean. We will load the login-service properties again in a separate Java bean to demonstrate that. However, this time we will provide a no-argument constructor and setter methods.

@Configuration
@ConfigurationProperties(prefix = "login-service")
public class SetterBasedLoginProperties {
  private String loginUrl;
  private String username;
  private String password;

  public SetterBasedLoginProperties(){}

  public void setLoginUrl(String loginUrl) {
    this.loginUrl = loginUrl;
  }

  public void setUsername(String username) {
    this.username = username;
  }

  public void setPassword(String password) {
    this.password = password;
  }

  // ToString() method
}Code language: Java (java)

So, we have provided a default constructor and only the setter methods. As the class uses the ‘prefix = login-service‘, it will load the particular subset of properties that we saw in the preceding example.

* Setter Based Properties
loginUrl: https://login.example.com,
username: login_user,
password: password123Code language: plaintext (plaintext)

We can see that all the properties are correctly bound upon printing the populated Java bean.

Binding Properties with Different Names

Having Setter methods-based Properties binding, the @ConfigurationProperties allows us to bind fields that do not match exactly.

For example, we want to bind a properties or yaml file field to a Java bean field, but their names are different.

login-service:
  login-url: https://login.example.comCode language: YAML (yaml)

From the previously seen examples, we have login-url property under the login-service prefix. However, the respective Java bean has the url field.

private String url;Code language: Java (java)

By default, Spring @ConfigurationProperties won’t bind the login-url value in the url field of the Java bean. To overcome that problem, we can provide a dummy setter method that matches the properties field name pattern.

@Configuration
@ConfigurationProperties(prefix = "login-service")
public class DifferentlyNamedProperties {
  private String url;

  public void setLoginUrl(String loginUrl) {
    this.url = loginUrl;
  }

  @Override
  public String toString() {
    return "url: " + url;
  }
}Code language: Java (java)

Note that we have provided a dummy setter method, which sets the value of the url variable. When binding the properties values, Spring uses their name patterns to locate the appropriate setter method. That means it doesn’t care about the actual name of the Java class field.

Let’s start the application and print the populated Properties class.

* DifferentlyNamedProperties
url: https://login.example.comCode language: plaintext (plaintext)

That proves we can provide a dummy setter method to map properties with different names.

Reading Properties with Constructor

As we have seen, mapping properties or yaml file fields using Setter methods are straightforward. However, having the Setter methods in a properties class makes it mutable. As the properties configurations are constants, we may want to make the application properties class immutable.

To make the application properties class immutable, we can eliminate the setter methods and use Constructor based properties binding in Spring. To do so, we need to use @ConstructorBinding annotation on our @ConfigurationProperties bean.

@ConstructorBinding
@ConfigurationProperties(prefix = "login-service")
public class ConstructorBasedLoginProperties {
  private String loginUrl;
  private String username;
  private String password;

  public ConstructorBasedLoginProperties(
      String loginUrl,
      String username,
      String password) {
    this.loginUrl = loginUrl;
    this.username = username;
    this.password = password;
  }

  // Getter Methods
  // toString() method
}Code language: Java (java)

Note that our properties class has an all-argument constructor, the only constructor, and no setter methods.

Having @ConstructorBinding annotation, Spring loads the properties configurations using the provided constructor. Also, the state of the created instance cannot be modified without setter methods. Thus, it is immutable in behaviour.

The only thing to note here is, To use @ConstructorBinding, the Configuration properties need to be enabled explicitly. This is done using @EnableConfigurationProperties or @ConfigurationPropertiesScan annotation in the Application class or on a @Configuration class.

Summary

With this tutorial, we explored the @ConfigurationProperties annotation in Spring. We learned that using @ConfigurationProperties in Spring Boot, we can read and bind application-level Properties or YAML file configurations into a Java bean.

Spring uses a relaxed mapping strategy to match property field names with the java class field names. Thus the properties or yaml file fields can exist in UPPER_CASE, kebab-case, camelCase, or underscore_notation.

Then we explored ways of mapping simple properties with a flat structure or using prefix attributes to bind properties subsets based on a common prefix. While doing so, we understood that Spring uses Setter methods to inject the configurations in the Java class. However, we can use @ConstructorBinding annotation to use the constructor for properties injection. By doing so, we can make our application properties class immutable.

Refer to the GitHub Repository for the complete source code.

Further Reading: