【问题标题】:Bind list of objects using Guice + Kotlin使用 Guice + Kotlin 绑定对象列表
【发布时间】:2016-08-06 03:06:05
【问题描述】:

我正在使用以下控制器定义在 Kotlin 中编写 JavaFX 应用程序:

class MainController {

    @Inject private lateinit var componentDescriptors: List<ComponentDescriptor>
    /* More code goes here */

}

我正在使用 Guice 进行依赖管理。我正在尝试注入通过java.util.ServiceLoader 加载的类实例列表。我的问题是定义一个绑定,它将加载的对象实例列表注入到声明的字段中。我尝试了基于注释的配置:

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 会知道将什么注入这个字段。

【问题讨论】:

    标签: dependency-injection guice kotlin


    【解决方案1】:

    是的,它是由于差异而发生的,但有办法让它发挥作用。

    class MainController {
        @JvmSuppressWildcards
        @Inject
        private lateinit var componentDescriptors: List<ComponentDescriptor>    
    }
    

    默认情况下,Kotlin 为 componentDescriptors 字段生成 List&lt;? extends ComponentDescriptor&gt; 签名。 @JvmSuppressWildcards 使它生成一个简单的参数化签名List&lt;ComponentDescriptor&gt;。

    【讨论】:

    • 有没有办法让构造函数注入工作?我尝试将注释放在要注入 Set 的构造函数参数之前,但它不起作用。当我将@JvmSuppressWildcards 注释放在类上时它确实有效,但我不想为每个类都这样做......
    • @JanWytze 您是否尝试过将此注释应用于构造函数?
    • This annotation is not applicable to target 'constructor'
    【解决方案2】:

    @Michael 给出了正确的answer 和解释。下面是一个针对喜欢测试其模块的人对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)
      }
    }
    

    【讨论】:

      【解决方案3】:

      @Michael 评论有效。如果要在构造函数中进行注入,则需要执行类似

      的操作
      class MainController @Inject consturctor(
          private var componentDescriptors: List<@JvmSuppressWildcards ComponentDescriptor>  
      ) {}
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-08-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-03-12
        • 2012-09-28
        • 2016-02-17
        • 1970-01-01
        相关资源
        最近更新 更多