在Guice模块中如何访问Guice注入器?

41

我正在扩展Guice的AbstractModule,并且在扩展类内部需要访问Guice的注射器。如果可以,如何实现?

2个回答

64
这是一个不寻常的请求。模块更像是配置文件而不是逻辑文件:模块会被读取以创建注入器,一旦注入器创建完成,模块的任务便完成了。对于简单的模块来说,在模块准备被丢弃之前,注入器实际上并不存在。
无论如何,与其请求一个注入器来获取类 X,你通常应该请求一个Provider<X>。Guice将注入XProvider<X>到任何X绑定、Provider<X>绑定或@Provides X绑定中,所以你几乎总是可以这样做。尽管如此,注入注入器将允许你通过反射获取实例,或者检查注入器的绑定(等等)。
以下是一些需要在模块内访问注入器的有效原因/设计:

@Provides方法中:

模块可以包含使用@Provides注释的小型提供程序。请记住,Injector是可注入的:如果您需要在其中一个方法中使用Injector,则可以将其作为参数接受:

public class OneModule extends AbstractModule {
  @Override public void configure() { /* ... */ }

  @Provides YourDependency getYourDependency(Injector injector) {
    return injector.getInstance(Class.forName(yourDependencyName));
  }

  @Provides Something getSomething(SomethingImpl something) {
    return initialize(something); // preferred: only ask for what you need
  }

  @Provides SomethingElse getSomethingElse(Provider<Thing> thingProvider) {
    return new SomethingElse(thingProvider); // asking for a provider works too
  }
}

在配置中获取提供程序:

AbstractModules正是为了这个目的而公开了getProvider(),但如果您在注入器准备好提供它之前(比如在配置时间)调用get(),则会出现错误:

public class TwoModule extends AbstractModule {
  @Override public void configure() {
    bind(Thingy.class).toInstance(
        new MyThingy(8675309, getProvider(Another.class)));
  }
}

你可能可以调用getProvider(Injector.class),但我不知道这是否有效,也不知道你为什么想要这样做。

在configure()中获取实例:

这是一个坏主意;直到所有的configure方法运行完毕,Guice才准备好提供实例。你能得到的最接近的是使用其他模块创建一个子注入器,并将其传递到此模块中,但即使如此,这也很少需要。

public class MainClass {
  public static void main(String[] args) {
    Injector firstStage =
        Guice.createInjector(new OtherModule1(), new OtherModule2());
    // An alternative design would @Inject-annotate fields in ThreeModule
    // and get it from firstStage, but that's nonstandard and may be confusing.
    Injector secondStage =
        firstStage.createChildInjector(new ThreeModule(firstStage));
  }
}

public class ThreeModule extends AbstractModule {
  private final Injector otherInjector;

  public ThreeModule(Injector otherInjector) { 
    this.otherInjector = otherInjector;
  }

  @Override public void configure() {
    bindStuffBasedOn(otherInjector);
  }
}

2
我同意,这是一个不寻常的请求,可能暗示了设计上的问题。 - ilikeorangutans
在AbstractModule#configure()中,getProvider()方法是正确的方法。 - leozilla
11
@ilikeorangutans 很喜欢这样的评论;它们总是让我感到自己的设计有点“臭”,但却没有任何改进的想法:D - Line
1
虽然不寻常,但我认为这并不总是一个糟糕的设计,哈哈。我们有一个场景,需要从其类动态获取实例。随着您接近动态场景(反射等),您开始需要高级的“元”功能。 - Ferran Maylinch
@FerranMaylinch 对于我自己来说:我的反对与动态访问或使用注射器无关。当然,尽可能清楚地了解您的依赖关系,并在可能的情况下最小化使用特定于Guice的组件是有意义的,但通过Injector进行反射访问是Guice的一个很好的用例。我的建议更多地是清楚地区分“配置时间”和“运行时间”:您应该在类中使用Injector而不是模块(可能除了@Provides方法),因为直到运行configure之后,Injector才会准备就绪。 - Jeff Bowman

2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接