我有一个需要一些Depedency
的模块。是否可以注入模块本身?我意识到这有点像鸡蛋和鸡蛋的情况......
示例:
public class MyModule implements Module {
private final Dependency d_;
@Inject public MyModule(Dependency d) {
d_ = d;
}
public void configure(Binder b) { }
@Provides Something provideSomething() {
// this requires d_
}
}
我认为在这种情况下,解决方案是将@Provides
方法转换为完整的Provider<Something>
类。这显然是一个简化的例子;我正在处理的代码有很多这样的@Provides
方法,所以将它们分别划分为单独的Provider<...>
类并引入一个模块来配置它们会增加相当多的混乱 - 我认为Guice就是关于减少样板杂乱?
也许这反映了我对Guice的相对苛刻,但我遇到了一些我很想做上述事情的案例。我一定错过了什么......
答案 0 :(得分:28)
@Provides
方法可以将依赖项作为参数,就像@Inject
带注释的构造函数或方法的参数一样:
@Provides Something provideSomething(Dependency d) {
return new Something(d); // or whatever
}
这是记录here,但也许可以使它更突出。
答案 1 :(得分:10)
如果需要依赖项来手动构造对象,则使用提供程序或@Provides方法非常有用。但是,如果您需要某些东西来帮助您决定如何配置绑定本身呢?事实证明,您可以使用Guice来创建(和配置)您的模块。
这是一个(人为的)例子。首先,我们要配置的模块:
/**
* Creates a binding for a Set<String> which represents the food in a pantry.
*/
public class PantryModule extends AbstractModule {
private final boolean addCheese;
@Inject
public ConditionalModule(@Named("addCheese") boolean addCheese) {
this.addCheese = addCheese;
}
@Override
protected void configure() {
Multibinder<String> pantryBinder = Multibinder
.newSetBinder(binder(), String.class);
pantryBinder.addBinding().toInstance("milk");
if (addCheese) {
pantryBinder.addBinding().toInstance("cheese");
}
pantryBinder.addBinding().toInstance("bread");
}
}
PantryModule需要注入一个布尔值来决定它是否应该在食品室中包含奶酪。
接下来,我们将使用Guice配置模块:
// Here we use an anonymous class as the "configuring" module. In real life, you would
// probably use a standalone module.
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
// No cheese please!
bindConstant().annotatedWith(Names.named("addCheese")).to(false);
bind(PantryModule.class);
}
});
Module configuredConditionalModule = injector.getInstance(PantryModule.class);
现在我们已经配置了模块,我们将更新我们的注入器以使用它......
//...continued from last snippet...
injector = injector.createChildInjector(configuredConditionalModule);
最后我们将获得代表我们食品室的字符串集:
//...continued from last snippet...
Set<String> pantry = injector.getInstance(new Key<Set<String>>() {});
for (String food : pantry) {
System.out.println(food);
}
如果您将所有部分放在一个main方法中并运行它,您将获得以下输出:
milk
bread
如果将“addCheese”布尔值的绑定更改为true,则会得到:
milk
cheese
bread
这种技术很酷,但可能仅在您控制Injector实例并且仅在模块需要复杂依赖项时才有用。毫无道理,我发现在一个真正的工作项目中真正需要这个。如果我这样做,那么其他人也可能。
答案 2 :(得分:5)
这个问题已经得到了很好的回答,但我只是想为Colin的例子添加一个变体:
class MyModule extends AbstractModule {
public void configure() {
bind(Something.class).toProvider(new Provider<Something>() {
@Inject Dependency d;
Something get() { return d.buildSomething(); }
}
}
}
对于这个简单的情况,@Provides方法方法比我上面的方法更清晰,但我发现在某些情况下实例化实际的Provider也很有用。我从邮件列表中偷走了一些东西;不会发生在我自己身上;)
答案 3 :(得分:0)
仅通过调用new MyModule(d)
或创建注入了Provider
的{{1}}来初始化模块有什么问题?那些似乎是处理这类问题的标准方法。如前所述,您还可以将<Something
><Something
方法与参数一起使用。
如果依赖项是可选的,那么您可以创建模块,然后调用setter以在需要时初始化值(例如,Injector
使用属性执行此操作,同时使用@Provides
加载正确的配置)。
否则我认为有可能这样做(然后调用 com.google.inject.persist.jpa.JpaPersistModule
),但我几乎总是更喜欢其中一种方法。