はじめてのGuice

@Java, Guice
Guiceは、Googleの提供するDIコンテナです。
サイトはこちら
サイトから「guice-1.0.zip」ダウンロードして、
とりあえずその中の「guice-1.0.jar」があれば動きます。

SeasarのDiconやSpringのXMLのような設定ファイルはなく、
代わりにJavaプログラム上でコンポーネント登録を行います。
AbstractModuleクラスを継承した独自のModuleクラスを作成します。
登録の仕方をとりあえず二つ試してみました:
 A. Classクラス同士を関連付け
 B. Classクラスとインスタンスを関連付け
Injector injector = Guice.createInjector(new AbstractModule() {
    @Override
    protected void configure() {
        bind(Foo.class).to(FooImpl.class); // A
        bind(Bar.class).toInstance(new Bar());  // B
    }
});
// /==============================
// Example Classes 'Foo' and 'Bar'
// ==========/
private static interface Foo {
    void execute();
}
private static class FooImpl implements Foo {
    public void execute() {
    }
}
private static class Bar {
    private Foo foo;
    public Foo getFoo() {
        return foo;
    }
    @Inject
    public void setFoo(Foo foo) {
        this.foo = foo;
    }
}
インターフェースなしコンポーネントの登録(Barクラス)は、
「A」では不可、「B」ではOKでした。
bind(Bar.class).to(Bar.class); → × (例外発生)
bind(Bar.class).toInstance(new Bar()); → ○

Setterに「@Inject」を付与すると、コンポーネント生成時に
該当コンポーネントがインジェクトされます。
どうやら特にSetterである必要は無さそうです。

Guice.createInjector()で生成したInjectorを使って、
コンポーネントを直接取得したり、Clientクラスに
コンポーネントをインジェクトしたりします。
// Get component
Foo foo = injector.getInstance(Foo.class);
Bar bar = injector.getInstance(Bar.class);
// Inject component to client instance
Client client = injector.getInstance(Client.class);
Foo foo = client.getFoo();
Bar bar = client.getBar();
// /==============================
// Example Class 'Client'
// ==========/
private static class Client {
    private Foo foo;
    private Bar bar;
    public Foo getFoo() {
        return foo;
    }
    public Bar getBar() {
        return bar;
    }
    @Inject
    public void setFooAndBar(Foo foo, Bar bar) {
        this.foo = foo;
        this.bar = bar;
    }
}
Clientクラスは、ある意味「登録されないコンポーネント」という感じで、
他のクラスにインジェクションされることは無いけれども、getInstance()
を経由してインスタンスを生成することで、そのClientクラスが欲する
コンポーネントがインジェクトされた状態のインスタンスになります。

通常は、コンポーネントを直接取得することはなく、
injector.getInstance(Client.class)を利用して、登録されている
コンポーネントをClientクラスにインジェクトして利用するって形に
なると思います。ただ、もう一個「injector.injectMembers()」という
メソッドもあり、既にインスタンスが生成されているClientクラスに対して、
登録されているコンポーネントをインジェクトすることも可能なので、
そのとき利用する関連フレームワーク次第かなとも思います。

このGuiceを使ってちょっとした仕組みを作るとなると、
登録されているコンポーネントを直接取得することもあるかと思います。
例えば、S2TestCaseのGuice版などを作るような場合。

特徴的なのが、「登録されているコンポーネントを直接取得」する行為と
「Clientクラスにインジェクト」する行為が同じメソッドなので、「
登録されてなければ例外になって欲しい」ってのがダメだったりします。
getInstance()は、登録されていないクラスが指定された場合は、
デフォルトコンストラクタをnewして、@Injectがあればインジェクトする、
って挙動になります。@Injectがないクラスであればただnewされるだけです。

「じゃあ事前に登録されているかどうかをif文でチェックしよう」ですが、
findBindingsByType()を使ってできました。
List<?> bindings = 
    injector.findBindingsByType(TypeLiteral.get(type));
if (bindings.isEmpty()) {
    return;
}
ちょっとなんかおおげさなので、これで本当にいいのかわかりませんが、
とりあえず判定はできているようです。

とにもかくにも少ない知識で探って探って(ドキュメントほとんど読まずに)、
dbflute-guice-exampleができあがりました。