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.
Starting with Spring Boot v3.X the @ConstructorBinding annotation is no longer needed at the type level on @ConfigurationProperties and is not supported anymore. In case you have multiple constructors you will need to add this annotation to know which constructor should be used for property binding.
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")
data class ApiConfiguration @ConstructorBinding constructor(
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.