スレッドのコンテキスト クラス ローダーと通常のクラス ローダーの違いは何ですか?
つまり、Thread.currentThread().getContextClassLoader()
と がgetClass().getClassLoader()
異なるクラス ローダー オブジェクトを返す場合、どちらが使用されるのでしょうか?
ベストアンサー1
これは元の質問への回答ではありませんが、この質問はどのContextClassLoader
クエリに対しても高く評価され、リンクされているため、コンテキスト クラス ローダーをいつ使用すべきかという関連する質問に答えることが重要だと思います。簡単な答え: コンテキスト クラス ローダーは絶対に使用しないgetClass().getClassLoader()
でください。ただし、パラメーターがないメソッドを呼び出す必要がある場合は設定してくださいClassLoader
。
あるクラスのコードが別のクラスのロードを要求する場合、使用する正しいクラスローダーは呼び出し元クラスと同じクラスローダー(つまりgetClass().getClassLoader()
)です。これは、99.9%の確率で動作します。これはJVM自体が行うことです新しいクラスのインスタンスを初めて構築するとき、静的メソッドを呼び出すとき、または静的フィールドに初めてアクセスするとき。
リフレクションを使用してクラスを作成する場合 (構成可能な名前付きクラスの逆シリアル化やロードなど)、リフレクションを実行するライブラリは、アプリケーションからをパラメータとして受け取ることによって、使用するクラス ローダーを常にアプリケーションに問い合わせるClassLoader
必要があります。構築する必要があるすべてのクラスを認識しているアプリケーションは、それを に渡す必要がありますgetClass().getClassLoader()
。
クラスローダーを取得する他の方法は正しくありません。ライブラリが次のようなハックを使用している場合、Thread.getContextClassLoader()
、sun.misc.VM.latestUserDefinedLoader()
、 またはsun.reflect.Reflection.getCallerClass()
これは API の欠陥によって発生するバグです。基本的に、API を設計した人が をパラメータとして受け入れることを忘れたThread.getContextClassLoader()
ために発生し、この間違いは今日まで Java コミュニティを悩ませています。ObjectInputStream
ClassLoader
とはいえ、多くの JDK クラスでは、使用するクラス ローダーを推測するために、いくつかのハックのいずれかを使用しています。 を使用するものContextClassLoader
(これは、共有スレッド プールで異なるアプリケーションを実行する場合や を離れる場合に失敗しますContextClassLoader null
)、スタックをウォークするもの (これは、クラスの直接の呼び出し元自体がライブラリである場合に失敗)、システム クラス ローダーを使用するもの (これは、 内のクラスのみを使用するようにドキュメント化されている限り問題ありませんCLASSPATH
)、またはブートストラップ クラス ローダーを使用するもの、および上記の手法の予測できない組み合わせを使用するもの (これは、状況をより混乱させるだけです) などがあります。この結果、多くの嘆きと歯ぎしりが引き起こされました。
このような API を使用する場合は、まず、クラス ローダーをパラメーターとして受け入れるメソッドのオーバーロードを探します。適切なメソッドがない場合は、ContextClassLoader
API 呼び出しの前に を設定して (呼び出し後にリセットして) みてください。
ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
// call some API that uses reflection without taking ClassLoader param
} finally {
Thread.currentThread().setContextClassLoader(originalClassLoader);
}