我有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
应该避免。但我不确定他们之间有什么区别。
答案 0 :(得分:1)
如果您刚刚创建了注射器,请使用getInstance
。否则,尝试从较窄的注射中获取物体。
getBinding
和getExistingBinding
实际上都适用于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
。在我看来,这是将第一个对象从图表中删除的最佳方式。
然而,这就是您听到的建议的来源:在第一次getInstance
或injectMembers
电话之后您可能不应该使用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.
}
}