InsertKoinIO / koin

Koin - a pragmatic lightweight dependency injection framework for Kotlin & Kotlin Multiplatform
https://insert-koin.io
Apache License 2.0
8.98k stars 710 forks source link

org.koin.core.error.NoBeanDefFoundException: No definition found for type 'java.util.List' when injecting list of dependencies #1715

Closed alexstaeding closed 5 months ago

alexstaeding commented 10 months ago

Describe the bug According to the docs, it is possible to inject a List of dependencies by binding several implementations to a common supertype and injecting a List of that supertype.

I have a module with these bindings:

singleOf(::CommandRegistrar) {
    named("command")
    bind<Registrar>()
}
singleOf(::ListenerRegistrar) {
    named("listener")
    bind<Registrar>()
}

I try to inject a List of these dependencies in another service:

class CatalystVelocityPlugin(
    // ...
    private val registrars: List<Registrar>,
) {

which results in this error:

Caused by: org.koin.core.error.NoBeanDefFoundException: No definition found for type 'java.util.List'. Check your Modules configuration and add missing type and/or qualifier!
    at org.koin.core.scope.Scope.throwDefinitionNotFound(Scope.kt:301) ~[?:?]
    at org.koin.core.scope.Scope.resolveValue(Scope.kt:271) ~[?:?]
    at org.koin.core.scope.Scope.resolveInstance(Scope.kt:233) ~[?:?]
    at org.koin.core.scope.Scope.get(Scope.kt:212) ~[?:?]

Injecting CommandRegistrar and ListenerRegistrar separately works fine.

To Reproduce Steps to reproduce the behavior:

  1. Bind multiple implementations to the same interface
  2. Inject a list of said interface
  3. See error

Expected behavior It should be possible to inject all instances bound to this interface as in the docs.

Koin module and version: Reproduced on:

Snippet or Sample project to help reproduce MRE:

import org.koin.core.module.dsl.bind
import org.koin.core.module.dsl.singleOf
import org.koin.dsl.koinApplication
import org.koin.dsl.module

interface Foo {
    fun bar()
}

class FooA : Foo {
    override fun bar() = println("bar")
}

class FooB : Foo {
    override fun bar() = println("baz")
}

class Thingy(private val foos: List<Foo>) {
    fun doThing() = foos.forEach { it.bar() }
}

fun main() {
    koinApplication {
        modules(
            module {
                singleOf(::FooA) { bind<Foo>() }
                singleOf(::FooB) { bind<Foo>() }
                singleOf(::Thingy)
            }
        )
    }.koin.get<Thingy>().doThing()
}
alexstaeding commented 10 months ago

Naturally, immediately after submitting the issue, I discovered my blunder:

The singleOf utility method assumes all constructor parameters are to be initialized with get() and does not use getAll() as in the example in the docs.

Is there a sensible way of detecting a multi-bind in singleOf and using getAll() instead?

stale[bot] commented 5 months ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.