クラスの読み込みプロセス
クラスローダーの主な仕事は、クラスファイルをJVMにロードすることです。下の図に示すように、プロセスは3つのステップに分かれています。
1。読み込み:ロードするクラスファイルを見つけ、バイトストリームをJVMにロードします。
2。リンク:最も基本的なメモリ構造を割り当ててロードして、プロパティ、メソッド、参照クラスなどの情報を保存します。この段階では、クラスはまだ利用できません。
(1)検証:フォーマットやセキュリティなどのロードされたバイトストリームを確認します。
(2)メモリの割り当て:このクラスがそのプロパティ、方法、参照クラスを表すようにメモリスペースを準備します。
(3)分析:親クラス、実装されたインターフェイスなど、このクラスが参照する他のクラスをロードします。
3。初期化:クラス変数に値を割り当てます。
クラスローダーレベル
下の図の点線は、JDKが提供するいくつかの重要なクラスローダーであり、詳細な説明は次のとおりです。
(1)ブートストラップクラスローダー:メイン関数を含むクラスを起動するとき、java_home/libディレクトリまたは-xbootclasspath指定ディレクトリにJARパッケージをロードします。
(2)拡張クラスローダー:java_home/lib/extディレクトリまたは-djava.ext.dirs指定ディレクトリにJARパッケージをロードします。
(3)システムクラスローダー:指定されたディレクトリにClassPathまたは-djava.class.pathクラスまたはJARパッケージをロードします。
あなたが知る必要があるのは:
1。ブートストラップクラスローダーに加えて、他のクラスローダーはjava.lang.classloaderクラスのサブクラスです。
2。ブートストラップクラスローダーはJavaに実装されていません。パーソナライズされたクラスローダーを使用しない場合、java.lang.string.class.getClassLoader()はnullであり、拡張クラスローダーの親ローダーもnullです。
3.クラスローダーを取得するいくつかの方法:
(1)ブートストラップクラスローダーの取得:ブートストラップクラスローダーを取得しようとする場合、得られるものはnullでなければなりません。次のように確認できます。Java.lang.String.Class.getClassLoader()など、Rt.JarパッケージのクラスオブジェクトのGetClassLoaderメソッドを使用して、拡張クラスローダーを取得または取得し、GetParentメソッドを呼び出して取得します。
(2)拡張クラスローダーを取得する:JAVA_HOME/LIB/EXTディレクトリの下にあるJARパッケージのクラスオブジェクトのGetClassLoaderメソッドを使用するか、最初にシステムクラスローダーを取得してから、GetParentメソッドを介して取得します。
(3)システムクラスローダーを取得する:メイン関数を含むクラスオブジェクトのgetClassLoaderメソッドを呼び出すか、swerch.currentthread()。getContextClassLoader()を呼び出すか、メイン関数内のclassloader.getSystemClassLoader()を呼び出します。
(4)ユーザー定義のクラスローダーを取得する:クラスオブジェクトのgetClassLoaderメソッドを呼び出すか、swerch.currentthread()。getContextClassLoader();
クラスローダーの運用原則
1。エージェントの原則
2。可視性の原則
3。独自性の原則
4.プロキシ原則プロキシ原理は、クラスのロード時に親ローダープロキシをロードするように依頼するクラスローダーを指します。また、親ローダーは、下の図に示すように、親ローダープロキシをロードするように要求します。
なぜプロキシモードを使用するのですか?まず第一に、これによりクラスの重複負荷を減らすことができます。 (他に何か理由はありますか?)
誤解する場所:
一般的に、クラスローダーのプロキシ順序は最初に親であると考えられています。つまり、次のとおりです。
1.クラスをロードするとき、クラスローダーは最初にクラスをロードしたかどうかを確認します。ロードされている場合、戻ります。それ以外の場合は、親ローダーにプロキシを依頼します。
2.親ローダーは、ブートストラップクラスローダーまで1の動作を繰り返します。
3.ブートストラップクラスローダーがクラスをロードしない場合、ロードしようとし、成功した場合は戻ります。失敗した場合、classNotFoundExceptionがスローされ、子ローダーによってロードされます。
4.サブクラスローダーは例外をキャッチし、ロードしようとします。それが成功した場合、それは戻ります。失敗した場合、ロードされたサブクラスローダーが開始されるまでClassNotFoundExceptionをスローします。
この理解は、ブートストラップクラスローダー、拡張クラスローダー、システムクラスローダーなどのローダーに適していますが、一部のパーソナライズされたローダーはそうではありません。たとえば、IBM Web Sphere Portal Serverによって実装された一部のクラスローダーは最後の親です。これは、最初にロードしようとするチャイルドローダーです。負荷が失敗した場合、親ローダーに尋ねられます。その理由は、LOG4Jの特定のバージョンがすべてのアプリケーションで使用されると予想される場合、was_homeライブラリに置くと、開始時にロードされます。アプリケーションがLOG4Jの別のバージョンを使用したい場合、LOG4Jのクラスがすでに親ローダーにロードされているため、最初に親を使用する場合は達成できません。ただし、親が最後に使用される場合、アプリケーションのロードを担当するクラスローダーは、log4jの別のバージョンのロードを優先します。
可視性の原則
下の図に示すように、各クラスのクラスローダーへの可視性は異なります。
知識を拡張するために、OSGIはこの機能を利用します。各バンドルは個別のクラスローダーでロードされるため、各クラスローダーは特定のクラスのバージョンをロードできるため、システム全体がクラスの複数のバージョンを使用できます。
独自性の原則
各クラスは、最大でローダーにロードされます。
拡張知識1:正確には、シングルトンパターンは、一連のクラスローダーにクラスの1つのコピーのみがシングルトンオブジェクトを指します。
拡張知識2:複数のクラスローダーでクラスをロードできます。各クラスオブジェクトは独自の名前空間にあります。クラスオブジェクトを比較する場合、またはインスタンスを変換するタイプを使用すると、次のようなそれぞれの名前空間を同時に比較します。
Klassクラスは、クラスオブジェクトがKlassaであると仮定して、ClassLoaderaによってロードされます。クラスオブジェクトがklassbであると仮定して、classloaderbによってロードされ、KlassaはKlassbと等しくありません。同時に、クラスのインスタンスがKlassbにキャストされると、ClassCastExceptionの例外がスローされます。
なぜパーソナライズされたクラスローダーが必要なのですか?パーソナライズされたクラスローダーは、Java言語に多くの柔軟性を追加します。主な用途は次のとおりです。
1。クラスは、ネットワーク上、データベース、または即座にコンパイルされたソースファイルなど、複数の場所からロードできます。
2。パーソナライズ後、クラスローダーは、実行時に原則としてクラスファイルの特定のバージョンをロードできます。
3。パーソナライズ後、クラスローダーはいくつかのクラスを動的にロードできます。
4.パーソナライズ後、クラスローダーはクラスをロードする前にクラスを復号化および解凍できます。
クラスの暗黙的かつ明示的なロード
暗黙的な負荷:クラスが参照、継承、またはインスタンス化されると、暗黙的にロードされます。負荷が失敗した場合、Noclassdeffounderrorがスローされます。
明示的な負荷:次の方法を使用します。負荷が故障した場合、classNotFoundExceptionがスローされます。
cl.LoadClass()、CLはクラスローダーのインスタンスです。
class.forname()、現在のクラスのクラスローダーを使用してロードします。
クラスの静的ブロックの実行に次のクラスがある場合:
パッケージcn.fengd; public class dummy {static {system.out.println( "hi"); }}別のテストクラスを作成します:
パッケージcn.fengd; public class classloadertest {public static void main(string [] args)throws instantiationexception、Exception {try { / * *さまざまなロード方法。 */ class c = classloadertest.class.getClassLoader()。loadClass( "cn.fengd.dummy"); } catch(例外e){// todo auto-enerated catch block e.printstacktrace(); }}}実行後はどれくらい効果的ですか?
class.forname( "cn.fengd.dummy")に置き換えられた場合はどうなりますか?または新しいdummy()?
こんにちは出力が出力されます。
よくある質問:
1。指定されたタイプは、異なるクラスローダーによってロードされていますが、まだ同じタイプですか?
Javaでは、クラスが完全に資格のあるクラス名を識別子として使用します。ここで言及されている正確な一致クラス名には、パッケージ名とクラス名が含まれています。ただし、JVMでは、クラスがフルネームとロードクラスクラスローダーのインスタンスを一意の識別子として使用します。異なるクラスローダーでロードされたクラスは、異なる名前空間に配置されます。 2つのカスタムクラスローダーを使用して、カスタムタイプ(カスタムタイプのバイトコードをシステムパスまたは拡張パスに配置しないことに注意してください。そうしないと、最初にシステムクラスローダーまたは拡張クラスローダーによってロードされます)。これは、2つのカスタムクラスローダーを作成して同じカスタムタイプをロードし、判断を下すことで実行できます。同時に、Javaの負荷をテストできます。*タイプ、テスト結果を比較してテストできます。
2。コードのclass.forname(string name)メソッドを直接呼び出します。どのクラスローダーがクラスの読み込み動作をトリガーしますか?
class.forname(string name)は、デフォルトでクラスをロードするためにクラスを呼び出すクラスローダーを使用します。対応するJDKコードを直接分析しましょう。
//java.lang.class.java publicStatic class <? getCallerClassLoader(){//呼び出しクラス(Caller)クラスCaller = Reflection.GetCallerClass(3)を取得します。 // VMがそれを要求している場合、これはnullになります(caller == null){returnnull; } // java.lang.classのローカルメソッドを呼び出して、呼び出しクラス(caller)をロードするクラスローダーを取得しますcaller.getClassLoader0();} 3.カスタムクラスローダーを書くとき、親ローダーが設定されていない場合、親ローダーは何ですか?
親クラスローダーが指定されていない場合、システムクラスローダーはデフォルトで使用されます。一部の人々は理解できないかもしれませんが、JDKの対応するコード実装を見てみましょう。誰もが知っているように、java.lang.classloader abstractクラスから直接または間接的に継承するカスタムクラスローダーを書きます。パラメーターのない対応するデフォルトコンストラクターは、次のように実装されます。
// java.lang.classloader.javaprotected classloaderからの抜粋if(security!= null){security.checkcreateclassloader(); } this.parent = getsystemClassLoader();初期化= true;}getsystemclassloader()メソッドの対応する実装を見てみましょう。
privatestaticynchronizedVoid initsystemClassLoader(){// ... sun.misc.launcher l = sun.misc.launcher.getLauncher(); scl = l.getClassLoader(); // ...}簡単なテストコードを作成してテストできます。
System.out.println(sun.misc.launcher.getLauncher()。getClassLoader());
このマシンの対応する出力は次のとおりです。
sun.misc.launcher$appclassloader@197d257
したがって、カスタムクラスローダーが親クラスローダーを指定していない場合、デフォルトの親クラスローダーがシステムクラスローダーであると信じることができます。同時に、次の結論を導き出すことができます。
インスタントユーザー定義のクラスローダーは、親クラスローダーを指定していません。次の3つの場所のクラスもロードできます。
(1)<java_runtime_home>/libに基づくクラス
(2)システム変数java.ext.dirによって指定された場所のクラス
(3)現在のプロジェクトクラスパスの下またはシステム変数java.class.pathで指定された場所にあるクラス
4.カスタムクラスローダーを書くとき、親クラスローダーがnullにされることを余儀なくされた場合はどうなりますか?カスタムクラスローダーが指定されたクラスをロードできない場合、間違いなく失敗しますか?
JVM仕様は、ユーザー定義のクラスローダーが親クラスローダーを強制する場合、スタートアップクラスローダーが現在のユーザー定義クラスローダーの親クラスローダーに自動的に設定されることを規定しています(この問題は以前に分析されたことがあります)。同時に、次の結論を導き出すことができます。
インスタントユーザー定義のクラスローダーが親クラスローダーを指定していない場合、クラスを<java_runtime_home>/libの下にロードすることもできますが、<java_runtime_home>/lib/extディレクトリの下のクラスはこの時点でロードできません。
注:質問3と4の推定結論は、java.lang.classloader.loadclass(…)のデフォルトの委任ロジックを継続するユーザー定義のクラスローダー自体に基づいています。ユーザーがこのデフォルトの委任ロジックを変更した場合、上記の推測された結論が有効でない場合があります。詳細については、質問5を参照してください。
5.カスタムクラスローダーを書くときの一般的な注意点は何ですか?
(1)一般的に、既存のLoadclass(…)メソッドの委任ロジックをオーバーライドしないようにしてください。一般的に、JDK 1.2の前にバージョンで行われ、そうすることでシステムのデフォルトのクラスローダーが適切に機能しない可能性が非常に高いことがわかります。 JVM仕様とJDKドキュメント(1.2以降のバージョン以降)では、ユーザーがLoadClass(…)メソッドを上書きすることをお勧めしません。対照的に、開発者は、カスタムクラスローダーを開発するときにFindClass(…)ロジックを上書きすることを明確に思い出させます。問題を確認するために例を示します。
//ユーザーカスタムクラスローダー誤ったクラスローダー.java(loadclass logicを上書き)publicClassWrongClassLoadErxEnds classLoader {public class <?> loadclass(string name)throws classNotFoundException {returnthis.findclass(name); }保護されたクラス<?以前の分析を通じて、ユーザー定義のクラスローダー(誤ったクラスローダー)のデフォルトがすでにわかっています
認識されているクラスローダーはシステムクラスローダーですが、4種類の問題の結論は現在有効ではありません。誰もができます
テストしてください、今<java_runtime_home>/lib、<java_runtime_home>/lib/ext and
プログラムクラスパスのクラスはロードできません。
質問5テストコード1
publicClass誤ったClassLoaderTEST {publicStaticVoid main(string [] args){try {誤解Classloader loader = new MurtingClassLoader(); class classloaded = loader.loadclass( "beans.account"); System.out.println(classloaded.getName()); system.out.println(classloaded.getClassLoader()); } catch(Exception e){e.printstacktrace(); }}}(注:D:「クラス「豆」account.classが物理的に存在する)
出力結果:
java.io.filenotfoundexception:d: "classes" java "lang" object.class(システムは指定されたパスを見つけることができません。)java.io.fileinputstream.open(ネイティブ方法)java.io.fileinputStream。間違ったclassloader.loadclass(誤ったclassloader.java:29)でjava.lang.classloader.loasclassinternal(classloader.java:319)at java.lang.classloader.defineclass1(ネイティブ方法)のjava.lang.lang.lassoloader.defineclass(classloadlodyclass) java.lang.classloader.defineclass(classloader.java:400)at MurtingClassLoader.findClass(MurtingClassLoader.java:43)at MurtingClassLoader.LoadClass(誤解Classloader.java:29)at MurryClassLoadert.Java:27 "Main" Main "Main" Mainpclasstest.java:27) java.lang.noclassdeffounderror:java.lang.classloaser.defineclass1のjava/lang/object at java.lang.classloader.defineclass(classloader.java:620)のjava.lang.lang.lang.classoloader.defineclass(classloadloader.defineclass(classloader.java:620)) MurtingClassLoader.FindClass(誤ったClassLoader.java:43)infurtingClassLoader.LoadClass(MurtingClassLoader.Java:29)at MurtingClassLoadertEST.Main(MurtingClassLoadertest.java:27)
これは、ロードされるタイプのsupertype java.lang.objectでさえロードできないことを意味します。ロードクラス(…)の上書きによって引き起こされる論理エラーは明らかに比較的単純であり、実際に引き起こされる論理エラーははるかに複雑になる可能性があります。
質問5テスト2
//ユーザーカスタムクラスローダー誤ったクラスローダー.java(ロードクラスロジックを上書きしないでください)publicClasswrongClassLoadErxEdds classloader {protected class <?カスタムクラスローダーコードを変更した後、間違ったClassloader.java、テストコードを実行すると、出力の結果は次のとおりです。
beans.accountwrongclassloader@1c78e57
これは、Beans.Accountが正常にロードされ、カスタムクラスローダーの間違ったクラスローダーによってロードされることを示しています。
この理由を説明する必要はないと思います。分析できるはずです。
(2)上記の質問4および質問5の分析を通じて、親クラスローダーを正しくセットアップします。個人的には、これがユーザークラスローダーをカスタマイズするときに最も重要なポイントだと思いますが、多くの場合、無視されるか、簡単に持ち込まれます。以前のJDKコードを基礎として分析することで、誰もが今どんな例でも説明できると思います。
(3)FindClass(String)メソッドの論理的正しさを確保し、定義するクラスローダーが完了するロードタスクを正確に理解し、対応するバイトコードコンテンツを最大限に取得できることを確認してください。
6.システムクラスローダーがロードできるパスを判断する方法は?
まず、ClassLoader.getSystemClassLoader()またはその他のメソッドを直接呼び出して、システムクラスローダー(システムクラスローダーと拡張クラスローダー自体がURLClassLoaderから派生したもの)を取得できます。
次に、システム属性java.class.pathを取得することにより、現在のクラスパスのエントリ情報を直接表示できます。 System.getProperty( "java.class.path")
7.標準の拡張クラスローダーがロードできるパスを判断する方法は?
方法1:
try {url [] exturls =((urlclassloader)classloader.getSystemClassLoader()。getParent())。getUrls(); for(int i = 0; i <exturls.length; i ++){system.out.println(exturls [i]); }} catch(例外e){//…}このマシンの対応する出力は次のとおりです。
file:/d:/demo/jdk1.5.09/jre/lib/ext/dnsns.jarfile:/d:/demo/jdk1.5.09/jre/lib/ext/localedata.jarfile :/d:/demo/jdk1.5.09/jre/lib/ext/sunjce_provider.jarfile:/d:/demo/jdk1.5.0_09/jre/lib/ext/sunpkcs11.jar