2011年4月24日日曜日

パッケージ内のクラス一覧を取得する

普段からSpringなどのDIフレームワークを使っているのですが、 Springでは、xmlファイルでパッケージ名を指定すると、そのパッケージ内のクラスをインスタンス化して注入してくれます。
便利な機能なのでよく使うのですが、どのようにしてパッケージ名からクラス一覧を取得しているかを知りませんでした。

実装を知るには、ソースを読めば手っ取り早いですが、今回はソースは読まずに、実装方法はこんな感じだろうかと考えてみます。

パッケージ名から、そのパッケージ内のクラス一覧を取得する
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;

/**
 * 指定パッケージ内のクラス情報のコレクションを返す。
 * 
 * @param x_packageName パッケージ名
 * @return クラス情報のコレクション
 */
public static Collection<Class<? extends Object>> getClasses(String x_packageName) {
 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
 JavaFileManager fm = compiler.getStandardFileManager(new DiagnosticCollector<JavaFileObject>(), null, null);

 // 一覧に含めるオブジェクト種別。(クラスのみを含める)
 Set<JavaFileObject.Kind> kind = new HashSet<JavaFileObject.Kind>();
 kind.add(JavaFileObject.Kind.CLASS);

 Collection<Class<? extends Object>> results = new ArrayList<Class<? extends Object>>();

 Iterable<JavaFileObject> jfos = null;
 try {
  jfos = fm.list(StandardLocation.CLASS_PATH, x_packageName, kind, false);
 } catch (IOException e) {
  e.printStackTrace();
 }
 if (jfos != null) {
  for (JavaFileObject fos : jfos) {
   try {
    Class<? extends Object> clazz = Class.forName(x_packageName + "." + fos.getName().replaceAll("\\.class$", ""));
    if (clazz.isEnum() || clazz.isInterface() || clazz.isAnonymousClass() || clazz.isLocalClass()) {
     continue;
    }
    results.add(clazz);
   } catch (ClassNotFoundException e) {
    e.printStackTrace();
   }
  }
 }
 return results;
}
使用するのはjava.lang.reflectパッケージのクラスかと思っていましたが、実際はjavax.toolsパッケージのクラスです。
これらのクラスがあるのは、javaのバージョン1.6以降のようです。
バージョン1.5以前ではもうちょっと面倒なやり方になると思います。

フレームワークの実装を知ることで、フレームワークの使い方を知ります。
今回は、実装を想像しただけですが...

0 件のコメント:

コメントを投稿