Google Guiceをはじめて触ってみる
GoogleのDIコンテナGuiceを試してみました。xmlを書かないでアノテーションでDIを解決している。Seasar2も自動登録でxmlを書かなくてよくなるけれど、あることはあるし書こうと思えば書けるが、Guiceはxmlは一切書かないみたいな感じ。で、どうやって試したかというと、
またしてもこちらの本
- 作者: 須賀幸次,木村聡,西川麗,高安厚思,白井博章,椎野峻輔,岡薫,藤村浩士,ひがやすを
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 2006/02/25
- メディア: 大型本
- 購入: 7人 クリック: 51回
- この商品を含むブログ (67件) を見る
の第4章にCSVの住所ファイルを読み込んで文字を加工してコンソールに出力するってプログラムがあって、ずいぶん昔にそのサンプルを試したプロジェクトが私のEclipseにあったので(本のサンプルと少し違うところがありますが同じことを実現してます)、そちらをGuiceで書き直してGuiceの動きを試してみました。
main()とAddressManagerImplのコードはこれ
コンテナにAddressManagerの実装をくれと要求すると、読み込むファイルのPATHがコンストラクタインジェクションされ、readerプロパティにAddressFileReaderImplが明示的にインジェクションされ、printerプロパティにはConsolePrinterが自動登録されたAddressManagerImplのオブジェクトが帰ってくるよね。
guiceではxmlファイルの代わりにmoduleというインターフェイスを実装したクラスに実装を登録しておくって感じなので、早速MyModuleを作ってみる。
package example; import com.google.inject.Binder; import com.google.inject.Module; public class MyModule implements Module{ public void configure(Binder binder) { //mainで取得するクラス binder.bind(AddressManager.class).to(AddressManagerImpl.class); //以下AddressManagerにDIされる実装 binder.bind(AddressFileReader.class).to(AddressFileReaderImpl.class); binder.bind(AddressPrinter.class).to(ConsolePrinter.class).asEagerSingleton(); binder.bind(String.class).toInstance("/address.csv"); } }
これでコンテナにそれぞれのインタフェースの実装(インタフェースの実装が2つ以上ある場合は?それは後で。とりあえずこれでいける)を登録できました。そして実際にDIされるようにAddressManagerImplをちょっとかえる。
package example; import java.util.ArrayList; import com.google.inject.Inject; public class AddressManagerImpl implements AddressManager { private String fileName; private ArrayList<AddressData> addresses; private AddressFileReader reader; private AddressPrinter printer; @Inject public AddressManagerImpl(String fileName) { this.fileName = fileName; } /* * public void initAddressManager(AddressPrinter printer){ * this.printer.printTitle(); } */ @Inject public void setReader(AddressFileReader reader) { this.reader = reader; } @Inject public void setPrinter(AddressPrinter printer) { this.printer = printer; } public void load() { addresses = reader.readFile(fileName); } public void printAll() { for (AddressData address : addresses) { printer.print(address); } } public void printHead() { this.printer.printTitle(); } }
インジジェクションしたいものに@Injectアノテーションをつけてあげればこれで見事さっき登録した実装がDIされます。メソッドでもコンストラクタでもフィールドでもいいみたい。メソッドはsetXXX()という名前でなくても問題ないようだ。基本的にはこれだけ変えてあげればこのサンプルに関しては問題なく動く。
ちなみにコメントにした部分はサンプルではinitMethodで出力開始的文言をコンソール出力する処理を動かしているのですが、それはどうすればいいのか分からなかったので、printHead()ってメソッドを作って明示的に呼び出すように変えましたのです。
実装2つとか登録できないのか?どうするんだってことなんですがStringで試してみました。まず明らかにできそうも無いけれど、
binder.bind(String.class).toInstance("/address.csv"); binder.bind(String.class).toInstance("/work/address.csv");
↑同じStringを登録したら、
A binding to java.lang.String was already configured at example.MyModule.configure(MyModule.java:15).
って怒られた。
アノテーションを使って実現できるらしい。まず1実装ごとにアノテーションを作ります。
package example.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import com.google.inject.BindingAnnotation; @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD,ElementType.PARAMETER}) @BindingAnnotation public @interface Cpath { }
package example.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import com.google.inject.BindingAnnotation; @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD,ElementType.PARAMETER}) @BindingAnnotation public @interface Npath { }
それでこうやって登録しておく。
binder.bind(String.class).annotatedWith(Cpath.class).toInstance("/address.csv"); binder.bind(String.class).annotatedWith(Npath.class).toInstance("/work/address.csv");
それでAddressManagerImplでDIする処理をこんなかんじにすると指定した実装がDIされました。
public class AddressManagerImpl implements AddressManager { private String fileName; private ArrayList<AddressData> addresses; private AddressFileReader reader; private AddressPrinter printer; @Inject public AddressManagerImpl(@Cpath String fileName) { this.fileName = fileName; } 〜以下略〜
とりあえずこんな感じで少しずつやっていきます。webでも動かしてみたいと思います。2.0からProviderというものがアノテーションになっているみたいでその辺の違いも試してみたんですがもう少し整理できてからメモっとこうと思っております。