DIコンフィグクラス。
別掲記事にも記載があるが、今回紹介しているメタデータエディタのサンプルでは、ドメイン駆動実装を試験的に採用しているため、DIの仕組みが必要となる。前掲プログラムとほぼ同じものであるが、一応掲載した。
package di;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import util.AppLog;
import util.PropUtil;
import util.SvFile;
import exception.ApplicationInternalException;
//---------------------------------------------------------------------------//
// このクラスの使い方
// 1. DI設定のタブ区切りファイル "di.tsv" を作成します。
// 2. DI設定は次のように記述します。
// "[DI設定するクラスの名前]"[tab]"[パッケージ]"
// 実際の設定は、こんな感じです。
// "Service" "service.impl"
// "Repository" "domain"
// "Dao" "dao.impl"
// 3. 上記2.のように記述した場合、"service.impl"パッケージにある
// "Service"という文字列を含むクラスを検索し、インスタンスを作成します。
// 作成したインスタンスは"XXXService"というクラス名をキーとして、
// 本クラスのマップに保存されます。
// クラス名が"XXXServiceImpl"だとしても、設定ファイルに"Service"
// で登録されていれば、マップには"XXXService"で登録します。
// 4. マップに保存されたクラス全てを調べ、"setXXXService"という名前
// を持つメソッドがあれば設定します。
// 以上のようにして、DIを行います。
// DIするために必要な命名規則は、次の通りです。
// (1) インターフェース名、クラス名の末尾を揃える。
// "XXXService"、"XXXRepository"、"XXXDao"などのようにDI設定
// で検索できるよう、インターフェース名、クラス名を揃えます。
// (2) インターフェースを用いる場合は、"XXXServiceImpl"などのように
// DI設定のキーワードの後に"Impl"などをつけて、クラスを実装してください。
// また、クラスを実装するパッケージは"service.impl"などインターフェース
// とは別のパッケージとしてください。
// (3) DI設定させたいメンバがある場合は、必ずpublicメソッドで
// "setXXXService"というメソッドを用意してください。
//---------------------------------------------------------------------------//
/**
* DI設定クラス
*/
public class DiConfig {
/** シングルトンのインスタンス */
private static DiConfig diConfig = null;
/**
* シングルトンのインスタンスを取得します。
*
* @return インスタンス
*/
public static DiConfig getInstance() {
if (diConfig == null) {
diConfig = new DiConfig();
}
return diConfig;
}
/** DIマップ */
private Map<String, Object> diMap = null;
/**
* 名前を指定して、DIインスタンスを取得します。
*
* @param name
* 取得するDIインスタンス名
* @return DIインスタンス
*/
public Object getDiInstance(String name) {
return diMap.get(name);
}
/**
* コンストラクタ
*/
private DiConfig() {
try {
loadDb();
injectDependency();
} catch (IOException e) {
throw new ApplicationInternalException(
PropUtil.get("msg.err.dbLoad"));
}
}
/**
* 設定を読み込み、依存性注入を実施します。
*/
private void injectDependency() {
// 設定が存在しなければエラーとする
List<List<String>> csvLineList = getSvLineList();
if (csvLineList == null || csvLineList.size() == 0) {
throw new ApplicationInternalException(
PropUtil.get("msg.err.noDiConfig"));
}
// DI基本パスを取得する
String diBasePath = PropUtil.get("di.config.basePath");
if (diBasePath == null || diBasePath.isEmpty()) {
throw new ApplicationInternalException(
PropUtil.get("msg.err.noDiConfigBasePath"));
}
// 基本パスがjarファイルである場合は、jarファイルを参照する
if (diBasePath.contains(".jar")) {
injectDependencyFromJar(diBasePath, csvLineList);
return;
}
// 全てのDI設定を読み込むまでループ
diMap = new HashMap<String, Object>();
for (List<String> csvLine : csvLineList) {
// CSV行の設定値が2つでない場合はエラーとする
if (csvLine.size() != 2) {
throw new ApplicationInternalException(
PropUtil.get("msg.err.ngConfig"));
}
// DIのキーワードとDIパッケージを元に、DI設定を検索する
searchDiSetting(diBasePath, csvLine.get(0), csvLine.get(1));
}
// DI設定に基づき、インジェクションを実行する
injectByDiSetting();
}
/**
* DIキーワードでDIパッケージを検索し、見つかったクラスを記憶します。
*
* @param diBasePath
* DI基本パス
* @param diKeyword
* DIキーワード
* @param diPackage
* DIパッケージ
*/
private void searchDiSetting(String diBasePath, String diKeyword,
String diPackage) {
// DI基本パス+DIパッケージの位置にある全てのクラスファイルを取得する
File dir = new File(diBasePath + "/" + diPackage.replaceAll("\\.", "/"));
File[] fileList = dir.listFiles();
if (fileList == null || fileList.length == 0) {
return;
}
// DIキーワードでDIパッケージを検索し、見つかったクラスを記憶する
for (File file : fileList) {
// ディレクトリは処理対象外とする
if (file.isDirectory()) {
continue;
}
// ファイル名に[DIキーワード]が含まれる場合、DI設定として保存する
if (file.getName().contains(diKeyword)) {
try {
String className = file.getName().substring(0,
file.getName().indexOf(diKeyword))
+ diKeyword;
String fullPathClassName = diPackage
+ "."
+ file.getName().substring(0,
file.getName().indexOf(".class"));
Object newInstance = Class.forName(fullPathClassName)
.newInstance();
diMap.put(className, newInstance);
} catch (Exception e) {
throw new ApplicationInternalException(
PropUtil.get("msg.err.createDiInstanceFailed"));
}
}
}
}
/**
* DI設定にあるパッケージを再帰的に操作し、依存性注入を実行します。
*/
private void injectByDiSetting() {
// DIマップ内の全てのクラスを処理するまでループ
for (String diClassName : diMap.keySet()) {
// インジェクション可能なものにインジェクションを行う
for (String targetClassName : diMap.keySet()) {
// メソッド配列を取得する
Method[] methods = diMap.get(targetClassName).getClass()
.getMethods();
// メソッドにDIできるものがあればインジェクションを行う
for (Method method : methods) {
if (method.getName().equals("set" + diClassName)) {
try {
method.invoke(diMap.get(targetClassName),
diMap.get(diClassName));
} catch (Exception e) {
throw new ApplicationInternalException(
PropUtil.get("msg.err.setDiInstanceFailed"));
}
}
}
}
}
}
/**
* jarファイルからDI設定条件に合致するクラスを探しだし、依存性挿入を行います。
*
* @param jarFileName
* jarファイル名
* @param csvLineList
* CSV行リスト
*/
private void injectDependencyFromJar(String jarFileName,
List<List<String>> csvLineList) {
try {
// jarファイル内の全エントリを取得する
File file = new File(jarFileName);
JarFile jarFile = new JarFile(file);
Enumeration<JarEntry> jarEntries = jarFile.entries();
// jarファイル内の全エントリから、クラスのエントリのみを抽出する
List<String> classNameList = new ArrayList<String>();
while (jarEntries.hasMoreElements()) {
// jarファイル内のエントリを取得し、クラスでなければループの先頭に戻る
JarEntry jarEntry = jarEntries.nextElement();
if (!jarEntry.getName().endsWith(".class")) {
continue;
}
// クラス名を求める
String className = jarEntry.getName().substring(0,
jarEntry.getName().lastIndexOf(".class"));
className = className.replace("/", ".");
classNameList.add(className);
}
// 全てのDI設定を読み込むまでループ
diMap = new HashMap<String, Object>();
for (List<String> csvLine : csvLineList) {
// CSV行の設定値が2つでない場合はエラーとする
if (csvLine.size() != 2) {
throw new ApplicationInternalException(
PropUtil.get("msg.err.ngConfig"));
}
// DIのキーワードとDIパッケージを元に、DI設定を検索する
searchDiSettingFromJar(file, csvLine.get(0), csvLine.get(1),
classNameList);
}
// DI設定に基づき、インジェクションを実行する
injectByDiSetting();
} catch (Exception e) {
throw new ApplicationInternalException(e);
}
}
/**
* jarファイル内からDI設定に合致するクラスを探し、DIマップを作成します。
*
* @param file
* jarファイル
* @param diKeyword
* DIキーワード
* @param diPackage
* DIパッケージ
* @param classNameList
* クラス名リスト
*/
private void searchDiSettingFromJar(File file, String diKeyword,
String diPackage, List<String> classNameList) {
try {
// jarファイルのURLを取得する
URL[] urls = new URL[] { file.toURI().toURL() };
// クラスローダを作成する
ClassLoader classLoader = URLClassLoader.newInstance(urls);
// クラス名リストを検索し、DIキーワードに合致するクラスを探し出す
for (String className : classNameList) {
// DIキーワードが見つからない場合は、ループの先頭に戻る
if (!className.contains(diKeyword)) {
continue;
}
// 当該クラスがDIパッケージで示されるパッケージにない場合は、ループの先頭に戻る
if (!className.contains(diPackage)) {
continue;
}
// 名前からクラスを取得する
Class<?> cls = Class.forName(className, true, classLoader);
// クラスのインスタンスを取得する
Object newInstance = cls.newInstance();
// DIマップに作成したインスタンスを追加する
String diClassName = className.substring(className
.lastIndexOf(".") + 1);
diClassName = diClassName.substring(0,
diClassName.indexOf(diKeyword))
+ diKeyword;
diMap.put(diClassName, newInstance);
// TODO 消す
// +
AppLog.getInstance().debug(
"diClassName : " + diClassName + " newInstance : "
+ newInstance.getClass().getName());
// -
}
} catch (Exception e) {
throw new ApplicationInternalException(e);
}
}
/** データベース */
private SvFile db = null;
/**
* データベースのインスタンスを取得します。
*
* @return
* @throws IOException
*/
private SvFile getDb() throws IOException {
if (db == null) {
db = new SvFile();
}
return db;
}
/**
* SV行データリストを取得します。
*
* @return 行データリスト
*/
private List<List<String>> getSvLineList() {
// SV行データリストを取得する
List<List<String>> svLineList;
try {
svLineList = getDb().getSvLineList();
} catch (IOException e) {
throw new ApplicationInternalException(
PropUtil.get("msg.err.dbRef"));
}
// SV行データリストを呼び出し側に戻す
return svLineList;
}
/**
* データベースからデータをロードします。
*
* @throws IOException
*/
private void loadDb() throws IOException {
// ファイルを開いてみる
String fileName = PropUtil.get("di.config.fileName");
File f = new File(fileName);
// ファイルが存在しない場合は新規作成する
if (!f.exists()) {
FileWriter fw = new FileWriter(f);
fw.close();
}
// データベースファイルを開く
getDb().loadFile(PropUtil.get("di.config.fileName"));
}
}
0 件のコメント:
コメントを投稿