java Guice:是否可以注入模块?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5504555/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 11:26:55  来源:igfitidea点击:

Guice: is it possible to inject modules?

javadependency-injectionguice

提问by sxc731

I have a Module that requires some Depedency. Is there a way Modules themselves can be injected? I realize this is a bit of a chicken and egg situation...

我有一个需要一些Depedency. 有没有办法可以注入模块本身?我意识到这有点鸡和蛋的情况......

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 @Providesmethod into a full-fledged Provider<Something>class. This is clearly a simplified example; the code I'm dealing with has many such @Providesmethods 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<Something>类。这显然是一个简化的例子;我正在处理的代码有很多这样的@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. I must be missing something...

也许这反映了我对 Guice 的相对笨拙,但我遇到过一些我很想执行上述操作的案例。我肯定错过了什么...

回答by ColinD

@Providesmethods can take dependencies as parameters just like parameters to an @Injectannotated 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.

此处记录这一点,但也许可以使其更加突出。

回答by Jim Hurne

Using a provider or @Provides methods are great if you need a dependency to manually construct an object. 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.

如果您需要依赖项来手动构造对象,则使用提供程序或 @Provides 方法非常有用。但是,如果您需要一些东西来帮助您决定如何配置绑定本身呢?事实证明,您可以使用 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. Nonethless, I found a real need for this on a real project at work. If I did, then someone else might too.

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

回答by CJC

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. Something I stole from the mailing list; wouldn't have occurred to me on my own ;)

@Provides 方法方法比我上面针对这个简单案例的方法更清晰,但我发现实例化实际的 Provider 在某些情况下也很有用。我从邮件列表中偷的东西;我自己不会想到的;)

回答by David H. Clements

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? Those would appear to be the standard ways of handling this sort of problem. As has been mentioned, you can also use @Providesmethods with arguments.

仅通过调用new MyModule(d)或创建具有注入的 来初始化模块有什么问题?这些似乎是处理此类问题的标准方法。如前所述,您还可以使用带参数的方法。Provider<Something>Injector@Provides

If the dependency is optional then you can create the module and then call a setter to initialize the value if needed (e.g., com.google.inject.persist.jpa.JpaPersistModuledoes 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)),但我几乎总是更喜欢其他方法之一。