Guice:可以注入模块吗?

2022-09-01 18:41:34

我有一个模块,需要一些.有没有办法注入模块本身?我意识到这有点像先有鸡还是先有蛋的情况...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_
    }
}

我想在这种情况下,解决方案是将方法变成一个成熟的类。这显然是一个简化的例子。我正在处理的代码有很多这样的方法,所以将它们分别切割成单独的类并引入一个模块来配置它们会增加相当多的混乱 - 我认为Guice就是为了减少样板混乱?@ProvidesProvider<Something>@ProvidesProvider<...>

也许这反映了我对Guice的相对贪度,但我遇到了相当多的案例,我一直想做上述事情。我一定错过了什么...


答案 1

@Provides方法可以将依赖项作为参数,就像带注释的构造函数或方法的参数一样:@Inject

@Provides Something provideSomething(Dependency d) {
   return new Something(d); // or whatever
}

这在这里有记载,尽管也许可以让它更加突出。


答案 2

如果需要依赖项来手动构造对象,则使用提供程序或@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

这种技术很酷,但可能只有在您控制注入器实例并且仅当模块需要复杂的依赖项时才有用。无休止,我发现在工作中的一个真实项目中确实需要它。如果我这样做了,那么其他人也可能这样做。


推荐