
Learn through the super-clean Baeldung Pro experience:
>> Membership and Baeldung Pro.
No ads, dark-mode and 6 months free of IntelliJ Idea Ultimate to start with.
Last updated: February 20, 2024
One common requirement in Spring Boot applications is to inject external configuration properties into Spring Beans. The @ConfigurationProperties annotation allows us to bind a set of configuration properties into a class. Alternatively, we can leverage the @Value annotation to inject arbitrary configuration properties into a Spring Bean.
In this quick tutorial, we’ll explore how to inject configuration properties using @Value.
Let’s create a simple YAML file named application-inject-value.yml under src/main/resources as our application’s configuration:
my-app:
magic-number: 42
magic-string: "It's a magic string"
magic-flag: true
magic-string-with-default: "It's another magic string"
To make @PropertySource load our YAML configuration file, we create the YamlPropertySourceFactory class:
class YamlPropertySourceFactory : PropertySourceFactory {
override fun createPropertySource(@Nullable name: String?, encodedResource: EncodedResource): PropertySource<*> =
PropertiesPropertySource(encodedResource.resource.filename,
YamlPropertiesFactoryBean().also { it.setResources(encodedResource.resource) }.getObject()
)
}
Then, we can use the @PropertySource annotation to parse our YAML configuration:
@PropertySource(value = ["classpath:application-inject-value.yml"], factory = YamlPropertySourceFactory::class)
@SpringBootApplication(scanBasePackages = ["com.baeldung.theValueAnnotation"])
class KotlinValueInjectionApplication
Next, we’ll create a Spring component and inject configuration properties based on this YAML file.
When we work with @Value in Java, we can inject configuration property in this way: @Value(“${prop.name.from.config}”).
In Kotlin, we adhere to a similar approach. However, due to Kotlin’s String template using ‘$‘ to reference variables or expressions, we must escape ‘$‘ within the @Value annotation. Now, let’s create a Spring component and inject several properties from our application-inject-value.yml:
@Component
class ValueBean(
@Value("\${my-app.magic-number}")
val magicNumber: Int,
@Value("\${my-app.magic-string}")
val magicString: String,
@Value("\${my-app.magic-flag}")
val magicFlag: Boolean,
)
As the ValueBean class shows, we applied the constructor injection approach to inject three configuration properties.
Next, we’ll verify whether these @Value annotations work as expected in a unit test. Let’s start with creating a test class:
@SpringBootTest
@TestConstructor(autowireMode = AutowireMode.ALL)
class TheValueAnnotationUnitTest(val valueBean: ValueBean) {
...
}
We used the @TestConstructor annotation to implement constructor injection for injecting the ValueBean Spring component within the test class. Subsequently, we can conduct verifications within a test function:
with(valueBean) {
assertEquals(42, magicNumber)
assertEquals("It's a magic string", magicString)
assertTrue(magicFlag)
}
The test passes if we run it. That is, these three configuration properties have been injected into the ValueBean bean as expected.
Sometimes, we may wish to offer a default value for situations where a property might be absent in the configuration file. To accomplish this, we can adopt the @Value(“\${prop.name:defaultValue”) pattern.
Now, let’s extend our ValueBean component by injecting two new properties with default values:
@Component
class ValueBean(
...
// with default values
@Value("\${my-app.not-defined-value:1024}")
val magicNumberWithDefault: Int,
@Value("\${my-app.magic-string-with-default:default Value}")
val magicStringWithDefault: String,
}
In the code above, both @Value injections are with default values.
Looking at the configuration file, my-app.not-defined-value is absent, while my-app.magic-string-with-default is explicitly defined. As a result, the default value 1024 is applied to magicNumberWithDefault, and the specified value is injected into magicStringWithDefault:
with(valueBean) {
assertEquals(1024, magicNumberWithDefault)
assertEquals("It's another magic string", magicStringWithDefault)
}
We’ve learned how to set non-null values as defaults when using @Value injections. Occasionally, when a property is missing in configuration files, we might prefer to treat null as the default.
Let’s further extend ValueBean to cover this case:
@Component
class ValueBean(
...
// with null as the default value
@Value("\${my-app.not-defined-value:null}")
val stringDefaultLiteralNull: String?,
@Value("\${my-app.not-defined-value:#{null}}")
val stringDefaultNull: String?
)
As Kotlin has nullable and non-nullable types, we must declare the fields nullable if we want their defaults to be null.
We can see that although both @Value attempt to inject non-existing properties and introduce defaults, one uses “prop.name:null”, and the other one employs “prop.name:#{null}“.
Next, let’s look at the outcome:
with(valueBean) {
assertEquals("null", stringDefaultLiteralNull)
assertNull(stringDefaultNull)
}
The test shows their difference clearly:
In this article, we explored using Spring’s @Value for injecting configuration properties in Kotlin, including how to introduce defaults for absent properties in the configuration file.