ClassLoaderをより深く理解するには、まずどのクラスローダーが使用されているかを知る必要があります。名前が示すように、プログラムで使用するためにクラスファイルをJVMにロードするために使用されます。 Javaプログラムはクラスの定義を動的にロードできることを知っています。この動的読み込みメカニズムはClassLoaderを介して実装されているため、ClassLoaderの重要性を想像できます。
これを見た後、一部の友人は質問を考えるかもしれません。つまり、クラスローダーはクラスをJVMにロードするために使用されるため、クラスローダーはどのようにロードされますか? Javaクラスではありませんか?
そうです、Java言語で書かれていないが、JVM実装の一部であるクラスローダーがここにあります。このクラスローダーはブートストラップクラスローダー(スタートクラスローダー)です。このクラスローダーは、JVMが実行されているときにJavaコアAPIをロードし、ユーザー定義のクラスローダーを含むJavaプログラムの最も基本的なニーズを満たすためにロードされます。ここのいわゆるユーザー定義のクラスローダーは、Javaプログラムを通じて実装されたクラスローダーを指します。 1つはextclassloaderです。このクラスローダーは、Java Extension API、つまりクラスの /lib /ext、もう1つはAppClassLoaderです。このクラスローダーは、ユーザーのマシンのクラスパス設定ディレクトリにクラスをロードするために使用されます。通常、クラスローダーを指定せずに、プログラマーのカスタマイズされたクラスはクラスローダーによってロードされます。
プログラムを実行するとき、JVMは起動してBootstrapクラスローダーを実行します。 ClassLoaderはJava Core API(EstclassLoaderとAppClassLoaderもこの時点でロードされます)をロードし、ExtClassLoaderを呼び出して拡張機能APIをロードし、最後にAppClassLoaderはClassPathディレクトリで定義されたクラスをロードします。これは、プログラムの最も基本的な読み込みプロセスです。
上記は、クラスローダーの役割と最も基本的な負荷プロセスについて簡単に説明します。次に、クラスローダーのロード方法について説明します。ここでは、クラスの読み込みに親デリゲートモードの使用について話す必要があります。
各カスタムクラスローダーは、抽象クラスクラスローダーを継承する必要があり、各クラスローダーには親クラスローダーがあります。現在のクラスローダーの親を返すために使用される抽象クラスクラスローダーには、 getParent()メソッドがあることがわかります。この親は、継承されたクラスを参照するのではなく、クラスローダーをインスタンス化するときに指定されたクラスローダーを指すことに注意してください。この親がnullの場合、クラスローダーのデフォルトの親はブートストラップクラスローダーです。この親の使用は何ですか?
この状況を考慮することができます。 ClientDefClassLoaderをカスタマイズして、このカスタムクラスローダーを使用してJava.lang.Stringをロードして、このクラスローダーによって文字列がロードされますか?実際、Java.lang.Stringクラスは、ClientDefClassLoaderによってロードされるのではなく、Bootstrapクラスローダーによってロードされます。なぜこれが起こっているのですか?実際、これが親の委任モードの理由です。カスタムクラスローダーがクラスをロードする前に、まず父親のクラスローダーをロードするために委任するからです。父親のクラスローダーを正常にロードできない後にのみ、単独でロードされます。上記の例では、Java.lang.StringはJava Core APIに属するクラスであるため、ClientDefClassLoaderを使用してロードする場合、ClassLoaderはまず父親のクラスローダーをロードするために委任します。上記のように、クラスローダーの親がnullである場合、クラスローダーの親はブートストラップクラスローダーであるため、クラスローダーのトップレベルにはブートストラップクラスローダーがあるため、最終的にクラスローダーが存在するときにブートストラップに委任します。
クラスローダーのソースコードを見てみましょう。
保護された同期クラスロードクラス(文字列名、ブールレジュル)スローClassNotFoundException {//名前で指定されたクラスがロードされているかどうかを最初に確認します。 if(c == null){try {if(parent!= null){//親がnullでない場合、親のloadclassをload = parent.loadclass(name、false); } else {//親はnull、bootstrapclassloaderを呼び出してc = findbootstrapclass0(name); }} catch(classNotFoundException e){//ロードがまだ成功していない場合は、独自のFindClassを呼び出してC = FindClass(name); }} if(resolve){resolveclass(c); } return c; }上記のコードから、クラスをロードする一般的なプロセスは、以前に行った例と同じであることがわかります。カスタムクラスを実装したい場合は、FindClassメソッドを実装するだけです。
なぜこの親委任モデルを使用するのですか?
最初の理由は、これが繰り返しの荷重を避けることができることです。父親がクラスをロードしたとき、子供のクラスローダーが再びロードする必要はありません。
2番目の理由は、セキュリティ要因を考慮することです。このデリゲートモードを使用しない場合、Java Core APIの定義されたタイプをいつでも動的に置き換えることができ、非常に大きなセキュリティリスクをもたらすことを想像してみましょう。親はこの状況を回避できます。これは、文字列がスタートアップで既にロードされているため、ユーザー定義のクラスでカスタムクラスローダーをロードできないためです。
上記は、クラスローダーのロードメカニズムの簡単な紹介です。次に、ClassLoaderに関連する別のクラス、つまりクラスクラスを説明する必要があります。クラスローダーによってロードされた各クラスファイルは、最終的にプログラマーによってクラスクラスのインスタンスとして参照されます。クラスクラスを通常のクラスのテンプレートとして扱うことができます。 JVMは、このテンプレートに基づいて対応するインスタンスを生成し、最終的にプログラマーによって使用されます。
クラスクラスには、静的メソッドfornameがあることがわかります。この方法は、クラスローダーのLoadClassメソッドと同じです。クラスのロードに使用されますが、2つは異なる機能を持っています。
クラス<?> loadclass(string name)
クラス<?> loadclass(文字列名、ブール解像度)
上記の2つの方法宣言が表示されます。 2番目のメソッドの2番目のパラメーターは、クラスをロードするときにクラスを接続するかどうかを設定するために使用されます。真実の場合、接続されます。そうしないと、接続されません。
接続といえば、ここで説明する必要があります。 JVMでクラスをロードするときは、ロード、接続、初期化の3つのステップを実行する必要があります。読み込みとは、対応するクラスファイルを見つけ、JVMに読み取り、それを初期化する必要があることを説明する必要があります。最も重要なことは、接続について話すことです。
接続は3つのステップに分かれています。最初のステップは、クラスが仕様を満たしているかどうかを確認することです。 2番目のステップは準備です。クラス変数にメモリを割り当て、デフォルトの初期値を設定することです。 3番目のステップは説明です。このステップはオプションです。上記のLoadClassメソッドの2番目のパラメーターによれば、説明が必要かどうかが決定されます。いわゆる説明は、クラスのシンボル参照に基づいて対応するエンティティを見つけることで、シンボル参照を直接参照に置き換えるという本「詳細なJVM」の定義に基づいています。それは少し深いです、ハハ、私はここでそれを説明しません。もっと知りたい場合は、「詳細なJVM」を読んでください。ははは、ステップバイステップで説明し続けると、いつ完成するかわかりません。
2つのパラメーターを使用したLoadClassメソッドを見てみましょう。 Java APIドキュメントでは、この方法の定義が保護されています。つまり、メソッドが保護されており、ユーザーが実際に使用する方法は1つのパラメーターを持つ方法です。 1つのパラメーターのLoadClassメソッドは、実際に2つのパラメーターを使用してメソッドを呼び出し、2番目のパラメーターはデフォルトでfalseになります。したがって、ここでは、ロードクラスを介したローディングクラスが実際にロード時に説明されていないため、クラスは初期化されないことがわかります。クラスクラスのforNameメソッドは逆です。 fornameでロードすると、クラスが説明され、初期化されます。 ForNameには、クラスローダーを初期化して設定するかどうかを設定できる別のバージョンのメソッドもあります。ここではこれ以上話しません。
これら2つの負荷方法の説明が十分に明確であるのだろうか。ここで例を挙げましょう。たとえば、 JDBCドライバーをロードするとき、JDBCドライバーをロードするときにクラスローダーのLoadClassメソッドの代わりにForNameを使用しますか? JDBCドライバーはDrivermanagerを介しており、Drivermanagerに登録する必要があることがわかっています。ドライバークラスが初期化されていない場合、Drivermanagerに登録できません。したがって、loadclassの代わりにfornameを使用する必要があります。
ClassLoaderを介して、クラスローダーをカスタマイズして、ネットワークからのロード、他の形式のファイルからのロードなど、必要な読み込み方法をカスタマイズできます。実際、クラスローダーなどのいくつかの実装など、クラスローダーで言及されていないことがたくさんあります。
この記事を通して、編集者は、誰もがクラスローダーのメカニズムをある程度理解することを望んでいます。ご支援ありがとうございます!