Handling Configurations in Spring Boot


I am currently getting back to coding in Java + Spring after about 8 years. In the last 8 years, the time I spent on coding has gone significantly as I am now into leadership roles that take me away from writing code. Having said that, I need to understand some level of coding, especially in the Java world, as that’s the language in which I find most of my projects, and I cannot help my teams effectively unless I am familiar with coding. So much has changed since I stopped coding, and I am learning everything again. This is the first of the many articles I will write to get as I know new things. Also, I am building an application more from my personal fruition. I generally cannot spend consistent time, which allows me to spend more time learning instead of trying to meet the deadlines of an actual life client project.

In this post, I will talk about how to use externalize configurations in a Spring Boot application.


1. Overview

We will use Spring Boot’s default setup to create some configurations and read them in our application. We will also look at a simple key-value way of setting up properties and YAML based configurations.

I prefer using YAML and from this point onwards I will only be using YAML bases setups.


2. Initial Setup

During my implementation run, I noticed that I was eventually required to use the spring-boot-configuration-processor dependency. Else I would get an error, and the code would not compile. I didn’t find much on why this is needed in my research, but adding this dependency resolved the issue for me.

The second dependency I have added is for Actuator, which gives us some exciting tools. This is unnecessary, but if you are looking to debug your properties and find more configurations to work with, I will suggest you add this as well.

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

3. Setting up Configuration Files

The following code is for application.properties file which is the default format you will get in Spring Boot.

# This is to expose all the endpoints for Actuator. use * for all ONLY in DEV
management.endpoints.web.exposure.include=*

# Custom Properties
bungie.rootPath="https://www.bungie.net/Platform"
bungie.apiKey=000999888111 

The following is same properties for in YAML format.

bungie:
  rootPath: "https://www.bungie.net/Platform"
  apiKey: 000999888111

4. Creating the Bean that will read these configurations

Now that we have created the properties, we have 2 use cases that we need to consider when reading the properties.

  • Reading One off property – In this case, we may need to create a property that needs to be read once or can’t be categorised with any other property. In this case, you can use the @Value annotation to read it.
  • Reading a set of properties – In our example, we have already identified two grouped properties under the “bungie” category. This is how I prefer to create properties. I do not like having orphan properties, and hence we will only see how to set these up. The following example will show us creating a Java Bean/Configuration, which will be able to read our property file and populate the object.
package io.howtoarchitect.destinyclanwars.config;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConfigurationProperties("bungie")
@Getter @Setter
public class BungieClientSettings {
    private String rootPath;
    private String apiKey;
}

If you observe the code block above, you will notice a few things:

  • We have used @Configuration to let the Spring application know that this is a bean and should be initialised as such
  • @Getter and @Setter are from the Lombok package, giving us default Getters and Setters. These are mandatory as Spring application will always need these getters and setters.
  • @ConfigurationProperties is the annotation that does the main trick here. It will go through all the properties available in our context and will search for any that have been mapped user “bungie”. Once found, this annotation will map the YAML/Properties file values and add them to our strings.

5. Consuming Properties

Once you have this setup, the last step is to read these properties in our application. We will be reading these properties in another Spring bean/service.

package io.howtoarchitect.destinyclanwars.bungieclient;

import io.howtoarchitect.destinyclanwars.config.BungieClientSettings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;

@Service
public class BungieClient {
    private static WebClient client;

    @Autowired
    private BungieClientSettings bungieClientSettings;

    public WebClient getDefaultClient() {
        client = WebClient.builder()
                .baseUrl(bungieClientSettings.getRootPath())
                .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .defaultHeader("X-API-KEY", bungieClientSettings.getApiKey())
                .build();

        return client;
    }
}

You will notice that I have added BungieClientSettings as an @Autowired dependency. This injects the bean in my class, and when I need to access these properties, all I need to do is bungieClientSettings.getAPIKey() and bungieClientSettings.getRootPath().

Conclusion

This is all you need to do is to externalise your properties. This is important to set up early on because if you do not, you will end up having many of these scattered in classes, and moving across to multiple environments will become hard.

In next few articles, we will look at

  1. How to use environment-based configuration. In my case, I will have different keys for development and production, and I will be able to handle the same.
  2. Use Spring’s cloud configuration Server, which will allow us to centralise our configurations into 1 project and also be able to swap designs without having to deploy the code (Configuration as Code pattern).
  3. Finally, we will look at how can we secure some of these properties via encryption. For example, my apiKeys need to be confirmed. I have used random values in the samples that I have given, but in my application, I need keys to be valid and not exposed via GITHUB repo plain text files.

Initiate your idea here...

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s