If you are writing applications in Kotlin, you may be using default value Kotlin feature. Be careful though when you use default values for injected dependencies in Spring applications. It may lead to unexpected behaviour, and Spring will not even warn you.
Here is an example.
We have Ford Prefect who depends on humans:
@Component
class FordPrefect(
private val humans: List<Human>,
) {
@PostConstruct
fun init() {
log.info("humans: $humans")
}
companion object {
private val log = LoggerFactory.getLogger(FordPrefect::class.java)
}
}
Where a human is defined as
interface Human
And then we have a couple of humans who depend on Ford:
@Component
class ArthurDent(
private val fordPrefect: FordPrefect,
) : Human {
override fun toString() = "Arthur"
}
@Component
class TrishaMcMillan(
private val fordPrefect: FordPrefect,
) : Human {
override fun toString() = "Trisha"
}
If you start the spring application with these three components, it will fail with UnsatisfiedDependencyException
due to the circular dependency, which is expected:
┌─────┐
| arthurDent defined in file [/.../ArthurDent.class]
↑ ↓
| fordPrefect defined in file [/.../FordPrefect.class]
└─────┘
However, the situation changes if you add the default value for humans
property:
private val humans: List<Human> = emptyList(),
Now the application starts without any problem or warning. And it prints an empty list of humans:
INFO 6380 --- [ main] gDependencyWithDefaultValueApplicationKt : Starting SpringDependencyWithDefaultValueApplicationKt using Java 17.0.8.1 with PID 6380 (...)
INFO 6380 --- [ main] gDependencyWithDefaultValueApplicationKt : No active profile set, falling back to 1 default profile: "default"
INFO 6380 --- [ main] d.p.s.d.defaultvalue.FordPrefect : humans: []
INFO 6380 --- [ main] gDependencyWithDefaultValueApplicationKt : Started SpringDependencyWithDefaultValueApplicationKt in 1.179 seconds (process running for 2.067)
I bet you do not expect such a thing. By setting the default value you probably expect humans
to be set with an empty list when no beans of Human
are available. Or fail as in the previous scenario due to the circular dependency. However, this is not what is happening.
Actually, if you delete both ArthurDent
and TrishaMcMillan
components, the application will start and the output will be exactly the same - now as expected.
So, remember this and be careful when you are setting default values for injected dependencies.
The full source code is available on GitHub.
Dream your code, code your dream.
Top comments (0)