easydi
超簡単DIコンテナ。このページの一番最後に開発時のeclipseプロジェクトの添付ファイルがあります。
かなり適当ですが、半日で作ったので勘弁。
※特徴
- アノテーションベース
- 再帰的な注入が可能
- ライフサイクル管理が可能(シングルトン扱いと、毎回生成する扱いの2パターン)
- プロパティ設定が可能。(プロパティファイルから読み込みと、Javaで設定の2パターン)
※簡単な仕様
1)概念
- コンテナが管理するインスタンスを「コンポーネント」と呼びます。
- 「コンポーネント」は、ある「型」と「名前」で一意になります。「型」ごとに複数のコンポーネントを保有できるイメージです。
- 「コンポーネント」は、「コンポーネント」もしくは「コンポーネントではない通常のBean」に注入することができます。
- 「コンポーネント」は、「コンポーネントファインダ」で明示的に取得することが可能です。
2)コンポーネントセット
- ある一組のコンポーネントを作るために必要な1郡のセットです。
A)インタフェース
public BeanInterface {
public void execute();
}
B)アノテーション
@InjectionType(Interface=BeanInterface.class)
@Retention(RetentionPolicy.RUNTIME)
publlc @interface BeanInterface{
public String name();
}
C)実装クラス
public BeanInterfaceImpl implements BeanInterface {
public void execute(){System.out.println("foo");}
}
3)コンポーネントのセットアップと取得
PropertyComposeStrategy propertyComposeStrategy = new PropertyComposeStrategyFromFile();
ComponentManager manager = new ComponentManager (propertyComposeStrategy );
Composer composer = new Composer(componentManager);
ComponentFinder finder = new ComponentFinder(composer,componentManager);
componentManager.load(FooService.class,FooServiceImpl.class, "foo",LifeCycle.Application);
componentManager.load(FooService.class,FooServiceImpl2.class, "bar",LifeCycle.Requeied);
componentManager.load(BarService.class,BarServiceImpl.class, "baz",LifeCycle.Requeied);
componentManager.load(Injected.class,InjectedImpl.class, "foo",LifeCycle.Requeied);
FooService foo = finder.find(FooService.class,"foo");
foo.service();
型と名前に対して実装クラスをひとつ紐付けます。
4)コンポーネントのインジェクション
class SampleClass{
//注入先のフィールドにアノテーションする
@InjectFooService("name=foo")
FooService fooService;
public void execute(){
fooService.service();
}
}
注入時は下記の通り。
SampleClass sampleClass = new SampleClass(); //これはコンポーネントではない。
composer.compose(sampleClass);
sampleClas.execute();
5)コンポーネント間のインジェクション
class BarServiceImpl implemsnts BarService{
//注入先のフィールドにアノテーションする。指定の仕方は4)とおなじ
@InjectFooService("name=foo")
FooService fooService;
public void execute(){
fooService.service();
}
}
コンポーネントをファインダから取得すると、自動的にインジェクション済みとなる。
BarService bar = finder.find(BarService.class,"baz");
bar.service();
6)コンポーネントのライフサイクル
componentManager.load(FooService.class,FooServiceImpl.class, "foo",LifeCycle.Application); ←シングルトン。VM毎にインスタンスは唯一
componentManager.load(FooService.class,FooServiceImpl2.class, "bar",LifeCycle.Requeied);←要求都度生成。
LifeCycle.Requeiedは注意が必要。LifeCycle.Requeiedとした場合、コンポーネントが生成されるタイミングはそのコンポーネントがFinderから要求される都度。再帰的にコンポーネントが注入されるときにもFinderがよばれるのでそのたび作り直される。ただしLifeCycle.Requeiedのコンポーネントを保有するコンポーネントがLifeCycle.Applicationである場合は、親コンポーネントの取得を何度要求しても親コンポーネントが保有する子コンポーネントは同一インスタンスとなる。これは、親コンポーネントのインスタンスは一度作られたらそのまま同じものが使いまわされるためである。
たとえば上記のBarServiceImplの例でいうと
型 ライフサイクル
FooService Application
BarService Request
→BarServiceは要求都度生成。ただし、全てのBarServiceが参照するFooServiceはシングルトン
型 ライフサイクル
FooService Request
BarService Application
→BarServiceはシングルトン。FooServiceは、BarServiceが保有するインスタンスは全て同一。
ただし、BarServiceを経由せず取得したFooServiceのインスタンスは、上記とは別インスタンスとなる。
というかんじ。
7)プロパティの設定
①プロパティファイルから読み込む方法 (PropertyComposeStrategyFromFile)
コンポーネントの型インタフェースのクラス名(FQCN)とコンポーネント名より、以下の名前のファイルがロードされ、値が設定される。
例)コンポーネントの型インタフェースがeasydi.FooComponent、コンポーネント名がfooの場合
easydi/FooComponent.foo
上記の名称のファイルをクラスローダ経由で取得する。
プロパティファイルには、Bean属性名に設定する値をプロパティファイル形式で記載する。
callCount=10
これで、コンポーネント取得時に毎回上記の値が設定され返却される。(ライフサイクルがRequestの場合は、インスタンスが生成されるたびに設定される)
②JavaAPIから設定する方法 (PropertyComposeStrategyFromJavaCode)
PropertyComposeStrategyFromJavaCode propertyComposeStrategyFromJavaCode = new PropertyComposeStrategyFromJavaCode();
propertyComposeStrategyFromJavaCode
.getValueSetup(FooService.class, "foo")
.setup("callCount1", "999")
.setup("callCount", "999");
TODO
- アノテーションをコンポーネントごとに作らなくても良いようにする
- ComposerとPropertyComposeStrategyを毎回APIで意識しなくても良いようにする
- もうちょっとまともなドキュメントにする
- jacadoc
- アスペクト機能
- プロパティからコンポーネントをロードする機能。JUnitでのテストのことを考え、Javaからもロードできる(併用可能、上書きする)ようにするのがいい。
- シングルトン保証できてるかチェック。シングルトンは要求時ではなくブート時にインスタンス生成しているだろうか?
最終更新:2008年10月12日 12:31