使用 Guice + Kotlin 绑定对象列表
Bind list of objects using Guice + Kotlin
我正在使用以下控制器定义在 Kotlin 中编写 JavaFX 应用程序:
class MainController {
@Inject private lateinit var componentDescriptors: List<ComponentDescriptor>
/* More code goes here */
}
我正在使用 Guice 进行依赖管理。我正在尝试注入通过 java.util.ServiceLoader
加载的 class 实例列表。我的问题是定义一个绑定,它将加载的对象实例列表注入到声明的字段中。我尝试了基于注释的配置:
internal class MyModule: AbstractModule() {
override fun configure() { }
@Provides @Singleton
fun bindComponentDescriptors(): List<ComponentDescriptor> =
ServiceLoader.load(ComponentDescriptor::class.java).toList()
}
和多绑定扩展(在 corse 的字段定义中将 List 切换为 Set):
internal class MyModule: AbstractModule() {
override fun configure() {
val componentDescriptorBinder = Multibinder.newSetBinder(binder(), ComponentDescriptor::class.java)
ServiceLoader.load(ComponentDescriptor::class.java).forEach {
componentDescriptorBinder.addBinding().toInstance(it)
}
}
}
但这两种方法都会导致相同的错误:
No implementation for java.util.List<? extends simpleApp.ComponentDescriptor> was bound.
while locating java.util.List<? extends simpleApp.ComponentDescriptor>
for field at simpleApp.MainController.componentDescryptors(MainController.kt:6)
while locating simpleApp.MainController
1 error
at com.google.inject.internal.InjectorImpl.getProvider(InjectorImpl.java:1042)
at com.google.inject.internal.InjectorImpl.getProvider(InjectorImpl.java:1001)
at com.google.inject.internal.InjectorImpl.getInstance(InjectorImpl.java:1051)
at com.gluonhq.ignite.guice.GuiceContext.getInstance(GuiceContext.java:46)
at javafx.fxml.FXMLLoader$ValueElement.processAttribute(FXMLLoader.java:929)
at javafx.fxml.FXMLLoader$InstanceDeclarationElement.processAttribute(FXMLLoader.java:971)
at javafx.fxml.FXMLLoader$Element.processStartElement(FXMLLoader.java:220)
at javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:744)
at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2707)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2527)
... 12 more
我开始怀疑它在某种程度上与 Kotlin 通用变体和 Guice 严格类型检查有关。但我不知道如何声明绑定,以便 Guice 知道将什么注入到该字段中。
是的,它是由于差异而发生的,但有一种方法可以让它发挥作用。
class MainController {
@JvmSuppressWildcards
@Inject
private lateinit var componentDescriptors: List<ComponentDescriptor>
}
默认情况下,Kotlin 为 componentDescriptors
字段生成 List<? extends ComponentDescriptor>
签名。 @JvmSuppressWildcards
使它生成一个简单的参数化签名 List<ComponentDescriptor>
.
@Michael给出了正确的和解释。这是一个单元测试策略的示例 Set
多绑定,供那些喜欢测试其模块的用户使用:
class MyModuleTest {
@JvmSuppressWildcards
@Inject
private lateinit var myTypes: Set<MyType>
@Before fun before() {
val injector = Guice.createInjector(MyModule())
injector.injectMembers(this)
}
@Test fun multibindings() {
assertNotNull(myTypes)
assertTrue(myTypes.iterator().next() is MyType)
}
}
@Michael 评论有效。如果你想在构造函数中进行注入,你需要做类似
class MainController @Inject consturctor(
private var componentDescriptors: List<@JvmSuppressWildcards ComponentDescriptor>
) {}
我正在使用以下控制器定义在 Kotlin 中编写 JavaFX 应用程序:
class MainController {
@Inject private lateinit var componentDescriptors: List<ComponentDescriptor>
/* More code goes here */
}
我正在使用 Guice 进行依赖管理。我正在尝试注入通过 java.util.ServiceLoader
加载的 class 实例列表。我的问题是定义一个绑定,它将加载的对象实例列表注入到声明的字段中。我尝试了基于注释的配置:
internal class MyModule: AbstractModule() {
override fun configure() { }
@Provides @Singleton
fun bindComponentDescriptors(): List<ComponentDescriptor> =
ServiceLoader.load(ComponentDescriptor::class.java).toList()
}
和多绑定扩展(在 corse 的字段定义中将 List 切换为 Set):
internal class MyModule: AbstractModule() {
override fun configure() {
val componentDescriptorBinder = Multibinder.newSetBinder(binder(), ComponentDescriptor::class.java)
ServiceLoader.load(ComponentDescriptor::class.java).forEach {
componentDescriptorBinder.addBinding().toInstance(it)
}
}
}
但这两种方法都会导致相同的错误:
No implementation for java.util.List<? extends simpleApp.ComponentDescriptor> was bound.
while locating java.util.List<? extends simpleApp.ComponentDescriptor>
for field at simpleApp.MainController.componentDescryptors(MainController.kt:6)
while locating simpleApp.MainController
1 error
at com.google.inject.internal.InjectorImpl.getProvider(InjectorImpl.java:1042)
at com.google.inject.internal.InjectorImpl.getProvider(InjectorImpl.java:1001)
at com.google.inject.internal.InjectorImpl.getInstance(InjectorImpl.java:1051)
at com.gluonhq.ignite.guice.GuiceContext.getInstance(GuiceContext.java:46)
at javafx.fxml.FXMLLoader$ValueElement.processAttribute(FXMLLoader.java:929)
at javafx.fxml.FXMLLoader$InstanceDeclarationElement.processAttribute(FXMLLoader.java:971)
at javafx.fxml.FXMLLoader$Element.processStartElement(FXMLLoader.java:220)
at javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:744)
at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2707)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2527)
... 12 more
我开始怀疑它在某种程度上与 Kotlin 通用变体和 Guice 严格类型检查有关。但我不知道如何声明绑定,以便 Guice 知道将什么注入到该字段中。
是的,它是由于差异而发生的,但有一种方法可以让它发挥作用。
class MainController {
@JvmSuppressWildcards
@Inject
private lateinit var componentDescriptors: List<ComponentDescriptor>
}
默认情况下,Kotlin 为 componentDescriptors
字段生成 List<? extends ComponentDescriptor>
签名。 @JvmSuppressWildcards
使它生成一个简单的参数化签名 List<ComponentDescriptor>
.
@Michael给出了正确的Set
多绑定,供那些喜欢测试其模块的用户使用:
class MyModuleTest {
@JvmSuppressWildcards
@Inject
private lateinit var myTypes: Set<MyType>
@Before fun before() {
val injector = Guice.createInjector(MyModule())
injector.injectMembers(this)
}
@Test fun multibindings() {
assertNotNull(myTypes)
assertTrue(myTypes.iterator().next() is MyType)
}
}
@Michael 评论有效。如果你想在构造函数中进行注入,你需要做类似
class MainController @Inject consturctor(
private var componentDescriptors: List<@JvmSuppressWildcards ComponentDescriptor>
) {}