山崎屋の技術メモ

IT業界で働く中でテクノロジーを愛するSIerのシステムエンジニア👨‍💻 | AndroidとWebアプリの二刀流🧙‍♂️ | コードの裏にあるストーリーを綴るブログ執筆者✍️ | 日々進化するデジタル世界で学び続ける探究者🚀 | #TechLover #CodeArtisan、気になること、メモしておきたいことを書いていきます。

java パッケージ配下のクラス一覧を取得 外部ライブラリは使用しない

外部ライブラリを使用せずにパッケージ配下のクラス一覧を取得する方法を調査した。

DIコンテナなどは、「パッケージを指定して、その配下のクラスをコンテナに登録する」というのが一般的だと思うが、その仕組みを理解するのに役に立つ。

あと、個人的な課題としてDTOクラスのテストコードを自動生成したいと思っていて、リフレクションを使用したクラス一覧の取得方法を確立しておく必要があった。

参考にしたページ。
package 配下のクラス一覧を取得する方法いろいろ - A Memorandum
Javaで特定のパッケージ配下のクラスを検索する - CLOVER

参考にしたページのやり方をそのままでもいいが、私の場合、jar ファイルに対応する必要がないので、もうちょっとすっきり書けそうだったので、自分で作ってみた。

ひとまず、ソース全文

package org.yyama.autotest;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

public class GetClassList {
	private static final String PK_NAME = "org.some.classes";

	public static void main(String... args) throws Exception {
		String packageName = PK_NAME;
		List<Class<?>> classes = getClasses(packageName);
		for (Class<?> class1 : classes) {
			System.out.println(class1.getName());
		}
	}

	/**
	 * パッケージ名を文字列で取得し、パッケージ直下のクラスをリストに詰めて返します。
	 * 
	 * @param packageName
	 *            パッケージ名
	 * @return クラスのリスト
	 * @throws IOException
	 * @throws URISyntaxException
	 * @throws ClassNotFoundException
	 */
	private static List<Class<?>> getClasses(String packageName)
			throws IOException, URISyntaxException, ClassNotFoundException {
		// クラスローダを取得
		ClassLoader cl = Thread.currentThread().getContextClassLoader();

		// パッケージ配下のリソースを取得(複数の場合あり)
		Enumeration<URL> e = cl.getResources(packageName.replace(".", "/"));

		// 返却用のリストを宣言
		List<Class<?>> classes = new ArrayList<>();

		// パッケージ配下のリソースの数だけループ
		for (; e.hasMoreElements();) {

			// リソースのURLを取得
			URL url = e.nextElement();

			// URLをファイルオブジェクトに変換
			File dir = new File(url.getPath());

			// ディレクトリ配下のファイル数分ループ
			for (String path : dir.list()) {

				// ".class"で終わるファイルのみ返却用のリストに追加
				if (path.endsWith(".class")) {
					classes.add(Class.forName(packageName + "." + path.substring(0, path.length() - 6)));
				}
			}
		}
		return classes;
	}
}

処理の大体の流れはコメントに記載した。

参考にしたページでは、

		// パッケージ配下のリソースを取得(複数の場合あり)
		Enumeration<URL> e = cl.getResources(packageName.replace(".", "/"));

の部分を、以下のように書いていた。

        URL url = classLoader.getResource(resourceName);

getResource と getResources の違いがある。あまり気にする必要はないと思うが、同じパッケージ名が複数のプロジェクトで使用されていた場合 getResource だと対応できないので getResources を使用している。

以下のパッケージを指定している箇所は、適宜変更して欲しい。パッケージ名は引数で渡す方法に変更してもいいかも知れない。Eclipse で使用する場合、引数の指定が面倒くさいので、ハードコードとしている。

	private static final String PK_NAME = "org.some.classes";

実行方法

クラス一覧を作成したいプロジェクトをビルドパスに加える。

方法は以下のとおり。

GetClassList クラスを含むプロジェクトを右クリックして properties を選択し、プロジェクトのプロパティ画面を表示する。左側のツリーから Java Build Path を選択する。

f:id:yyama1556:20160918145108p:plain

[ Add Class Folder ... ] を選択する。

f:id:yyama1556:20160918145216p:plain

フォルダ選択画面が表示されるので、クラス一覧を作成したいパッケージが置いてあるフォルダを選択する。下図の例では Classes プロジェクトの bin フォルダを選択している。

f:id:yyama1556:20160918145438p:plain

Classes プロジェクトの構成は以下のとおり。

f:id:yyama1556:20160918144101p:plain

クラス名を取得するのが目的なのでクラスの中身は空っぽ。

そして、実行結果がこちら。

org.some.classes.A
org.some.classes.B
org.some.classes.C

パッケージ配下のクラスがすべて取得できた。

おしまい。

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

EFFECTIVE JAVA 第2版 (The Java Series)

EFFECTIVE JAVA 第2版 (The Java Series)