【问题标题】:How to bind essential GWT components with GIN?如何将必要的 GWT 组件与 GIN 绑定?
【发布时间】:2012-11-16 00:11:12
【问题描述】:

我已关注 GWT-GIN 教程页面上的 basic setup instructions。我在第 3 步(Declare bindings)并试图弄清楚如何使用 GIN 的 Binder API。

public class MyModule extends AbstractGinModule {
    @Override
    public void configure() {
        // 1. Declare an instance of EventBus and make sure every
        // injection request pulls back the same instance.
        EventBus eventBus = new EventBus();
        bind(EventBus.class).to??? // no toInstance() method!

        // 2. Declare two instances of Fizz using different constructors,
        // and allow the injection of either of them.
        Fizz f1 = new Fizz(true, "oh yeah", null);
        Fizz f2 = new Fizz();
        bind(Fizz.class).to??? // no toInstance() AND don't know how to choose f1 or f2!

        // 3. Declare a List of Buzz objects and allow them to be
        // injectable.
        List<Buzz> buzzes = new ArrayList<Buzz>();
        configureBuzzes(buzzes); // adds configured Buzz objects to the list
        bind(???).to(buzzes); // no toInstance() methods AND how to bind List<?>.class?!

        // 4. Conditionally bind SomePlace to Place *only* when we want the default Place
        // that 'historyHandler.handleCurrentHistory()' will go to when called onModuleLoad.
        bind(Place.class).to(SomePlace.class); // forces me to only have SomePlace instances!
    }
}

上面的四个用例是我正在努力解决的问题。分别:

  1. 如何在每次客户端请求时重用EventBus 的同一个实例?
  2. 从上面的 #1 构建,如何在不同的场景下有 2+ 个不同的实例可以注入?
  3. 如何注入List 的任何东西?
  4. 可能和上面的#2一样,但是如何将2+ Place子类绑定到Place.class

提前致谢!

【问题讨论】:

    标签: java gwt dependency-injection gwt-gin


    【解决方案1】:

    很好的问题有助于阐明 Guice 本身的工作原理,以及 Guice 和 Gin 之间的区别。 Gin 与 Guice 不太一样——configure() 方法在生成 JavaScript 时运行,因此编译器只烘焙正确的类型集——否则你的应用程序可能包含整个 JRE!这对 Gin 来说有点作弊,一旦你理解了这一点,GWT DI 就更有意义了。

    基本思想是configure() 方法只应该处理连线——而不是创建实例。这提供了 1) 的答案,以及 2) 的部分答案。实际上编写将在应用程序运行时使用的代码(Provider 对象、@Provides 方法,当然还有任何用@Inject 注释的东西)需要反过来——它只会被编译成 JS。这意味着虽然您可以在 3) 中定义像 configureBuzzes 这样的方法,但您只需要小心从 configure() 方法内部调用这些方法 - 并且永远不要从常规应用程序调用 configure()代码。

    2)、3) 和 4) 的答案主要与 Guice 本身的一般工作方式有关。我提供的解决方案 1) 也适用于普通的 Guice,而且我会一直建议这种方法 - 我发现如果你不混合布线和实际的对象构建,它往往会产生更易读的代码.

    1. 不要在configure() 方法中创建实例,只需进行绑定即可。您可以将绑定设置为例如

      bind(EventBus.class).to(SimpleEventBus.class).in(Singleton.class);
      

      创建实例,并将其限定为单例 - 默认情况下将使用默认构造函数。

      • 如果您想使用非默认构造函数,有多种选择。您可以使用@Inject 注释特定的构造函数,并为每个值提供一些注释(稍后会详细介绍),或者您可以构建一个提供程序或@Provides 方法来创建实例。同样,您可能希望@Singleton 有意义,但这取决于您的用例(这将是您 GinModule 中的另一种方法):

        @Provides
        //@Singleton //optional, depends on your use case
        public Fizz provideFirstFizz() {
            return new Fizz(true, "oh yeah", null);
        }
        
      • 接下来,你如何提供两种不同的同一种东西?你如何在 Guice 中做到这一点?您如何期望注入Fizz 的代码得到正确的代码?事实证明,这些可能都有相同的答案——您需要找到一种方法来指示您想要哪个实例。它们都是相同的类型,所以这还不够,但我们可以提供其他提示,例如注入字段上的注释。假设我们需要f1f2 的代码看起来像这样

        @Inject
        @Red// Make up your own annotation, or use some existing ones
        private Fizz f1;
        
        @Inject @Blue private f2;
        

        现在我们有一种方法来区分它们,我们需要使用相同的注释来绑定它们。由于我们仍然假设Fizz 构造函数上没有@Inject,我们不能只调用bind(),所以我们只需将@Blue 添加到provides 方法中:

        @Provides
        @Blue
        //@Singleton //optional, depends on your use case
        public Fizz provideFirstFizz() {
            return new Fizz(true, "oh yeah", null);
        }
        

        我们可以将其解读为“此方法ProvidesBlueFizz 实例”。对于@Red,由于我们有默认的ctor,我们可以使用bind()

        bind(Fizz.class).annotatedWith(Red.class);//... no need to specify type
                                                  //in this case, but might want 
                                                  //singleton
        

        请参阅https://code.google.com/p/google-guice/wiki/BindingAnnotations 了解更多详情。

    2. 同样,我们可以为此使用@Provides,或者创建并绑定Provider&lt;T&gt; 类型。由于我们已经完成了几个提供者方法,让我们试试Provider&lt;List&lt;Buzz&gt;&gt;

      public class BuzzListProvider implements Provider<List<Buzz>> {
          public List<Buzz> get() {
              List<Buzz> buzzes = new ArrayList<Buzz>();
              // Configure them... This might call on a @Inject defined
              // within this BuzzListProvider, on the ctor or a field, or
              // just some code in this method.
              return buzzes;
          }
      }
      

      然后,将 Provider 绑定到该列表:

      // cant say List<Buzz>.class, use TypeLiteral instead
      bind(new TypeLiteral<List<Buzz>>(){})
          .toProvider(BuzzListProvider.class);
      //  .in(Singleton.class); if the list needs to be only created once
      
    3. 你的总结完全正确 - 这与 2 完全相同。我通常会做一个 @DefaultPlace 注释(或者只是简单的 @Default 以便我可以重用它)来处理这个那种情况。

    【讨论】:

    • 感谢@Colin Alworth (+1) - 我希望我能更多地支持它!一些跟进:(1)我喜欢@Provides 方法注释,但是它们被打包在什么类中,它们如何绑定到活页夹中,以及当我想要时如何在“客户端”调用它们使用它们进行注射? (2) GIN 是否支持@Named 注释,所以我不必创建100 万个注释?再次感谢!
    • @Provides 方法将被放入任何模块(gin 或 guice)本身。从技术上讲,我相信这些是一种快捷方式,而不是创建 Provider 类型。我发现它们在测试中非常有帮助 - 你可以在子类化模块时覆盖它们。
    • 是的,Gin 支持 @Named 注释以及带有 named(String) 方法的 Names 类。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-10
    • 2012-12-25
    • 2011-06-12
    • 2012-04-21
    • 1970-01-01
    • 2023-04-08
    相关资源
    最近更新 更多