次のような階層構造とする。
プレゼンテーション層(Seasar2のアクションクラス)
│
サービス層(独自に定義、DI設定も追加が必要)
│
ドメイン層(ドメイン・リポジトリ、独自に定義、DI設定も追加が必要)
│
データアクセス層(DAO・エンティティ)
上位の階層は1つ下の階層にあるクラスのみを呼び出せる。
2つ以上下の階層にあるクラスを直接呼び出してはいけない。
また、サービスとDAOはインターフェースとする。
(DI設定により、実装を差し替えられるようにするため。主にテストで必要となる)
リポジトリは、必ずドメイン生成とドメイン取得をメソッドとして用意する。
ドメインにDAOをインジェクションする場合、newはできないのでコピーメソッドを用意する。
public class Domain {
/**
* DI内容のコピー
*
* @return DIの内容をコピーしたドメインのインスタンス
*/
public Object copyDi() {
//
// DIではnewするごとにインジェクションはできず、DIコンテナの
// getメソッドを使うと、シングルトンやリクエスト、セッション
// ごとのインスタンスしか戻すことができない。
// よって、不特定多数のドメインを生成する場合は、インジェクション
// 設定をクローンメソッドによりコピーする必要がある。
// (そうしないと、シングルトンの内容を別のクラスで書き換える
// といったバグの温床になる)
//
try {
// クラスの新しいインスタンスを生成する
Object obj = this.getClass().newInstance();
// メソッド配列を取得する
Method[] methods = this.getClass().getMethods();
// getterメソッドのマップを作成するまでループ
Map<String, Method> getterMap = new HashMap<String, Method>();
for (int i = 0; i < methods.length; i++) {
// メソッド名がDaoかRepositoryのgetterの場合はマップに記録
if (methods[i].getName().indexOf("get") == 0) {
if (methods[i].getName().contains("Dao")
|| methods[i].getName().contains("Repository")) {
String targetName = methods[i].getName().substring(3);
getterMap.put(targetName, methods[i]);
}
}
}
// setterを使い、新しいインスタンスに設定を行うループ
for (int i = 0; i < methods.length; i++) {
// DaoかRepositoryのsetterの場合は、設定を行う
if (methods[i].getName().indexOf("set") == 0) {
if (methods[i].getName().contains("Dao")
|| methods[i].getName().contains("Repository")) {
String targetName = methods[i].getName().substring(3);
methods[i].invoke(obj, getterMap.get(targetName)
.invoke(this));
}
}
}
// 生成したクローンを呼び出し側に戻す
return obj;
} catch (Exception e) {
// 何らかのエラーが起きた場合はnullを戻す
return null;
}
}
}
コード中にも理由が書かれているが、ドメインはデータベース行数分だけ生成されるため、DIのインスタンス生成タイミングと一致しない。
1. リポジトリがドメインをDIする。(シングルトンで用意されているDaoがドメインにDIされる)
2. リポジトリでドメイン生成・ドメイン取得する場合は、上記1.のDIをコピーする。
Seasar2におけるドメイン・リポジトリのDI設定例は、次の通り。
<component class="org.seasar.framework.container.autoregister.FileSystemComponentAutoRegister">
<initMethod name="addClassPattern">
<arg>"rootpackage.domain"</arg>
<arg>".*Repository"</arg>
</initMethod>
</component>
<component class="org.seasar.framework.container.autoregister.FileSystemComponentAutoRegister">
<initMethod name="addClassPattern">
<arg>"rootpackage.domain"</arg>
<arg>".*Domain"</arg>
</initMethod>
</component>
FileSystemComponentAutoRegisterは、プロジェクト内のファイルから探す。
JARにまとめたクラスからDIさせる場合は、JarComponentAutoRegisterを使う。
0 件のコメント:
コメントを投稿