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

guice injection: difference among getBinding/getExistingBinding/getProvider and getInstance

我有一个 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 应该避免。但是我不确定它们之间有什么区别。

如果您刚刚创建了 Injector,请使用 getInstance。否则,请尝试从更窄的注入中获取对象。


getBindinggetExistingBinding 实际上都是 reflective purposes:

[Binding is] a mapping from a key (type and optional annotation) to the strategy for getting instances of the type. This interface is part of the introspection API and is intended primarily for use by tools.

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

中描述

Unlike getBinding(Key), this does not attempt to create just-in-time bindings for keys that aren't bound. This method is part of the Guice SPI and is intended for use by tools and extensions.

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

getInstance is the most important method on Injector, and I expect all Guice applications to call it at least once. If you've just created your Injector, you'll presumably want to either get an instance from it, or inject the @Inject fields of an object. The former happens with getInstance, and the latter with injectMembers。在我看来,这是从图表中获取第一个对象的最佳方式。

但是,这就是您听到的建议的来源:在第一次 getInstanceinjectMembers 调用后,您可能不应该使用注射器。 这是因为 Guice 的目标是使独立的组件能够精确地表达它们的依赖关系,这样您就可以轻松地替换这些依赖关系。这是依赖注入的主要好处。尽管您可以选择将 Injector 静态存储在某处,或者将 Injector 本身注入到您的依赖项中,但这会阻止您确切知道 class 的依赖项是什么:使用 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.
  }
}