I have the class below that fetches an instance of any given type from a DI framework; in this case, Koin.
class KoinLoader(private val container: Koin = Koin()) : DependencyLoader() {
override fun <TClass : Any> instantiate(cls: KClass<TClass>): TClass {
return container.get(cls)
}
}
However, this requires that the class is already explicitly declared to Koin. To allow automagic creation of the class itself I could use reflection to get the classes dependencies from the DI framework. Something like this:
override fun <TClass : Any> instantiate(cls: KClass<TClass>): TClass {
val constructor = cls.constructors.first()
val dependencies = constructor.parameters.map { it.type.classifier as KClass<*> }
val resolvedDependencies = dependencies.map { container.get(it) }
return constructor. Call(*resolvedDependencies.toTypedArray())
}
My question is: Is there any way of doing this, with any Kotlin DI framework, that doesn't use reflection at runtime?
I am curious whether it's possible to keep the dynamic dependency injection, that works for any class (as long as its dependencies are previously declared, but with the advantages that a compile-time solution would bring? Presumably this would have to involve code-generation annotations but the key point here is that only this "loader" class contains annotations and therefore the classes that are being instantiated are decoupled from the framework.
Context:
I'm developing a message bus library, in Kotlin, that acts as a kind of "use-case" orchestrator. It processes use-cases, or application services in a domain-driven-design application that use the command pattern. A command or event object is passed to the bus and one (in the case of commands), or multiple (in the case of events), handlers process this object to achieve a required outcome. I have implemented DI container integration as an optional feature; currently only using Koin. But this requires that each handler is explicitly declared to Koin; along with its dependencies. This piqued my curiosity and got me thinking whether there's any way to automate this step.
I'm also interested how inferior a reflection-based solution really is. Given the context above, is there much of a performance impact for this use of reflection? There is the obvious drawback of no compile-time safety for ensuring that dependencies have previously been declared but IMO this can be mitigated with well written tests.