9

JAR を動的にロードするアプリケーションを開発しています。JAR には、使用する一連のクラスの定義が含まれています。動的にロードされた JAR にある例外派生クラスをキャッチしようとするまで、すべてがうまくいきました。

次のスニペットは問題を示しています (DynamicJarLoaderは実際に JAR をロードするクラスです。 と の両方TestClassMyException外部 JAR にあります)。

public static void main(String[] args) {
    DynamicJarLoader.loadFile("../DynamicTestJar.jar");
    try {
         String foo = new TestClass().testMethod("42");
    } catch(MyException e) { }
}

実行しようとすると、次のようになります。

Exception in thread "main" java.lang.NoClassDefFoundError: dynamictestjar/MyException
Caused by: java.lang.ClassNotFoundException: dynamictestjar.MyException
        at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
        at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
Could not find the main class: dynamicjartestapp.Main. Program will exit.

に置き換えるcatch(MyException e)catch(Exception e)、プログラムは正常に動作します。これは、JAR がすでにロードされた後にJava検出できることを意味します。TestClassそのため、JVM はすべての Exception クラスをプログラムの実行開始時に定義する必要があり、それらが必要なとき (つまり、その特定の try-catch ブロックに達したとき) ではないようです。

なぜこれが起こるのですか?

編集

追加のテストをいくつか実行しましたが、これは確かにかなり奇妙です。これはの完全なソースですMyException:

package dynamictestjar;
public class MyException extends RuntimeException {
    public void test() {
        System.out.println("abc");
    }
}

このコードは次のように実行されます。

public static void main(String[] args) {
    DynamicJarLoader.loadFile("../DynamicTestJar.jar");
    String foo = new TestClass().testMethod("42");
    new MyException().test(); //prints "abc"
}

これはしません:

  public static void main(String[] args) {
    DynamicJarLoader.loadFile("../DynamicTestJar.jar");
    String foo = new TestClass().testMethod("42");
    new MyException().printStackTrace(); // throws NoClassDefFoundError
  }

NetBeans からテストを実行すると、すべてが計画どおりに進むことを指摘しておく必要があります。奇妙なことは、外部の Jar を Java の目から強制的に削除し、コマンド ラインからテスト アプリを実行したときにのみ始まります。

編集#2

答えに基づいて、私はこれを書きました。これは、私が受け入れたものが本当に正しいことを証明していると思います:

  public static void main(String[] args) {
    DynamicJarLoader.loadFile("../DynamicTestJar.jar");
    String foo = new TestClass().testMethod("42");
    class tempClass {
        public void test() {
            new MyException().printStackTrace();
        }
    }
    new tempClass().test(); // prints the stack trace, as expected
  }
4

4 に答える 4

6

「そのため、JVM は、必要なときではなく、プログラムの実行開始時にすべての例外クラスを定義する必要があるようです」 - いいえ、これは正しくないと思います。

あなたの問題は、これらの潜在的なエラーが原因であるに違いありません.JVMとは何の関係もありません。

  1. MyException.java ファイルの先頭に package ステートメントがありません。「package dynamictestjar」という意味のようですが、そこにはありません。
  2. MyException.java ファイルに正しい package ステートメントがありましたが、コンパイルすると、「dynamictestjar」という名前のフォルダーに MyException.class ファイルがありませんでした。
  3. コードを DynamicTestJar.jar にパッケージ化したとき (あなたはスター ウォーズ ファンです)、「dynamictestjar」フォルダを含むディレクトリを ZIP 圧縮しなかったため、正しいパスを取得できませんでした。したがって、クラス ローダーが認識するパスは次のようになります。正しくない。

これらはいずれも、クラス ローダーに関する大きな謎によるものではありません。自分の仕事が適切に行われていることを確認し、確認する必要があります。

このように JAR を動的にロードする必要があるのはなぜですか? 単純に JAR を CLASSPATH に入れ、クラス・ローダーにそれを取得させるだけでは達成できないことを何をしていますか?

これが単なる例であり、Java での「ベスト プラクティス」を示すものではないことを願っています。

catch(MyException e) { }

少なくともスタック トレースを出力するか、Log4J を使用して例外をログに記録する必要があります。

アップデート:

NetBeans からテストを実行すると、すべてが計画どおりに進むことを指摘しておく必要があります。奇妙なことは、外部の Jar を Java の目から強制的に削除し、コマンド ラインからテスト アプリを実行したときにのみ始まります。

これは、JAR へのパスにハードワイヤードしたパスが、コマンド ラインから実行したときに満たされていないことを示しています。

JVM は、NetBeans を使用する場合と、NetBeans を使用しない場合では機能しません。CLASSPATH、クラスローダー、およびパスの問題を理解することがすべてです。あなたが自分のものを整理すると、それはうまくいくでしょう。

于 2009-08-21T13:43:22.393 に答える
3

MyException は、動的にロードしている JAR の例外クラスですか?

クラス MyException を静的に使用していることに注意してください。これは、文字通りコード内にあるためです。

プログラムの実行中ではなく、コンパイル中に DynamicTestJar.jar をクラスパスに入れていますか? コンパイル中にクラスパスに入れないでください。これにより、コンパイラは JAR を使用している場所を静的な方法で表示します。

于 2009-08-21T14:06:51.723 に答える
1

実行時に JARDynamicTestJar.jarを動的にロードしていますが、コードをコンパイルするときにそれをクラスパスに追加します。

そのため、デフォルトのクラスローダが のバイトコードを読み込もうとすると、クラスパスでmain()見つけることができず、例外がスローされます。MyExceptionこれは、「DynamicJarLoader.loadFile("../DynamicTestJar.jar");」が実行される前に発生します!

そのため、ダイナミック JARのクラスが必要なクラスがロードされたときに、現在アクティブなクラスローダーでそれらのクラスが利用できることを確認する必要があります。後で JAR をクラスパスに追加できます。特に、そこから何かをインポートするクラスではできません。

于 2009-08-21T15:40:44.520 に答える
0

Java では、すべてのクラスはクラスローダーとクラスの FQN によって一意に識別されます。

ClassLoaders は階層的です。つまり、動的にロードされたクラスはその親のすべてのクラスにアクセスできますが、親はそのクラスに直接アクセスできません。

子クラスローダが親を拡張するクラスを定義する場合、親のコードはリフレクションまたは親クラスによってのみそのクラスを参照できます。

すべての Java コードをリフレクティブと考えると、これは簡単になります。いいえ:

Class.forName("MyException")

現在のクラスは子クラスローダーにアクセスできないため、これを行うことはできません。

于 2009-08-21T15:52:53.947 に答える