3

(Eclipse 起動プロファイルを介して) SWT アプリケーションを起動すると、次のスタック トレースが表示されます。

Exception in thread "main" java.lang.NoClassDefFoundError: org/eclipse/jface/resource/FontRegistry
    at org.eclipse.jface.resource.JFaceResources.getFontRegistry(JFaceResources.java:338)
    at org.eclipse.jface.window.Window.close(Window.java:313)
    at org.eclipse.jface.dialogs.Dialog.close(Dialog.java:971)
    at org.eclipse.jface.dialogs.ProgressMonitorDialog.close(ProgressMonitorDialog.java:348)
    at org.eclipse.jface.dialogs.ProgressMonitorDialog.finishedRun(ProgressMonitorDialog.java:582)
    at org.eclipse.jface.dialogs.ProgressMonitorDialog.run(ProgressMonitorDialog.java:498)
    at com.blah.si.workflow.SWTApplication.main(SWTApplication.java:135)

さて、これを奇妙にするもの:

  1. プロジェクトのビルド パスを変更し、jface.jar をソース プロジェクト (同じバージョン - 3.3.1) に置き換えると、エラーはなくなります。
  2. 同じ jar を使用する他のアプリケーション、および同じ起動プロファイルとプロジェクトのコピーはすべて正常に動作します。
  3. これは ではありませClassNotFoundException。クラスはクラスパスにあります。ソースを jar に添付すると、getFontRegistry メソッドにデバッグできます。このメソッドは、最終的NoClassDefFoundErrorに行 338 で をスローする前に、数回正常に実行されます。行 337 は、静的変数が初期化されているかどうかを確認する「if variable == null」ステートメントです。まだ初期化されていない場合、338行目で初期化しています。最初は null チェックに失敗し、初期化が実行されます。その後のメソッドのパスでは、null チェックがパスするため、既に初期化されている静的な値が返されます。最終パス (失敗したパス) で、(静的変数が既に初期化されていても) null チェックが再び失敗し、静的変数を再初期化しようとすると、NoClassDefFoundError投げられます。関連するソースは次のとおりです (336 行目以降、fontRegistry はプライベートな静的変数であり、他の場所では設定されていないことに注意してください)。

.

public static FontRegistry getFontRegistry() {
   if (fontRegistry == null) {
     fontRegistry = new FontRegistry(
         "org.eclipse.jface.resource.jfacefonts");
   }
   return fontRegistry;
}

.

  1. jar の新しいコピーを既に取得しており (破損していないことを確認するため)、.classpath ファイルと .project ファイルを削除し、新しいプロジェクトを開始して、起動プロファイルを再作成しました。変化なし。

上記の #3 の特殊性のため、ある種の奇妙なクラスローダの動作を疑っています。メソッドを通過する最後のパスが別のクラスローダにあるかのように見えますか?

アイデア?

更新: Pourquoi Litytestdata によって提供された回答により、ProgressMonitorDialog の 458 行目のすぐ上の try ブロックで何が起こるかに注意を払うようになりました。実際、そのコードは例外をスローしていましたが、それは finally ブロックによって飲み込まれていました。根本的な原因は、別のクラスの欠落でした (欠落しているクラスは、JFontRegistry またはそれに直接関連するクラスではなく、エッジ ケースで依存するクモの巣でした)。 、それが突破口だったので、Pourquoi'sを受け入れました。ありがとうございます。

4

5 に答える 5

3

Sanjiv JIVANによって書かれた 2006 年 7 月のこのブログ エントリに記載されているように、依存関係を保持する JAR ファイルが見つからないようです。

ClassNotFoundException と NoClassDefFoundError の違い

ClassNotFoundException報告されたクラスが ClassLoader によって見つからない場合、Aがスローされます。
これは通常、クラスが にないことを意味しCLASSPATHます。
問題のクラスが、親にロードされた別のクラスからロードされようとしているClassLoaderために、子のクラスが表示されClassLoaderないことを意味する場合もあります。
これは、App Server などのより複雑な環境で作業している場合に発生することがあります (WebSphere はこのようなClassLoader問題で悪名高い)。

java.lang.NoClassDefFoundErrorと混同しがちjava.lang.ClassNotFoundExceptionです。ただし、重要な違いがあります。

たとえば、例外 (のjava.lang.NoClassDefFoundErrorサブクラスであるため、実際にはエラーjava.lang.Error) のような

java.lang.NoClassDefFoundError:
org/apache/activemq/ActiveMQConnectionFactory

ActiveMQConnectionFactory クラスが にないという意味ではありませんCLASSPATH

実際、その正反対です。

これは、クラスActiveMQConnectionFactoryが によって検出されたことを意味しますがClassLoader、クラスをロードしようとすると、クラス定義の読み取り中にエラーが発生しました。

これは通常、問題のクラスに、 で見つからないクラスを使用する静的ブロックまたはメンバーがある場合に発生しますClassLoader

したがって、犯人を見つけるには、問題のクラスのソース (ActiveMQConnectionFactoryこの場合) を表示し、静的ブロックまたは静的メンバーを使用しているコードを探します
ソースにアクセスできない場合は、JADを使用して逆コンパイルしてください。

コードを調べて、次のようなコード行を見つけたとします。クラス SomeClass がCLASSPATH.

private static SomeClass foo = new SomeClass();

