Revision 1 as of 2011-11-27 04:50:20

Clear message
Locked History Actions

GIN/GinTutorial

GinTutorial

イントロダクション

通常のGuiceでは以下のように記述するかもしれない。

// GWTコードとしては動作しない!
MyWidgetMainPanel mainPanel = injector.getInstance(MyWidgetMainPanel.class);

が、コメントが示すようにGWTでは動かない。

  • 結果のjavascriptには実際のクラスが存在しない(?)
  • Guiceはリフレクションを多用しており、その大部分はGWTではエミュレートできない。

しかし幸運にも、同じ目的を達成するための異なるイディオムがGWTには存在する。

Ginを使う

Step 1:GINモジュールをinheritする

<module>
  ...
  <inherits name="com.google.gwt.inject.Inject"/>
  ...
</module>

Step 2:Ginjectorを定義する

所望のタイプを返すメソッドを持つインターフェースを宣言する。

public interface MyWidgetGinjector extends Ginjector {
  MyWidgetMainPanel getMainPanel();
}

経験のあるGWTユーザは、これがGWTのイメージバンドルやメッセージと同様のやり方であることに気がつくと思う。 単に、作成して欲しいと思う各オブジェクトのタイプを返すメソッドを宣言することで、その実装はコンパイル時に自動生成されるのだ。

ただし、君のトップレベルの初期化コードから直接アクセスされるものについてインジェクタメソッドを作成すればよいことに注意すること。 例えば、RootPanelにインストールされるUIクラス等だ。 下位レベルのクラスについてインジェクタメソッドは必要無い。それらは自動的にインジェクトされるからだ。

例えば、クラスAがクラスBを使い、さらにBはクラスCを使うとする。この場合、インジェクタメソッドはAに対してだけでよい。 他のBやCのクラスは自動的にAにインジェクトされる。

言い換えれば、インジェクタメソッドはGuice世界と非Guice世界の間の橋渡しをするものだ。

Step 3:バインディングを宣言する

次のステップは、Guiceモジュールを使って、様々なクラスとプロバイダをバインドすることだ。 モジュールクラスはおおよそ通常のGuiceのものと同じである(ただし、AbstractModuleの代わりにGinModuleあるいはAbsractGinModuleを使うけれども)。以下にモジュール例を示す。

public class MyWidgetClientModule extends AbstractGinModule {
  protected void configure() {
    bind(MyWidgetMainPanel.class).in(Singleton.class);
    bind(MyRemoteService.class).toProvider(MyRemoteServiceProvider.class);
  }
}

もし、GINがクラスのバインディングを見つけられないときは、そのクラスについてGWT.create()が呼び出されることに注意すること。 これは、image bundleやtranslated messageについて動作することを示す。

GINではGuiceの多くのタイプをエミュレートしているが、モジュールついてはエミュレートしない。なぜなら、

  • GINは我々自身のペースで開発を進めたいから
  • おそらくtoInstance(...)及びそれに類するものは決してサポートしない、なぜなら我々のモジュールはコンパイル時に動作するものであって、実行時に動作するものではないからだ。

通常のGuiceとの互換性のために、GinModuleAdapterクラスを提供しているが、それは、GinModuleをModuleとして使えるようにsるものだ。

Step 4:モジュールをインジェクタに関連付ける

GinModuleのアノテーションをGinjectorに付加する。つまりこれは、モジュールがアプリを構成するように示すためだ。

@GinModules(MyWidgetClientModule.class)
public interface MyWidgetGinjector extends Ginjector {
  MyWidgetMainPanel getMainPanel();
}

現在のプロジェクトレイアウトはこうなっているよ。

MyWidgetProject/
    client/
        MyWidget.java
        MyWidgetGinjector.java
        MyWidgetMainPanel.java
        MyWidgetClientModule.java
    public/
    server/

Step 5:Ginjectorを作成する

To create the injector instance, use the standard GWT.create() call. This can be done during static initialization:

public class MyWidget implements EntryPoint {

}

コンパイル

Your project should now be ready to be compiled. Since Gin uses Guice at compile-time and Guice operates on compiled java classes, you'll have to provide the compiled java classes to the gwt compiler. Example from an ant build file:

<target name="javac" description="Compiles Java types needed during GWT compilation">
  <mkdir dir="war/WEB-INF/classes"/>
  <javac srcdir="src" destdir="war/WEB-INF/classes">
    <classpath refid="project.libs"/>
  </javac>
</target>

<target name="gwtc" depends="javac" description="GWT Web Compilation">
  <java classname="com.google.gwt.dev.Compiler">
    <classpath>
      <pathelement location="src"/>

       <!-- Note the reference to the compiled java classes -->
      <pathelement location="war/WEB-INF/classes"/>
      <path refid="project.libs"/>
    </classpath>
     <arg value="com.example.myapp.MyApp"/>
  </java>
</target>

Full Example

You can find an example for Gin project layout and compilation in our samples collection. Gin "Magic"

Gin tries to make injection painless and remove as much boilerplate from your code as possible. To do that the generated code includes some magic behind the scenes which is explained here. Deferred Binding

One way Gin optimizes code is by automating GWT deferred binding. So if you inject an interface or class1 bound through deferred binding (but not through a Guice/Gin binding), Gin will internally call GWT.create on it and inject the result. One example are GWT messages and constants (used for i18n purposes):

public interface MyConstants extends Constants {

  • String myWords();

}

public class MyWidget {

  • @Inject

    public MyWidget(MyConstants myconstants) {

    • // The injected constants object will be fully initialized - // GWT.create has been called on it and no further work is necessary.
    }

}

Note: Gin will not bind the instances created through GWT.create in singleton scope. That should not cause unnecessary overhead though, since deferred binding generators usually implement singleton patterns in their generated code.

1: Gin creates all instances through GWT.create (instead of new) for which it is legal: interfaces and classes with default constructors (whether they have an @Inject annotation or not). Remote Services

The RemoteService magic Gin performs is an extension of the deferred binding optimization explained above. Every time Gin is asked to inject an asynchronous remote service, it will inject an instance retrieved through calling GWT.create on its regular remote service:

public interface MyRemoteService extends RemoteService { ... }

public interface MyRemoteServiceAsync { ... }

public class MyWidget {

}