Authors Top

If you have a few years of experience with the Kotlin language and server-side development, and you’re interested in sharing that experience with the community, have a look at our Contribution Guidelines.

1. Overview

In this tutorial, we’ll learn how to use Spring Boot’s @ConfigurationProperties annotation in conjunction with Kotlin’s data class as part of a Spring Boot configuration.

2. @ConfigurationProperties in a Kotlin Data Class

Spring Boot provides a method to configure individual external properties using the @Value(“${property}”) annotation. However, this method becomes cumbersome, especially if we’re working with many properties or if the properties are hierarchical in nature.

Therefore, Spring Boot provides an alternative @ConfigurationProperties annotation that makes it convenient for us to map the external application properties to Kotlin or Java bean objects.

2.1. Enabling @ConfigurationProperties

Spring Boot provides a couple of methods to bind and register the configuration class as a bean. One option is to annotate a class with @ConfigurationProperties and then use it with the @Bean method in the @Configuration class. Another option is to enable the support for @ConfigurationProperties as a bean and register it by using the @EnableAutoConfiguration annotation.

Let’s look at an example where we’re configuring a third-party API endpoint. As part of that, we can externalize the properties like the client id, API URL, and API key. The application.yml file will look like:

api:
    clientId: "client123"
    url: "https://api.url.com"
    key: "api-access-key"

Let’s look at the definition of a @ConfigurationProperties data class for the above properties:

@ConfigurationProperties(prefix = "api")
data class ApiConfiguration(
    var clientId: String = "",
    var url: String = "",
    var key: String = ""
)

In the above code, note that the properties are defined as var. In the subsequent section, we’ll see how to refactor this code to use val for property definitions.

Now, let’s register the @ConfigurationProperties annotated class as a @Bean:

@Configuration
class AppConfiguration {
    @Bean
    fun apiConfiguration(): ApiConfiguration {
        return ApiConfiguration()
    }
}

Finally, let’s enable the configuration class scanning to load our @ConfigurationProperties class using @EnableAutoConfiguration:

@Configuration
@EnableConfigurationProperties(ApiConfiguration::class)
class AppConfiguration

2.2. Using @ConfigurationProperties

Since @ConfigurationProperties is registered as a bean, we can inject it like any other Spring bean. We can inject the ApiConfiguration bean into a @Service class:

@Service
class ApiService(val apiConfiguration: ApiConfiguration)

As a result of the injection, we have access to external properties via the ApiConfiguration class inside the ApiService class.

3. Binding @ConfigurationProperties in Kotlin Data Class

Let’s see how to use the Kotlin data class in a mutable and immutable fashion for defining the @ConfigurationProperties.

3.1. JavaBean Binding Option

Let’s look at the ApiConfiguration again:

@ConfigurationProperties(prefix = "api")
data class ApiConfiguration(
    var clientId: String = "",
    var url: String = "",
    var key: String = ""
)

If we look at the ApiConfiguration class member properties, we see that these are defined as var and not val as Kotlin recommends for final values. Thus, the var properties are mutable.

Moreover, we must initialize all properties with initial values. Otherwise, we’ll see the following error:

***************************
APPLICATION FAILED TO START
***************************

Description:
Parameter 0 of constructor in com.example.kotlin.kotlinspring.config.ApiConfiguration required a bean of type 'java.lang.String' that could not be found.

Action:
Consider defining a bean of type 'java.lang.String' in your configuration.

Disconnected from the target VM, address: '127.0.0.1:49351', transport: 'socket'

Process finished with exit code 1

For @ConfigurationProperties class with properties of var type, Spring Boot instantiates the class using the default no-arguments constructor. Consequently, it uses accessors (getters and setters) to set the values. Moreover, we know that a Kotlin data class inherently provides the getters and setters for the defined properties.

3.2. Constructor Binding Option

Since Spring Boot v2.2.0, there’s an alternative to the JavaBean binding option: to instantiate and set the @ConfigurationProperties using the @ConstructorBinding annotation.

We can annotate at the class level or at a constructor, in the case of multiple constructors, to indicate constructor binding should be used. We must ensure that the constructor takes in all the parameters that need to be bound.

By using @ConstructorBinding, all properties are bound during the instantiation and values are final. As a result, we can use val to define the properties. This makes our data class definition much neater.

Note that in order to use @ConstructorBinding, we must register the @ConfigurationProperties bean using the @EnableAutoConfiguration or configuration property scanning. As a result, we cannot use the @ConstructorBinding if we use regular Spring bean registering methods like @Bean or @Component to create @ConfigurationProperties. Moreover, we’ve already seen how to register @ConfigurationProperties in an earlier section.

Let’s rewrite our example using the @ConstructorBinding annotation:

@ConfigurationProperties(prefix = "api")
@ConstructorBinding
data class ApiConfiguration(
    val clientId: String,
    val url: String,
    val key: String
)

Note the use of val in the above example.

4. Conclusion

In this article, we learned how to register and use the Spring Boot @ConfigurationProperties bean using a Kotlin data class. Additionally, we also looked at how to use the Kotlin data class in a mutable and immutable fashion to bind the external properties to the @ConfigurationProperties bean.

As always, code snippets can be found over on GitHub.

Authors Bottom

If you have a few years of experience with the Kotlin language and server-side development, and you’re interested in sharing that experience with the community, have a look at our Contribution Guidelines.

Comments are closed on this article!