Reading Nested Properties in Spring Boot

A guide on How to read nested properties configurations in Spring Boot using @ConfigurationProperties.

Overview

Spring Boot @ConfigurationProperties annotation provides an easy way to read and bind application configurations from a Properties or a YAML file into a Plain Old Java Object – POJO.

This tutorial will demonstrate different ways of reading Nested Application Properties using the @ConfigurationProperties annotation of Spring Boot. If you are looking to read a Simple Properties or YAML file or examples of using the prefix attribute of the @ConfigurationPropertis, we recommend reading Using @ConfigurationProperties in Spring Boot.

Nested Application Properties

The Properties or YAML files are useful for externalizing application configurations in an application. That helps change the configurations without modifying source files. Spring Boot provides a flexible way of externalizing configurations and binding the configurations into a Java POJO upon startup.

We often group related configurations under a common prefix in a Properties or a YAML file. Such groups can also contain nested subgroups of configurations.

login-service:
  login-url: https://login.example.com
  username: login_user
  password: password123
  header:
    auth_token: TOKEN
    content: C_TYPE

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

Consider the above yaml configuration, where we have two separate groups of properties. Let’s see examples of reading these groups of properties using Static Inner classes or creating dedicated properties classes for them.

@ConfigurationProperties using Dedicated Classes

Looking at the properties above, we can figure out three groups of properties – User Service Properties, Login Service Properties and Header properties – nested under login service properties. Let’s model this properties structure by creating dedicated Properties beans.

First is the bean to hold User Service Properties – Let’s call it UserProperties:

public class UserProperties {
  private String url;
  private String username;
  private String password;

  // Constructor, Getter and Setter Methods
}Code language: Java (java)

Note that we haven’t marked this class with @ConfigurationProperties, because it is not required.

Next, we will create a class to bind the header properties nested under the login service.

public class LoginHeaderProperties {
  private String authToken;
  private String content;

  // Constructor, Getter and Setter Methods
}Code language: Java (java)

Now, let’s create LoginProperties to bind login-service properties.

public class LoginProperties {
  private String loginUrl;
  private String username;
  private String password;
  private LoginHeaderProperties header;

  // Constructor, Getter and Setter Methods
}Code language: Java (java)

Note that it references the Header Properties, and the field name is in accordance with the nested property prefix.

Lastly, we will create our @ConfigurationProperties class that holds the reference to both of our main properties groups.

@Configuration
@ConfigurationProperties
public class NestedProperties {
    private LoginProperties loginService;
    private UserProperties userService;

    // Constructor, Getter and Setter Methods
    // ToString method
}Code language: Java (java)

We’ll also add a well-formatted toString() implementation to print all the nested fields and their values. We will use it to print the populated beans once the application starts.

Let’s start the application and print the NestedProperties instance.

* Nested Properties
loginService: 
	loginUrl: https://login.example.com
	username: login_user
	password: password123
	header: 
		authToken: TOKEN
		content: C_TYPE
userService: 
	url: https://users.example.com
	username: user_name
	password: strong-password

We can see we have correctly bound the nested application yaml (or a properties) file into our relational class structure.

@ConfigurationProperties using Nested Inner Classes

The previous way of binding nested properties using @ConfigurationProperties works. However, if too many groups and nesting levels exist, we will create a bunch of classes to hold respective properties.

Alternatively, we can use nested inner classes to represent the properties sub-groups.

@Configuration
@ConfigurationProperties
public class NestedClassesProperties {
  private LoginProperties loginService;
  private UserProperties userService;

  // Constructor, Getter and Setter methods
  // ToString method

  public static class LoginProperties {
    private String loginUrl;
    private String username;
    private String password;
    private LoginHeaderProperties header;

    // Constructor, Getter and Setter methods

    public static class LoginHeaderProperties {
      private String authToken;
      private String content;

      // Constructor, Getter and Setter methods
    }
  }

  public static class UserProperties {
    private String url;
    private String username;
    private String password;
       
    // Constructor, Getter and Setter methods
  }
}Code language: Java (java)

Here, we have only one class and several static inner classes, where each inner class represents a particular group of the properties fields.

We have also added a well-formatted toString() method to print all of the properties upon startup.

* Nested Properties with Inner Classes
loginService: 
	loginUrl: https://login.example.com
	username: login_user
	password: password123
	header: 
		authToken: TOKEN
		content: C_TYPE
userService: 
	url: https://users.example.com
	username: user_name
	password: strong-password

We have printed the NestedClassesProperties instance using a @PostConstruct method, and the output confirms that all of the nested properties are read correctly.

Summary

This quick tutorial taught us to use Spring @ConfigurationProperties to map a nested properties file or a yaml file into Java Beans. We worked on a Spring Boot application having application.yaml file with nested subgroups of properties.

We modelled the application configurations in the first example by creating multiple classes and used @ConfigurationProperties to read the nested properties. In the last example, instead of creating dedicated classes, we used static inner classes and achieved the same result.

Please refer to our GitHub Repository for the complete source code of the examples used in this tutorial.

More like this: