GUICE - 在运行时决定对象图

时间:2013-03-05 02:29:47

标签: java dependency-injection guice

我在评论Guice。假设我有以下设置:

public interface IsEmailer {...}
public interface IsSpellChecker {...}
public class Emailer implements IsEmailer { 
       @Inject
       public class Emailer(final IsSpellChecker spellChecker)....
}
public class FrenchSpellChecker implements IsSpellChecker {....}
public class EnglishSpellChecker implements IsSpellChecker {....}
@BindingAnnotation public @interface English {}
@BindingAnnotation public @interface French {} 

然后在我的模块中,我将接口绑定到各自的实现,并使用相应的绑定注释注释拼写检查器。

现在,假设基于运行时变量,我需要构建一个使用英语或法语拼写检查器的电子邮件。

我想在我的模块中使用命名提供程序:

@Provides
@English
IsEmailer provideEnglishEmailer() {
    return new Emailer(new EnglishSpellChecker());
}

@Provides
@French
IsEmailer provideFrenchEmailer() {
    return new Emailer(new FrenchSpellChecker());
}

这样的工作原理如下:

IsEmailer emailer = myModule.getInstance(Key.get(IsEmailer.class,
                French.class));

这是最干净的方法吗?毕竟,我被迫手工构建对象(在提供者中)。

由于

2 个答案:

答案 0 :(得分:2)

首先注意一些事项:

  • 通常,您希望尽可能避免使用getInstance,但“root”元素除外(例如YourApplication)。在Guice提供的任何内容中,您最好的选择是请求注入Provider<IsEmailer>,或者@English Provider<IsEmailer>@French Provider<IsEmailer>。在get上调用Provider之前,Guice实际上不会创建元素,因此创建Provider的开销非常小。

  • 您无需绑定到提供程序即可获取提供程序。 Guice会自动和透明地解决XProvider<X>@Provides XXProvider<X>注入的任何约束。

  • Provider实现可以采用注入参数,@Provides方法也可以。

  • 如果您想将很多内容绑定到@English@French,您还可以调查private modules,因为这听起来像是"robot legs"问题我

最简单的方法就是使用第一颗子弹并注入每个子弹Provider,特别是如果你只这样做一次。

如果可以通过Guice访问运行时变量,也可以在模块中绑定它。将其与上面的@Provides注释一起放入您的模块中。 (如上所述,您可能需要重写它们以分别接受EnglishSpellCheckerFrenchSpellChecker作为参数,以使拼写检查器能够注入自己的依赖项。)

@Provides IsEmailer provideEmailer(Settings settings,
    @English Provider<IsEmailer> englishEmailer,
    @French Provider<IsEmailer> frenchEmailer) {
  if (settings.isEnglish()) {
    return englishEmailer.get();
  } else {
    return frenchEmailer.get();
  }
}

答案 1 :(得分:1)

您可以使用MapBinder。这将允许您注入Map<Language, IsSpellChecker>,然后在运行时检索相应的拼写检查程序。