web-dev-qa-db-ja.com

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

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

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

221
abracadabra

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

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

139
David Roussel

Javaworld.comには、違いを説明する記事があります=> どのClassLoaderを使用すべきか

(1)

スレッドコンテキストクラスローダーは、クラスローディング委任スキームの裏口を提供します。

JNDIを例にとると、そのガットはrt.jarのbootstrapクラス(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プロトコルハンドラーは、bootstrapおよびシステムクラスローダーでのみ検索されます。
  • Java Serialization APIはデフォルトで呼び出し元の現在のクラスローダーを使用します
86
Timour

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

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

リフレクションを使用してクラスを作成する場合(設定可能な名前付きクラスをデシリアライズまたはロードする場合など)、リフレクションを行うライブラリは常にアプリケーションに問い合わせるどのクラスローダーを使用するか、ClassLoaderアプリケーションからのパラメーターとして。アプリケーション(構築が必要なすべてのクラスを知っている)は、getClass().getClassLoader()を渡す必要があります。

クラスローダーを取得するその他の方法は正しくありません。ライブラリが Thread.getContextClassLoader()Sun.misc.VM.latestUserDefinedLoader() 、または Sun.reflect.Reflection.getCallerClass() などのハックを使用している場合、APIの不足が原因のバグです。基本的に、Thread.getContextClassLoader()は、ObjectInputStream AP​​Iを設計した人がClassLoaderをパラメーターとして受け入れるのを忘れたためにのみ存在し、この間違いはJavaコミュニティに今日まで出没しています。

ただし、多くのJDKクラスでは、使用するクラスローダーを推測するために、いくつかのハックの1つを使用しています。一部のユーザーはContextClassLoader(共有スレッドプールで別のアプリを実行するとき、またはContextClassLoader nullを離れると失敗します)を使用し、一部はクラスの直接呼び出し元がそれ自体のとき失敗しますライブラリ)、システムクラスローダー(CLASSPATHのクラスのみを使用することが文書化されている限り)またはbootstrapクラスローダーを使用するものと、予測不可能な組み合わせを使用するもの上記のテクニックのうち、(混乱を招くだけです)。これは、歯の多くの泣きと歯ぎしりをもたらしました。

このようなAPIを使用する場合、最初に、クラスローダーをパラメーターとして受け入れるメソッドのオーバーロードを見つけてみてください。賢明なメソッドがない場合は、API呼び出しの前にContextClassLoaderを設定(および後でリセット)してみてください。

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);
}
85
yonran

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

class loader の仕組みを理解しましょう。

Javarevisitedのjavin paulブログから:

enter image description here

enter image description here

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

委任原則

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

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

  • Extension ClassLoaderクラスの読み込み要求をその親Bootstrapに委任し、失敗した場合は、jre/lib/extディレクトリまたはJava.ext.dirsシステムプロパティが指すその他のディレクトリからクラスを読み込みます

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

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

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

可視性の原則

可視性の原則に従って、Child ClassLoaderは、Parent ClassLoaderによってロードされたクラスを見ることができますしかし、その逆は真実ではありません。

一意性の原則

この原則によれば、Parentによってロードされたクラスは、Child ClassLoaderによって再びロードされるべきではありません。

32
Ravindra babu