スレッドのコンテキストクラスローダーと通常のクラスローダーの違い


242

スレッドのコンテキストクラスローダーと通常のクラスローダーの違いは何ですか?

つまり、異なるクラスローダーオブジェクトThread.currentThread().getContextClassLoader()getClass().getClassLoader()返す場合、どれが使用されますか?

回答:


151

各クラスは独自のクラスローダーを使用して他のクラスをロードします。その場合はClassA.class参照がClassB.class、その後ClassBのクラスローダのクラスパス上にある必要がありClassA、またはその親。

スレッドコンテキストクラスローダーは、現在のスレッドの現在のクラスローダーです。のクラスからオブジェクトを作成し、ClassLoaderCが所有するスレッドに渡すことができますClassLoaderD。この場合、オブジェクトはThread.currentThread().getContextClassLoader()、独自のクラスローダーでは利用できないリソースをロードしたい場合、直接使用する必要があります。


1
なぜそれがのローダー(またはのローダーの親)ClassBのクラスパス上になければならないのですか?それは可能ではありませんオーバーライドへのローダーが正常にロードできるように、場合でも、そのクラスパス上にないのですか?ClassAClassAClassAloadClass()ClassBClassB
Pacerier 2014

8
実際、すべてのクラスローダーにクラスパスがあるわけではありません。「ClassBはClassAのクラスローダーのクラスパス上にある必要がある」と書いたとき、「ClassBはClassAのクラスローダーによってロード可能である必要がある」という意味でした。90%は同じ意味です。ただし、URLベースのクラスローダーを使用していない場合は、2番目のケースのみが当てはまります。
David Roussel 2014

ClassA.class参照ClassB.class」とはどういう意味ですか?
jameshfisher 2014

1
ClassAにClassBのインポート文がある場合、またはClassAにClassB型のローカル変数を持つメソッドがある場合。ClassBがまだ読み込まれていない場合は、読み込まれます。
David Roussel 2014

私の問題はこのトピックに関連していると思います。私の溶液についてどう思いますか?私はそれが良いパターンではないことを知っていますが、それを修正する方法は他にありません
。stackoverflow.com/ questions / 29238493

109

これは元の質問には回答しませんが、質問は高度にランク付けされ、どのContextClassLoaderクエリに対してもリンクされているため、コンテキストクラスローダーをいつ使用するかという関連する質問に回答することが重要だと思います。短い答え:コンテキストクラスローダーを使用しないでください。ただしgetClass().getClassLoader()ClassLoaderパラメーターが欠落しているメソッドを呼び出す必要がある場合は、に設定してください。

あるクラスのコードが別のクラスをロードするように要求すると、 使用する正しいクラスローダーは、呼び出し元のクラスと同じクラスローダー(つまり、getClass().getClassLoader())です。これは、新しいクラスのインスタンスを初めて構築したり、静的メソッドを呼び出したり、静的フィールドにアクセスしたりしたときに、JVMがそれ自体で行うため、99.9%の時間で機能します。

リフレクションを使用してクラスを作成する場合(構成可能な名前付きクラスを逆シリアル化またはロードする場合など)、リフレクションを実行するライブラリは、アプリケーションからパラメーターとしてを受け取ることにより、使用するクラスローダーを常にアプリケーションに要求する必要がありClassLoaderます。アプリケーション(構築が必要なすべてのクラスを知っている)はそれを渡す必要がありますgetClass().getClassLoader()ます。

クラスローダーを取得する他の方法はすべて正しくありません。以下のようなライブラリの使用ハックした場合Thread.getContextClassLoader()sun.misc.VM.latestUserDefinedLoader()または、sun.reflect.Reflection.getCallerClass()それはAPIの欠乏によって引き起こされるバグです。基本的に、APIをThread.getContextClassLoader()設計した人が誰でもObjectInputStreamClassLoaderがパラメータとしてを、この間違いが今日までJavaコミュニティを悩ませてきました。

とはいえ、多くの多くのJDKクラスは、いくつかのハックの1つを使用して、使用するクラスローダーを推測します。一部の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);
}

5
はい、これは私が質問をする人に指摘する答えです。
Marko Topolnik 2016

6
この回答は、クラスローダーを使用してクラスをロードする(リフレクションなどを使用してインスタンス化する)ことに焦点を当てていますが、それを使用する別の目的(そして実際、私がこれまで個人的に使用した唯一の目的)は、リソースをロードすることです。同じ原則が適用されますか、それとも、呼び出し元のクラスローダーではなく、コンテキストクラスローダーを介してリソースをフェッチしたい場合がありますか?
エゴールハンス

90

javaworld.comに、違いを説明する記事があります=> 使用するClassLoader

