guice注入:getBinding / getExistingBinding / getProvider和getInstance之间的区别

时间:2017-11-23 21:20:48

标签: kotlin guice

我有PropertiesModule扩展AbstractModule并包含我在整个项目中使用的应用程序常量:

class PropertiesModule: AbstractModule(), Serializable {
  companion object {
    const val APP_NAME = "MyAppName"
    ...
  }
  override fun configure() {
    ...
  }
}

然后我使用PropertiesModule创建注入器:

...
val injector = Guice.createInjector(
  PropertiesModule(),
  ...
)

后来当我使用注入器获取属性值时,我有多个选择。我能做到:

val appName = injector.getInstance(
  Key.get(String::class.java, Names.named(PropertiesModule.APP_NAME))
)

val appName = injector.getExistingBinding(
  Key.get(String::class.java, Names.named(PropertiesModule.APP_NAME))
).provider.get()

val appName = injector.getProvider(
  Key.get(String::class.java, Names.named(PropertiesModule.APP_NAME))
).get()

我读到一般getInstance应该避免。但我不确定他们之间有什么区别。

1 个答案:

答案 0 :(得分:1)

如果您刚刚创建了注射器,请使用getInstance。否则,尝试从较窄的注射中获取物体。

getBindinggetExistingBinding 实际上都适用于reflective purposes

  

[Binding is]从键(类型和可选注释)到获取类型实例的策略的映射。此界面是introspection API的一部分,主要供工具使用。

虽然它可能在某种程度上起作用,但我建议不要使用getExistingBinding,如果只是因为它具有违反直觉的行为,即如果它们尚不存在则不创建实时绑定,如在the getExistingBinding docs中描述:

  

getBinding(Key)不同,这并不会尝试为不受约束的密钥创建即时绑定。   此方法是Guice SPI的一部分,旨在供工具和扩展使用。

getProvider(Class<T>)将从图中获取Provider<T>,这就像一个单键注入器,它只能从注入器创建或检索单个键的实例。将其视为创建一个lambda,为特定的Key调用getInstance,仅此而已。

getInstance是Injector上最重要的方法,我希望所有Guice应用程序至少调用一次。如果您刚刚创建了Injector,那么您可能希望从中获取实例,或者注入对象的@Inject字段。前者发生在getInstance,后者发生在injectMembers。在我看来,这是将第一个对象从图表中删除的最佳方式。

然而,这就是您听到的建议的来源:在第一次getInstanceinjectMembers电话之后您可能不应该使用Injector。这是因为Guice中的目标是创建独立的组件来简单地表达它们的依赖关系,以便您可以轻松地替换这些依赖关系。这是依赖注入的主要好处。虽然您可以选择将Injector存储在静态的某个地方,或者将Injector本身注入依赖项中,但这会阻止您准确了解您的类的依赖关系:使用Injector注入,您可以注入任何内容在世界上。

示范(请原谅我的Kotlin缺乏经验):

class BadIdea @Inject constructor(injector: Injector) {
  fun doSomething() {
    // Bad idea: you don't express this dependency anywhere, and this code
    // is nearly unusable without a real Guice Injector
    val appName = injector.getInstance(
      Key.get(String::class.java, Names.named(PropertiesModule.APP_NAME))
    )
  }
}

class WorseIdea {
  fun doSomething() {
    // Worse idea: same as the above, but now you rely on a static!
    val appName = StaticInjectorHolder.getInjector().getInstance(
      Key.get(String::class.java, Names.named(PropertiesModule.APP_NAME))
    )
  }
}

class GoodIdea @Inject constructor(@Named(PropertiesModule.APP_NAME) appName: String) {
  fun doSomething() {
    // Good idea: you express your dep, and anyone can call the constructor
    // manually, including you in your unit tests.
  }
}