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
- 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.
- 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).
- 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.
Published on Java Code Geeks with permission by Kapil Viren Ahuja, partner at our JCG program. See the original article here: HANDLING CONFIGURATIONS IN SPRING BOOT Opinions expressed by Java Code Geeks contributors are their own. |
The example is to small to be good. You wil never have an application that simple
Issues: Advocate yaml as property format. The format is much stricter and better organized than properties. Instead of @Getter and @Setter use @Data Don’t @Autowire. Make what you want to inject private final (you don’t want to be able to modify injected objects anyway) and add @RequiredArgsConstructor I don’t have the foggiest idea why you are using that private static for in the first case. @Service objects are singletons anyway. I would use the BungieClientProperties class, inject that into a BungieClientConfiguration class that can provide a WebClient as a bean or even an extended WebClient class. This way I can… Read more »