@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);
bind(Bar.class).toInstance(new 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クラスに
コンポーネントをインジェクトしたりします。
Foo foo = injector.getInstance(Foo.class);
Bar bar = injector.getInstance(Bar.class);
Client client = injector.getInstance(Client.class);
Foo foo = client.getFoo();
Bar bar = client.getBar();
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ができあがりました。