Can constructor dependencies be dynamically injected, in Kotlin, without runtime reflection?

70 views Asked by At

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.

0

There are 0 answers