Spring Kotlin DSL: получить все бобы определенного типа

Предположим, у меня есть интерфейс Yoyo и различные реализации этого интерфейса:

interface Yoyo { 
    fun haha() {
        println("hello world")
    }
}

@Component class Yoyo1 : Yoyo
@Component class Yoyo2 : Yoyo
@Component class Yoyo3 : Yoyo
@Component class YoyoN : Yoyo

Теперь я хотел бы создать экземпляр всех компонентов и выполнить некоторую логику после инициализации контекста:

@SpringBootApplication
class YoyoApp

fun main(args: Array<String>) {
    SpringApplicationBuilder()
            .sources(YoyoApp::class.java)
            .initializers(beans {
               bean {
                   CommandLineRunner {
                       val y1 = ref<Yoyo1>()
                       val y2 = ref<Yoyo2>()
                       val y3 = ref<Yoyo3>()
                       val yN = ref<YoyoN>()
                       arrayOf(y1, y2, y3, yN).forEach { it.haha() }
                   }
               }
            })
            .run(*args)
}

Вместо того, чтобы вручную получать ref для всех bean-компонентов (что довольно утомительно), я хотел бы сделать это:

val list = ref<List<Yoyo>>()
list.forEach { it.haha() }

Однако я получаю исключение:

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'java.util.List<?>' available

Я знаю, что мог бы сделать это вместо этого, но вместо этого я хотел бы использовать новый DSL Kotlin:

@Component
class Hoho : CommandLineRunner {
    @Autowired
    lateinit var list: List<Yoyo>

    override fun run(vararg args: String?) {
        list.forEach { it.haha() }
    }
}

Является ли это возможным? Есть идеи?

PS Вот суть.

2 ответа

Решение

ref Функция, используемая в DSL, может быть найдена здесь в источнике фреймворка. Не существует эквивалента для получения всех бинов типа, но вы можете добавить свое собственное расширение к BeanDefinitionDsl класс для этого:

inline fun <reified T : Any> BeanDefinitionDsl.refAll() : Map<String, T> {
    return context.getBeansOfType(T::class.java)
}

Единственная проблема в том, что context требуется для этого internal в текущей выпущенной версии фреймворка. Эта фиксация, сделанная 8 дней назад, делает ее общедоступной "для расширенных сценариев использования", но с тех пор не было выпущено новой версии фреймворка, поэтому она пока недоступна.

(Тот же коммит также заставляет класс расширять непосредственно BeanDefinitionDsl класс а не BeanDefinitionDsl.BeanDefinitionContext.)


Вывод: вам, вероятно, придется подождать следующего релиза, включающего коммит, упомянутый выше, и тогда вы сможете создать это расширение для себя. Я также отправил запрос на удаление в надежде, что он может быть включен в сам фреймворк.

context упомянутый в предыдущем ответе @zsmb13 был оставлен internal в пользу provider<Any>() функция (начиная с весны 5.1.1). В итоге я получил следующее:

interface Yoyo {
    fun haha() {
        println("hello world from: ${this.javaClass.canonicalName}")
    }
}

@Component class Yoyo1 : Yoyo
@Component class Yoyo2 : Yoyo
@Component class Yoyo3 : Yoyo
@Component class YoyoN : Yoyo


@SpringBootApplication
class YoyoApp

fun main(args: Array<String>) {
    SpringApplicationBuilder()
        .sources(YoyoApp::class.java)
        .initializers(beans {
            bean {
                CommandLineRunner {
                    val list = provider<Yoyo>().toList()
                    list.forEach { it.haha() }
                }
            }
        })
        .run(*args)
}
Другие вопросы по тегам