簡體   English   中英

Guice:可以注入模塊嗎?

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

我有一個需要一些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的相對苛刻,但我遇到了一些我很想做上述事情的案例。 我肯定錯過了什么...

@Provides方法可以將依賴項作為參數,就像@Inject注釋的構造函數或方法的參數一樣:

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

這是記錄在這里 ,雖然也許它可以作出更加突出。

如果需要依賴項來手動構造對象,則使用提供程序或@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實例並且僅在模塊需要復雜依賴項時才有用。 毫無道理,我發現在一個真正的工作項目中真正需要這個。 如果我這樣做,那么其他人也可能。

這個問題已經得到了很好的回答,但我只是想為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也很有用。 我從郵件列表中偷走了一些東西; 不會發生在我自己身上;)

僅通過調用new MyModule(d)或通過創建具有注入的InjectorProvider <Something >來初始化模塊有什么問題? 那些似乎是處理這類問題的標准方法。 如前所述,您還可以將@Provides方法與參數一起使用。

如果依賴項是可選的,則可以創建模塊,然后調用setter以在需要時初始化值(例如, com.google.inject.persist.jpa.JpaPersistModule使用屬性執行此操作,同時使用new JpaPersistModule(String)加載正確的配置)。

否則我想可能會這樣做(然后調用createChildInjector(Modules... modules) ),但我幾乎總是喜歡其中一種方法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM