回答:
各クラスは独自のクラスローダーを使用して他のクラスをロードします。その場合はClassA.class
参照がClassB.class
、その後ClassB
のクラスローダのクラスパス上にある必要がありClassA
、またはその親。
スレッドコンテキストクラスローダーは、現在のスレッドの現在のクラスローダーです。のクラスからオブジェクトを作成し、ClassLoaderC
が所有するスレッドに渡すことができますClassLoaderD
。この場合、オブジェクトはThread.currentThread().getContextClassLoader()
、独自のクラスローダーでは利用できないリソースをロードしたい場合、直接使用する必要があります。
ClassA.class
参照ClassB.class
」とはどういう意味ですか?
これは元の質問には回答しませんが、質問は高度にランク付けされ、どの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()
設計した人が誰でもObjectInputStream
、ClassLoader
がパラメータとしてを、この間違いが今日まで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);
}
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はデフォルトで呼び出し側の現在のクラスローダーを使用します
bootstrap
は、コンテキストクラスローダーとして設定されている親クラスローダーではなく、が設定されている子system
クラスパスクラスローダーですThread
。次に、JNDI
クラスはを使用Thread.currentThread().getContextClassLoader()
して、クラスパスで利用可能なJNDI実装クラスをロードします。
@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によってロードされたクラスを表示できますが、その逆は正しくありません。
この原則によると、親によってロードされたクラスは、子クラスローダーによって再度ロードされるべきではありません
ClassB
のクラスパス上になければならないのですか?それは可能ではありませんオーバーライドへのローダーが正常にロードできるように、場合でも、そのクラスパス上にないのですか?ClassA
ClassA
ClassA
loadClass()
ClassB
ClassB