1. 概要

ほとんどのJavaライブラリは、JARファイルとして入手できます。 このチュートリアルでは、コマンドラインおよびJavaプログラムから特定のJARファイル内のクラスの名前を取得する方法について説明します。

次に、実行時に特定のJARファイルからクラスをロードするJavaプログラムの例を見ていきます。

2. JARファイルの例

このチュートリアルでは、 strippe-0.0.1-SNAPSHOT.jar ファイルを例として取り上げ、JARファイルでクラス名を取得する方法を説明します。

3. jarコマンドの使用

JDKにはjarコマンドが付属しています。 このコマンドをtおよびfオプションとともに使用して、JARファイルのコンテンツを一覧表示できます。

$ jar tf stripe-0.0.1-SNAPSHOT.jar 
META-INF/
META-INF/MANIFEST.MF
...
templates/result.html
templates/checkout.html
application.properties
com/baeldung/stripe/StripeApplication.class
com/baeldung/stripe/ChargeRequest.class
com/baeldung/stripe/StripeService.class
com/baeldung/stripe/ChargeRequest$Currency.class
...

アーカイブ内の*。classファイルのみに関心があるため、grepコマンドを使用して出力をフィルタリングできます。

$ jar tf stripe-0.0.1-SNAPSHOT.jar | grep '\.class$'
com/baeldung/stripe/StripeApplication.class
com/baeldung/stripe/ChargeRequest.class
com/baeldung/stripe/StripeService.class
com/baeldung/stripe/ChargeRequest$Currency.class
com/baeldung/stripe/ChargeController.class
com/baeldung/stripe/CheckoutController.class

これにより、JARファイル内のクラスファイルのリストが得られます。

4. JavaでのJARファイルのクラス名の取得

jar コマンドを使用してJARファイルからクラス名を出力するのは、非常に簡単です。 ただし、JavaプログラムのJARファイルからいくつかのクラスをロードしたい場合があります。 この場合、コマンドライン出力では不十分です。

目的を達成するには、JavaプログラムからJARファイルをスキャンし、クラス名を取得する必要があります。

JarFileクラスとJarEntryクラスを使用して、サンプルJARファイルからクラス名を抽出する方法を見てみましょう。

public static Set<String> getClassNamesFromJarFile(File givenFile) throws IOException {
    Set<String> classNames = new HashSet<>();
    try (JarFile jarFile = new JarFile(givenFile)) {
        Enumeration<JarEntry> e = jarFile.entries();
        while (e.hasMoreElements()) {
            JarEntry jarEntry = e.nextElement();
            if (jarEntry.getName().endsWith(".class")) {
                String className = jarEntry.getName()
                  .replace("/", ".")
                  .replace(".class", "");
                classNames.add(className);
            }
        }
        return classNames;
    }
}

それでは、上記のメソッドのコードを詳しく見て、どのように機能するかを理解しましょう。

  • try(JarFile jarFile = new JarFile(givenFile)) –ここでは、 try-with-resourcesステートメントを使用して、指定されたからjarFileを取得しました。 ]ファイルオブジェクト
  • if(jarEntry.getName()。endsWith(“。class”)){…} –各クラス jarEntry を取得し、クラスファイルのパスを修飾クラスに変更します名前。たとえば、“ package1 / package2 / SomeType.class”“ package1.package2.SomeType”に変更します。

メソッドが単体テストメソッドを介してサンプルJARファイルからクラス名を抽出できるかどうかを確認しましょう。

private static final String JAR_PATH = "example-jar/stripe-0.0.1-SNAPSHOT.jar";
private static final Set<String> EXPECTED_CLASS_NAMES = Sets.newHashSet(
  "com.baeldung.stripe.StripeApplication",
  "com.baeldung.stripe.ChargeRequest",
  "com.baeldung.stripe.StripeService",
  "com.baeldung.stripe.ChargeRequest$Currency",
  "com.baeldung.stripe.ChargeController",
  "com.baeldung.stripe.CheckoutController");

@Test
public void givenJarFilePath_whenLoadClassNames_thenGetClassNames() throws IOException, URISyntaxException {
    File jarFile = new File(
      Objects.requireNonNull(getClass().getClassLoader().getResource(JAR_PATH)).toURI());

    Set<String> classNames = GetClassNamesFromJar.getClassNamesFromJarFile(jarFile);

    Assert.assertEquals(EXPECTED_CLASS_NAMES, classNames);
}

5. JavaでJARファイルからクラスを取得する

JARファイルからクラス名を取得する方法を見てきました。 実行時にJARファイルから動的にいくつかのクラスをロードしたい場合があります。

この場合、最初に getClassNamesFromJarFile メソッドを使用して、指定されたJARファイルからクラス名を取得できます。

次に、 ClassLoader を作成して、必要なクラスを名前でロードできます。

public static Set<Class> getClassesFromJarFile(File jarFile) throws IOException, ClassNotFoundException {
    Set<String> classNames = getClassNamesFromJarFile(jarFile);
    Set<Class> classes = new HashSet<>(classNames.size());
    try (URLClassLoader cl = URLClassLoader.newInstance(
           new URL[] { new URL("jar:file:" + jarFile + "!/") })) {
        for (String name : classNames) {
            Class clazz = cl.loadClass(name); // Load the class by its name
            classes.add(clazz);
        }
    }
    return classes;
}

上記のメソッドでは、クラスをロードするためのURLClassLoaderオブジェクトを作成しました。 実装は非常に簡単です。

ただし、JARURLの構文を少し説明する価値はあります。 有効なJARURLには、「jar:+[JARファイルの場所]+!/」の3つの部分が含まれています。 

終了する「!/」は、JARURLがJARファイル全体を参照していることを示します。  JARURLの例をいくつか見てみましょう。

jar:http://www.example.com/some_jar_file.jar!/
jar:file:/local/path/to/some_jar_file.jar!/
jar:file:/C:/windows/path/to/some_jar_file.jar!/

getClassesFromJarFile メソッドでは、JARファイルはローカルファイルシステム上にあるため、URLのプレフィックスは「 file:」です。

それでは、テストメソッドを作成して、このメソッドがすべての期待されるClassオブジェクトを取得できるかどうかを確認しましょう。

@Test
public void givenJarFilePath_whenLoadClass_thenGetClassObjects()
  throws IOException, ClassNotFoundException, URISyntaxException {
    File jarFile
      = new File(Objects.requireNonNull(getClass().getClassLoader().getResource(JAR_PATH)).toURI());
    Set<Class> classes = GetClassNamesFromJar.getClassesFromJarFile(jarFile);
    Set<String> names = classes.stream().map(Class::getName).collect(Collectors.toSet());
    Assert.assertEquals(EXPECTED_CLASS_NAMES, names);
}

必要なClassオブジェクトを取得したら、 Java Reflection を使用して、クラスのインスタンスを作成し、メソッドを呼び出すことができます。

6. 結論

この記事では、特定のJARファイルからクラス名を取得するための2つの異なるアプローチを学びました。

jar コマンドは、クラス名を出力できます。 JARファイルに特定のクラスが含まれているかどうかを確認する必要がある場合に非常に便利です。 ただし、実行中のJavaプログラムからクラス名を取得する必要がある場合は、JarFileおよびJarEntryを使用するとそれを実現できます。

最後に、実行時にJARファイルからクラスをロードするJavaプログラムの例も見てきました。

いつものように、記事の完全なソースコードは、GitHubから入手できます。