1

I've so far used Java's ServiceLoader to discover implementations of interfaces my app provides at runtime in the classpath, i.e. a classic plugin approach. I'm now starting to use Koin in the app (for Dependency Injection) and wondering whether I could actually use Koin to replace ServiceLoader and implement plugin discovery with Koin (i.e. use Koin as Service Locator, which it actually seems to be rather than a DI framework).

What I'm struggling with is that in all the examples for Koin I saw so far, the implementations passed to single / singleOf are static, i.e. known at compile time:

import dev.schuberth.stan.exporters.CsvExporter
import dev.schuberth.stan.exporters.ExcelExporter

// ...

interface Exporter

// ...

module {
    // The "CsvExporter" and "ExcelExporter" implementations have to be
    // known at compile time for this to work.
    singleOf(::CsvExporter) bind Exporter::class
    singleOf(::ExcelExporter) bind Exporter::class
}

// ...

val exporters = app.koin.getAll<Exporter>()

How would I implement discovery for an implementation of an interface that's contained in an external JAR that is only added to the app's classpath at runtime? In that case, I neither now the class's name nor the class's package at compile time (so even the @ComponentScan annotation would not work).

My hunch is that I'm supposed to create a Koin module already in the plugin's code, not only in the app's code, and somehow use that via Koin from the app, but I have no idea how.

sschuberth
  • 28,386
  • 6
  • 101
  • 146
  • Yes you would have to declare a Module in the plugin and add that module to the app when you initialize Koin. Something lile `startKkoin { modules(pluginModule...`. Since Koin has no compile time checks in the app (you can't use annotations for your use case), there's the chance of runtime exceptions if the required implementation isn't provided by the plugin. – Emanuel Moecklin Oct 30 '22 at 19:52
  • A problem is: *Where* to declare that module in the plugin? As there is no main function / entry point in a plugin's code, where to put the module code to ensure it's being called before the app starts Koin (with that module)? – sschuberth Oct 30 '22 at 21:33
  • You declare it wherever you want ;-). I usually create a property like `val myModule = module {` but you could use a top-level function that returns the module. In the app you need to add it as mentioned in my first comment. To mitigate the risk of undefined dependencies you can write a test case as explained here: https://insert-koin.io/docs/reference/koin-test/checkmodules. – Emanuel Moecklin Oct 30 '22 at 21:41
  • Also I just noticed that you bind two classes to the same interface. For this case you'd have to use the `named` qualifier -> https://insert-koin.io/docs/reference/koin-core/definitions#definition-naming--default-bindings. – Emanuel Moecklin Oct 30 '22 at 21:46
  • But the app must not know how the module variable is named. Also, just defining a dummy top-level variable (that is never accessed) does not work, the module code is not called in that case, I just checked. – sschuberth Oct 30 '22 at 21:57
  • PS: Binding to the same interface with this syntax actually works, but [using the lambda syntax does not work](https://github.com/InsertKoinIO/koin/issues/1463). – sschuberth Oct 30 '22 at 21:58
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/249177/discussion-between-emanuel-moecklin-and-sschuberth). – Emanuel Moecklin Oct 30 '22 at 22:09

0 Answers0