简体   繁体   English

Guice:可以注入模块吗?

[英]Guice: is it possible to inject modules?

I have a Module that requires some Depedency . 我有一个需要一些Depedency的模块。 Is there a way Modules themselves can be injected? 是否可以注入模块本身? I realize this is a bit of a chicken and egg situation... 我意识到这有点鸡蛋和鸡蛋的情况......

Example: 例:

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_
    }
}

I suppose in this case the solution would be to turn the @Provides method into a full-fledged Provider<Something> class. 我想在这种情况下,解决方案是将@Provides方法转换为完整的Provider<Something>类。 This is clearly a simplified example; 这显然是一个简化的例子; the code I'm dealing with has many such @Provides methods so cutting them each into individual Provider<...> classes and introducing a module to configure them adds a fair amount of clutter - and I thought Guice was all about reducing boilerplate clutter? 我正在处理的代码有许多这样的@Provides方法,所以将它们分别切割成单独的Provider<...>类并引入一个模块来配置它们会增加相当多的混乱 - 我认为Guice就是减少样板杂乱?

Perhaps it's a reflection of my relative noobyness to Guice but I've come across a fair few cases where I've been tempted to do the above. 也许这反映了我对Guice的相对苛刻,但我遇到了一些我很想做上述事情的案例。 I must be missing something... 我肯定错过了什么...

@Provides methods can take dependencies as parameters just like parameters to an @Inject annotated constructor or method: @Provides方法可以将依赖项作为参数,就像@Inject注释的构造函数或方法的参数一样:

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

This is documented here , though perhaps it could be made to stand out more. 这是记录在这里 ,虽然也许它可以作出更加突出。

Using a provider or @Provides methods are great if you need a dependency to manually construct an object. 如果需要依赖项来手动构造对象,则使用提供程序或@Provides方法非常有用。 However, what if you need something to help you decide how to configure the bindings themselves? 但是,如果您需要某些东西来帮助您决定如何配置绑定本身呢? It turns out you can use Guice to create (and configure) your module. 事实证明,您可以使用Guice来创建(和配置)您的模块。

Here is a (contrived) example. 这是一个(人为的)例子。 First, the module we want to configure: 首先,我们要配置的模块:

/**
 * 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");
  }
}

The PantryModule expects a boolean value to be injected to decide whether or not it should include cheese in the pantry. PantryModule需要注入一个布尔值来决定它是否应该在食品室中包含奶酪。

Next, we'll use Guice to configure the module: 接下来,我们将使用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);

Now that we have a configured module, we'll update our injector to use it... 现在我们已经配置了模块,我们将更新我们的注入器以使用它...

//...continued from last snippet...
injector = injector.createChildInjector(configuredConditionalModule);

And finally we'll get the set of strings that represent our pantry: 最后我们将获得代表我们食品室的字符串集:

//...continued from last snippet...
Set<String> pantry = injector.getInstance(new Key<Set<String>>() {});

for (String food : pantry) {
  System.out.println(food);
}

If you put all the pieces together in a main method and run it, you'll get the following output: 如果您将所有部分放在main方法中并运行它,您将获得以下输出:

milk
bread

If you change the binding to the "addCheese" boolean to true, you'll get: 如果将绑定更改为“addCheese”布尔值为true,您将获得:

milk
cheese
bread

This technique is cool, but probably only useful when you have control over the Injector instance and only when the module requires complex dependencies. 这种技术很酷,但可能仅在您控制Injector实例并且仅在模块需要复杂依赖项时才有用。 Nonethless, I found a real need for this on a real project at work. 毫无道理,我发现在一个真正的工作项目中真正需要这个。 If I did, then someone else might too. 如果我这样做,那么其他人也可能。

The question is already well answered, but I just wanted to add a variation to Colin's example: 这个问题已经得到了很好的回答,但我只是想为Colin的例子添加一个变体:

class MyModule extends AbstractModule { 
  public void configure() {
    bind(Something.class).toProvider(new Provider<Something>() {
       @Inject Dependency d;
       Something get() { return d.buildSomething(); }
    }
  }
}

The @Provides method approach is clearer than what I have above for this simple case, but I've found that instantiating an actual Provider can be useful in some situations too. 对于这个简单的情况,@Provides方法方法比我上面的方法更清晰,但我发现在某些情况下实例化实际的Provider也很有用。 Something I stole from the mailing list; 我从邮件列表中偷走了一些东西; wouldn't have occurred to me on my own ;) 不会发生在我自己身上;)

What is the problem with initializing the module just by calling new MyModule(d) or by creating a Provider <Something > that has an injected Injector ? 仅通过调用new MyModule(d)或通过创建具有注入的InjectorProvider <Something >来初始化模块有什么问题? Those would appear to be the standard ways of handling this sort of problem. 那些似乎是处理这类问题的标准方法。 As has been mentioned, you can also use @Provides methods with arguments. 如前所述,您还可以将@Provides方法与参数一起使用。

If the dependency is optional then you can create the module and then call a setter to initialize the value if needed (eg, com.google.inject.persist.jpa.JpaPersistModule does this with properties, while using new JpaPersistModule(String) to load the correct configuration). 如果依赖项是可选的,则可以创建模块,然后调用setter以在需要时初始化值(例如, com.google.inject.persist.jpa.JpaPersistModule使用属性执行此操作,同时使用new JpaPersistModule(String)加载正确的配置)。

Otherwise I suppose it might be possible to do so (and then call createChildInjector(Modules... modules) ), but I'd almost always prefer one of the other approaches to that one. 否则我想可能会这样做(然后调用createChildInjector(Modules... modules) ),但我几乎总是喜欢其中一种方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM