Guiceについて
概要
Googleが使っているDIに特化したJavaフレームワーク(というか機能)
フレームワークの定義が”To Do Here” ここでこんなことをしろ に限定されている物の場合、当てはまりません。
サンプルを一つ作ってみます。
今回作成するサンプルは、 1.Guiceで「インターフェース HelloService」とそのインターフェースを実装した「クラス HelloServiceImpl」を関連づけ
2.Guice経由で、 HelloService インターフェースの何らかの実装を使うクラス MyObject のインスタンスを作成
MyObjectクラス内のHelloService インターフェースに、HelloServiceImplクラスのインスタンスが組み込まれていることを確認します
? 何がおこるん?
通常なら、HelloServiceImplクラスをインスタンス化させて、
MyObjectのコンストラクタに入れてMyObjectをインスタンス化、でいいんですが、
ここでは、HelloServiceImpl、HelloService、MyObjectについての一切のnew演算子を使いません。
Guiceがそのへんの肩代わりをやってくれます。
The new "new" (あたらしいNew) が見れます。
バージョン 今回は2.0を使用
使用するための準備
1.ダウンロード
下記からguice-2.0.zipとかを持ってくる。
zip中には、下記が入っているはず。
COPYING.txt
aopalliance.jar
guice-2.0.jar
guice-assistedinject-2.0.jar
guice-jmx-2.0.jar
guice-jndi-2.0.jar
guice-multibindings-2.0.jar
guice-servlet-2.0.jar
guice-spring-2.0.jar
guice-struts2-plugin-2.0.jar
guice-throwingproviders-2.0.jar
javadoc(フォルダ)
2.Guice関連のJarをビルドパスに入れる
今回のサンプルでは下記だけを使う。
guice-2.0.jar
aopalliance.jar
環境 今回はクライアントサイド、ただのJavaApplicationとします。
サーバサイドでは動かない、完全にローカルのJavaで動くもの。
Java5以上。
サンプルのコーディング
HelloServiceという、とりあえずHelloWorld的な事をするものをサンプルで作ってみる。
Main.java
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Scopes;
/**
* エントリーポイントのクラス
* @author sassembla
*/
public class Main {
/**
* main関数
* @param args
*/
public static void main(String[] args) {
//インスタントにInjection処理を記述 AbstractModuleを拡張した別のクラスに書いてもいい。ここでは記述量の問題で逃がしてください。
Injector injector = Guice.createInjector(new AbstractModule(){
@Override
protected void configure() {
bind(HelloService.class).to(HelloServiceImpl.class).in(Scopes.NO_SCOPE);//スコープ指定で組み込むことができる。これが鬼機能。
}
});
MyObject object = injector.getInstance(MyObject.class);//実行したいオブジェクトにヒョウイさせる
object.execute();//処理を実行
}
}
HelloService.java
/**
* サービス定義のインターフェース
* 定義インターフェースでは、外部向けの内容をインターフェースとしてまとめておく事で、
* 見せたくない実装を隠せる。
* @author sassembla
*
*/
public interface HelloService {
/**
* 名前のみ
*/
public void sayHello();
}
HelloServiceImpl.java
/**
* 実装
*
* 抽象物ではなく、実際の処理が行われる部分
* @author sassembla
*
*/
public class HelloServiceImpl implements HelloService {
int count = 0;
/**
* とりあえずなにかしら実装
*/
public void sayHello() {
System.out.println("Hello, guice!_"+count);
count++;
}
}
MyObject.java
import com.google.inject.Inject;
/**
* 使用したいクラスをヒョウイさせる対象
*
* @author sassembla
*
*/
public class MyObject {
private HelloService helloService = null;
@Inject
public void setHelloService(HelloService helloService) {
this.helloService = helloService;
}
public void execute() {
helloService.sayHello();
}
}
実行結果
プリント文出力:Hello, Guice!_0
動いてる。ナイス。
実際に動いているという事は、
MyObjectクラス内のHelloServiceクラスに、HelloServiceImplクラスのインスタンスが組み込まれている
ということっすね。
なんで? MyObjectの setHelloService メソッドはどこからも呼ばれて無いじゃん。
その辺を、
MyObjectクラスのsetHelloServiceメソッドについてる@Inject アノテーションによってGuiceが「察してくれた」形になってます。
interface以外関連性無い。
白矢印はInterface、黒矢印は呼び出し(=メモリーに展開された、ということ)
InterfaceのHelloServiceと、それを実装(Implements)しているhelloServiceImplとの間の関連はありますが、HelloServiceImplの
クラス化は見た目行われていません。
利点 GuiceがhelloServiceImplのインスタンス化や保持を「しれっと」やってくれています。
☆Guice(Injector)がインスタンスの作成、保持をしている
new演算子は、AbstractModule記述以外に全く使用されていません。Guice/Injectorがすべてのインスタンスを保持してくれています。
モジュール側(今回はMainがあるクラス)から見ると、
HelloService と HelloServiceImplが結びつけられています。 ☆HelloService さえ実装されてれば、 HelloServiceImpl 以外のクラスでもMyObjectから使える!
インターフェースを用意して、バインド対象を入れ替えれば、使う側からの区別が消えます。
☆コード上に依存関係を記述できる!
プログラム外の設定コードとさよならできます。 主にクォーテーション、目コピー&手ペーストとさよならです。
スコープについて
bindの際にスコープという要素の設定が出来ます。定義されるのは、bindしたクラスのライフサイクルです。
Main.javaの中で、下記のような行があります。
bind(HelloService.class).to(HelloServiceImpl.class).in(Scopes.NO_SCOPE);//スコープ指定で組み込むことができる。これが鬼機能。
スコープは、Singleton やら Transactionalやら、いろいろ魅力的なものがそろってます。
NO_SCOPEだと、特に何のフォローもしません。デフォルトはNO_SCOPEです。
SINGLETONだと、Injectorの寿命が或る限り、あたかもbindしたクラスがシングルトンであるかのように、
Injector から取得できるクラスが固定されます。
試しに、スコープをSingletonに変え、Main.java に下記のコードを加えてみると、結果が面白い事になります。
/~/
bind(HelloService.class).to(HelloServiceImpl.class).in(Scopes.SINGLETON);//スコープをシングルトンに変更
/~/
MyObject object = injector.getInstance(MyObject.class);//実行したいオブジェクトにヒョウイさせる
object.execute();//処理を実行
//追記
MyObject object2 = injector.getInstance(MyObject.class);//実行したいオブジェクトにヒョウイさせる
object2.execute();//こんどはobject2でexecuteしてみよう
結果:
プリント文出力:
Hello, Guice!_0 Hello, Guice!_1
sayHelloが一つのHelloServiceImplから行われた事になっています。
GuiceがSingletonとして
HelloServiceImplの実装を持ってくれている。
SINGLETONに変えた事により明らかになるのは、
NO_SCOPE/デフォルトだと、通常のインスタンス作成と変わらない内容だ、という事です。(試しにスコープを戻してみるといい)
これ、Guiceを通したnewの定義。
スコープによって、bindしたインスタンスの寿命を設定できます。 すごく簡単に乱暴に無知に書くと、インスタントに作って使い捨てたければNO_SCOPEで、何度も使いたければSINGLETONで。
これ以外にも大量に、スコープに関する機能があります。Sessionとか、Transactionとか。
それぞれ、どんな寿命をbindしたオブジェクトに与えるのか、なんとなく想像がつくかと思います。
特にサーバ周りが強烈便利すぎる。
他人が作った物を使う際に非常に使えます。物理エンジンとか、描画スクリーンとか、
ステートマシン的なものを使う際には効果絶大です。
使いたい機能のインターフェースさえ作ってかぶせれば、OKと。 はじめからインターフェースがあればなおさらです。
スコープがもたらす副産物
BoilerCode(湯沸かしコード、とか?)と総称される、シングルトンとして呼びだされてほしいクラスを作るときに
毎回のように書かなければいけないコードは、書く必要から不要です。
書かないでよろしい。 すげー。
もうコンストラクタをprivateにしなくていい。
フレームワークとしては初めて、
”外見上でなにもしていないように見えてコード記述を管理している機構”なのでは。
シングルトンか否かは、使う側が設定していい。
とても素敵。
ただ、シングルトンを巡る問題は、シングルトンのコードの存在とは別の次元なので、あしからず。
感想
既存のものと並べてフレームワークと呼ぶ、というよりは、
インスタンスの依存、保持、ライフサイクルをサポートする機構/機能 です。
アノテーションでの記述は、楽の一言に尽きる。
これだけ簡単に使えるという事は、他人が書いた物を使う上でも、使用上の制約を低く保てる、という事。
他人の作った物をどれだけローリスクで限定範囲で使えるか、という部分と、
また他人が使えるように自分も細分化して物を作れるか、という事の補助になります。
複数のインスタンスから共通のステートマシンにアクセスする場合などがあるとしたら、
それはトランザクションがなんとかするのか。
驚くべきは、GWTでも使える(GIN)、Androidでばっちり動く(Without AOP版)、とかその辺。マジかYO。マジでした。
今後書き足します。
追記
GWTとの連携
Androidとの連携
without aopであれば使える。
Doja/Starとの連携,,,天変地異とか奇跡が起こったら書くかも。まあ今後、もう出番なさそうだし。
このページへのリンク
0 件のコメント:
コメントを投稿