ヒント : クラスが属する jar を見つけるには、Web サイトjarFinderを使用できます。これにより、ワイルドカードを使用してクラス名を指定でき、jar のデータベースでクラスが検索されます。
jarhooでも同じことができますが、無料では使用できなくなりました。

クラスが属する jar をローカル パスで見つけたい場合は、jarscanなどのユーティリティを使用できます。検索するクラスと、jar および zip ファイル内のクラスの検索を開始するルート ディレクトリ パスを指定するだけです。

于 2009-02-28T09:18:53.687 に答える
2

それがクラスローダーの問題であるかどうかをより適切に処理するには、それが機能するコードを調べて追加します。

try
{
    final Class       clazz;
    final ClassLoader loader;

    clazz  = Class.forName("org/eclipse/jface/resource/FontRegistry");
    loader = clazz.getClassLoader(); 
    System.out.println("The classloader at step 1 is: " + loader);
}
catch(final Throwable ex)
{
    ex.printStackTrace();
}

次に、NoClassDefFoundError を取得している場合と同じことを行い、クラス ローダーが異なるかどうかを確認します。

次に、異なるのは ClassLoader であることを確認できます。これでどうなるか、また報告していただけますか?結果によっては、もっとアイデアが浮かぶかもしれません。

于 2009-03-02T02:52:36.407 に答える
2

上記のスタックトレースは、ここでの本当の問題を隠していると思います。以下は、org.eclipse.jface.dialogs.ProgressMonitorDialogrun内 のメソッドのコードです(私が追加したコメント付き):

public void run(boolean fork, boolean cancelable,
         IRunnableWithProgress runnable) throws InvocationTargetException,
         InterruptedException {
     setCancelable(cancelable);
     try {
         aboutToRun();
         // Let the progress monitor know if they need to update in UI Thread
         progressMonitor.forked = fork;
         ModalContext.run(runnable, fork, getProgressMonitor(), getShell()
                 .getDisplay());
     } finally {
         finishedRun();  // this is line 498
     }
}

Jared のスタックトレースの下から 2 行目は、このクラスの 498 行目で、ブロックfinishedRun()内の呼び出しです。finally本当の原因は、tryブロックで例外がスローされていることだと思います。ブロック内のコードfinallyも例外をスローするため、元の例外は失われます。

于 2009-02-28T13:11:01.820 に答える
1

優れた TofuBeer の回答に追加するには、次のNoClassDefFoundErrorことを示しているため:

  • クラスは によって検出されました。org.eclipse.jface.resource.FontRegistry ClassLoader
  • 見つからないを使用する静的ブロックまたはメンバーがあるなど、エラーをトリガーせずにロードすることはできませんClassClassLoader

org.eclipse.jface.resource.FontRegistryソースコードを見てみましょう:

静的変数の初期化はありません (スーパークラスもありません)。

ソースコードを見てみましょうorg.eclipse.jface.resource.JFaceResources

getFontRegistry()エラーがトリガーされる関数は、静的変数 fontRegistry を使用しています

/**
 * The JFace font registry; <code>null</code> until lazily initialized or
 * explicitly set.
 */
private static FontRegistry fontRegistry = null;

したがって、疑問が生じ ます:静的に初期化された変数が突然null再び考慮されるのはなぜですか?

どういうわけFontRegistryか、またはJFaceResourcesgc によってアンロードされるため?!

フィールドが静的であると宣言されている場合、クラスのインスタンスが最終的にいくつ (場合によってはゼロ) 作成されても、フィールドの具体化は 1 つだけ存在します。クラス変数と呼ばれることもある static フィールドは、クラスが初期化されるときに具体化されます (§12.4)。

したがって、クラスのインスタンスがいつでも存在するかどうかは問題ではありません。クラス自体がロードされている限り、フィールドは存在します。


これがEclipseプラグインである場合、これはこのFAQエントリに関連している可能性があります

新規ユーザー向けの典型的なシナリオを次に示します
。プラグイン XYZ を拡張するプラグインを作成しています。
これをコンパイルするには、Java Build Path プロパティ ページから、または .classpath ファイルを編集して、プラグイン XYZ の JAR ファイルへの参照をプロジェクトのビルド パスに追加します。
ランタイム ワークベンチを起動すると、次の驚くべきエラーが報告されます: java.lang.NoClassDefFoundError: XYZ.SomeClass.

ランタイム ワークベンチの起動構成で [プラグインとフラグメント] タブを調べ始めないでください。
このタブは、ランタイム ワークベンチに使用されるプラグインと、プラグインがワークスペースから読み込まれるか、Eclipse インストール ディレクトリから読み込まれるかにのみ影響します。

代わりに、プラグイン マニフェストの検索を開始します。
plugin.xml ファイルを編集し、XYZ が必要なプラグインとして記載されていることを確認します
次に、plugin.xml ファイルを保存します。
これにより、プロジェクトのビルド パスが自動的に更新されます。

プラグインを作成するときは、.classpath ファイルを手動で編集しないでください。
プラグインのマニフェスト エディターは、変更内容を上書きするだけです。あまり文明的ではありませんが、それが機能する方法です。

于 2009-02-28T00:38:08.447 に答える
1

FontRegistry クラスを自分でロードしようとすると (TofoBeer で説明したように)、FontRegistry を使用すると、次の JAR のクラスが依存クラスであることがわかります。

org.eclipse.core.commands_xxxxx.jar

この JAR をビルド パスに追加する必要があります。

于 2011-09-20T11:46:15.023 に答える