(1)

スレッドコンテキストクラスローダーは、クラスローディング委譲スキームの周囲にバックドアを提供します。

JNDIを例にとると、その要点はrt.jar(J2SE 1.3以降)のブートストラップクラスによって実装されますが、これらのコアJNDIクラスは、独立したベンダーによって実装され、アプリケーションの-classpathにデプロイされる可能性のあるJNDIプロバイダーをロードする場合があります。このシナリオでは、親クラスローダー(この場合は原始クラスローダー)を呼び出して、その子クラスローダーの1つ(たとえば、システムクラスローダー)から見えるクラスをロードします。通常のJ2SE委任は機能しません。回避策は、コアJNDIクラスでスレッドコンテキストローダーを使用することです。これにより、適切な委任とは反対の方向にクラスローダー階層を効果的に「トンネリング」します。

(2)同じソースから:

この混乱はおそらくしばらくの間Javaにとどまるでしょう。あらゆる種類の動的リソースロードを備えたJ2SE APIを使用して、どのロード戦略を使用するかを推測してみてください。ここにサンプリングがあります:

  • JNDIはコンテキストクラスローダーを使用します
  • Class.getResource()およびClass.forName()は現在のクラスローダーを使用します
  • JAXPはコンテキストクラスローダーを使用します(J2SE 1.4以降)
  • java.util.ResourceBundleは、呼び出し元の現在のクラスローダーを使用します
  • java.protocol.handler.pkgsシステムプロパティで指定されたURLプロトコルハンドラは、ブートストラップおよびシステムクラスローダーでのみ検索されます
  • Java Serialization APIはデフォルトで呼び出し側の現在のクラスローダーを使用します

回避策はコアJNDIクラスがスレッドコンテキストローダーを使用するようにすることをお勧めしているため、これがこの場合にどのように役立つか理解していませんでした。 。したがって、スレッドのコンテキストクラスローダーでこの親クラスローダーを設定した場合でも、親を使用してそれらをどのようにロードできますか。
サニーグプタ

6
@SAM、推奨される回避策は実際には最後に言っていることとは正反対です。これbootstrapは、コンテキストクラスローダーとして設定されている親クラスローダーではなく、が設定されている子systemクラスパスクラスローダーですThread。次に、JNDIクラスはを使用Thread.currentThread().getContextClassLoader()して、クラスパスで利用可能なJNDI実装クラスをロードします。
Ravi Thapliyal 2013

「通常のJ2SE委任は機能しません」、なぜ機能しないのかわかりますか?Bootstrap ClassLoaderはrt.jarからのみクラスをロードでき、アプリケーションの-classpathからクラスをロードできないためですか?正しい?
YuFeng Shen

37

@David Rousselの回答に加えて、クラスは複数のクラスローダーによってロードされる場合があります。

クラスローダーの方法を理解しましょう仕組みを。

javarevisitedのjavin paulブログから:

ここに画像の説明を入力してください

ここに画像の説明を入力してください

ClassLoader 3つの原則に従います。

委任の原則

クラスは、必要なときにJavaにロードされます。Abc.classと呼ばれるアプリケーション固有のクラスがあるとします。このクラスをロードする最初の要求は、PrimordialまたはBootstrapクラスローダーにさらに委譲する親拡張ClassLoaderに委任するApplication ClassLoaderに送信されます。

  • Bootstrap ClassLoaderは、rt.jarから標準のJDKクラスファイルをロードし、Javaのすべてのクラスローダーの親です。ブートストラップクラスローダーには親がありません。

  • 拡張機能ClassLoaderは、クラスのロード要求をその親であるBootstrapに委任し、失敗した場合は、jre / lib / extディレクトリまたはjava.ext.dirsシステムプロパティが指すその他のディレクトリからクラスクラスをロードします。

  • システムまたはアプリケーションのクラスローダーであり、アプリケーション固有のクラスをCLASSPATH環境変数、-classpathまたは-cpコマンドラインオプション、JAR内のマニフェストファイルのClass-Path属性からロードします。

  • アプリケーションクラスローダーは、拡張クラスローダーの子であり、sun.misc.Launcher$AppClassLoaderクラスによって実装されます。

注:ほとんどがCでネイティブ言語で実装されているBootstrapクラスローダーを除き、すべてのJavaクラスローダーはを使用して実装されjava.lang.ClassLoaderます。

可視性の原則

可視性の原則によると、子ClassLoader親ClassLoaderによってロードされたクラスを表示できますが、その逆は正しくありません。

一意性の原則

この原則によると、親によってロードされたクラスは、子クラスローダーによって再度ロードされるべきではありません

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.