LoginSignup
4
5

More than 5 years have passed since last update.

任意のjarファイルから条件に合ったクラスをロードする

Last updated at Posted at 2016-05-26

Javaで作るシステムで、プラグインを作れるようなAPIを提供したくなることあるじゃないですか。
そういうときに使える、「jarファイルを指定すると、その中から指定interfaceの実装クラスを探してロードし、Classオブジェクトを返してくれる」メソッドloadClassesInJarを作りました。

指定interfaceを実装しているだけでなく、引数なしコンストラクタを持っていることもチェックしています。
ここは拡張して、指定シグネチャのコンストラクタを持っていること、というチェックにしても良いでしょう。

実際にソフトウェアに組み込む際は、起動時にプラグインディレクトリ内をなめて.jarファイルを探してloadClassesInJarでもいいですし、ファイルシステム監視を入れてプラグインディレクトリ内に新規ファイルが登場するたびloadClassesInJarという使い方もできます。

JDK5以降対応。

/**
 * 指定したjarファイルから指定interfaceの実装クラス(引数なしコンストラクタを持つもの)をすべてロードして返します。
 * @param jarPath jarファイル
 * @param i 実装しているべきinterfaceまたは親クラス
 * @param <Interface> 実装しているべきinterfaceまたは親クラス
 * @return jarファイル内に含まれる、条件に合うクラスすべてを含むリスト
 * @throws IOException jarファイルの読み込みができませんでした。
 */
public static <Interface> List<Class<Interface>> loadClassesInJar(String jarPath, Class<Interface> i) throws IOException
{
    final URL jarUrl = new File(jarPath).toURI().toURL();

    final URLClassLoader urlClassLoader = URLClassLoader.newInstance(new URL[]{jarUrl});

    final List<Class<Interface>> result = new ArrayList<Class<Interface>>();

    final JarFile jarFile = new JarFile(jarPath);
    for (final Enumeration<JarEntry> entries = jarFile.entries(); entries.hasMoreElements(); )
    {
        // ファイル要素に限って(=ディレクトリをはじいて)スキャン
        final JarEntry jarEntry = entries.nextElement();
        if (jarEntry.isDirectory()) continue;

        // classファイルに限定
        final String fileName = jarEntry.getName();
        if (!fileName.endsWith(".class")) continue;

        // classファイルをクラスとしてロード。
        final Class<?> clazz;
        try {
            clazz = urlClassLoader.loadClass(fileName.substring(0, fileName.length() - 6).replace('/', '.'));
        } catch (ClassNotFoundException e) {
            continue;
        }

        // iの派生型であることを確認
        if (!i.isAssignableFrom(clazz)) continue;

        // 引数なしコンストラクタを持つことを確認
        try {
            clazz.getConstructor();
        } catch (NoSuchMethodException e) {
            continue;
        }

        result.add((Class<Interface>) clazz);
    }

    return result;
}
4
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
5