6

目の前の状況は、タイトルが示すように単純ではありません。

JWS 経由で実行される Java 1.6_17。

私はクラスを持っており、MyClassそのインスタンスメンバー変数の1つは、クラスの初期化中に動的にいくつかの独自のクラスをロードしようとする誤ったサードパーティライブラリのタイプClass.forName(String)です。これらのケースの 1 つで、たまたま動的に呼び出されます: Class.forName("foo/Bar").このクラス名は、バイナリ名の JLS に従っていないため、最終的にjava.lang.NoClassDefFoundError: foo/Bar.

ClassLoaderサニタイズ メソッドを追加し、ClassLoader.findClass(String)このClassLoader.loadClass(String)問題を修正するカスタムがあります。

次のようなものを呼び出すことができます: myCustomClassLoader.findClass("foo/Bar")

その後、問題なくクラスをロードします。しかし、事前にクラスをロードしても、後で例外が発生します。これは、MyClass参照先Barの初期化中に、それらのClass.forName("foo/Bar")コードがどこかの静的ブロックで呼び出されるためです。使用しようとしていた ClassLoader が私のカスタム クラス ローダーである場合、これは実際には問題ありません。しかし、そうではありません。そんな衛生管理をしてcom.sun.jnlp.JNLPClassLoaderいないのが私の問題です。

Thread.currentThread().getContextClassLoader()カスタムクラスローダーに設定されていることを確認しました。しかし、これは (ご存知のように) 効果がありません。main()私が読んだいくつかのもののために、私が最初に行うこととしてそれを設定しましたが、それでもMyClass.class.getClassLoader()- JNLPClassLoader です。JNLPClassLoader ではなく、代わりに私のものを使用するように強制できれば、問題は解決しました。

クラスの初期化中に行われた静的な Class.forName("foo/Bar") 呼び出しを介して、どの ClassLoader を使用してクラスをロードするかを制御するにはどうすればよいですか? MyClass.class.getClassLoader()カスタム クラス ローダーを強制的に返却できれば、問題は解決すると思います。

誰かがアイデアを持っているなら、私は他のオプションを受け入れます。

TL;DR:Class.forName(String)によって参照されているサード パーティ ライブラリ内のすべての呼び出しを、MyClass自分の選択したクラスローダーを使用するように強制するのを手伝ってください。

4

3 に答える 3

4

これは、10 年前に読んだ、Java のクラスローディングの取り決めに関する記事を思い出させます。それはまだJavaWorldにあります。

この記事はあなたの質問に直接答えるものではありませんが、あなたの問題を理解するのに役立つかもしれません. MyClassカスタムクラスローダーを介してロードする必要があり、デフォルトのクラスロード動作を無効にする必要があります。これは、最初にクラスのロードを親クラスローダーに委任し、失敗した場合にのみクラスのロードを試行することです。

MyClassあなたのもの以外のクラスローダによってロードされることを許可すると、インスタンス化されたクラスからそのクラスローダへの関係が( 経由で)保存され、Java はその他のクラスローダを使用して、コンパイル時に見つかったgetClassLoader参照されたクラスを発見しようとし、カスタムクラスローダを効果的にバイパスします。クラスローダー階層と委任モデルのおかげです。が代わりにクラス ローダーによって定義されている場合は、2 回目のチャンスが得られます。MyClass

JAR に存在するクラスの委譲モデルをURLClassLoaderオーバーライドして打ち負かすような、何かの仕事のように思えます。loadClassブートストラップ アプローチ (上記のコメントで Thomas が提案したように) を使用して、カスタム クラス ローダーを介して単一のエントリポイント クラスを強制的にロードし、他のすべてのクラスをドラッグすることをお勧めします。

Class.forName の注意事項について警告している、同じ人物による別の JavaWorld の記事も参考になります。それも、クラスローディングの取り決めに影響を与える可能性があります。

これが役に立ち、有益であることを願っています。いずれにせよ、コードが進化するにつれて簡単に壊れてしまう難しいソリューションのように思えます。

于 2012-12-18T04:31:17.713 に答える
3

誰もが問題に答えるためにしっかりとした試みをしたと思います。しかし、問題を誤診していたことが判明しました。

私は同僚に問題を引き継がせ、デバッグ フラグをオンにした JDK を入手するように依頼しましたJNLPClassLoader

JDK を最初から再コンパイルするのはまったくの悪夢だったので、最終的に OpenJDK を入手しました (私たちは試しました)。OpenJDK を製品で動作させてデバッグした後JNLPClassLoader、リソース パスが間違っていた数か月前の非常に古い .jnlp をまだ使用していたため、クラスが見つからなかったことがわかりました。

正しい .jnlp を使用してサーバーを何度も正しく再デプロイし、実行時にクライアント アプリケーションに反映される多くのコード変更を行ったにも関わらず、古い .jnlp を使用し続けている理由がわかりませんでした。

クライアント マシンでは、Java が .jnlp ファイルをキャッシュしていることがわかりました。アプリケーションが変更され、アプリケーションが再ダウンロードされた場合でも、何らかの理由で新しい .jnlp が再ダウンロードされることはありません。したがって、すべての新しいコードを使用しますが、キャッシュされた .jnlp を使用してリソース/クラス パスを検索します。

次を実行する javaws -uninstall と、クライアント マシンで .jnlp キャッシュがクリアされ、次回は正しい .jnlp ファイルが使用されます。

これが問題だったのは本当に悲しいです。願わくば、これが私たちを引き起こしたように、他の誰かが無限のフラストレーションの時間を節約できることを願っています.

于 2013-06-13T17:35:49.923 に答える
2

s 自体にパッチを適用するアイデアがなくなった場合はClassLoader、ライブラリのバイトコード自体を書き直すことを検討してください。「foo/bar」定数を正しい値に置き換えるだけで、それ以上のクラスのロードをカスタマイズする必要はまったくありません。 !

これは、実行時または事前に行うことができます。

于 2012-12-18T05:25:03.597 に答える