目次 | 前の項目 | 次の項目 | Java セキュリティアーキテクチャ |
動的なクラスローディングは Java 仮想マシンの重要な機能で、Java プラットフォーム上でソフトウェアコンポーネントを実行時にインストール可能にします。この機能には、独特の特長がいくつかあります。まず、要求があってもできる限り直前までクラスがロードされない、遅延ローディングという特徴があります。また、動的なクラスローディングは、リンクタイムチェックを追加することによって、Java 仮想マシンの型の安全性を維持します。リンクタイムチェックは、特定のランタイムチェックと置き換わり、1 回だけ実行されます。さらに、プログラマは、特定のクラスのロード元となるリモートの場所を指定したり、クラスに適切なセキュリティ属性を割り当てたりするような、独自のクラスローダを定義できます。また、クラスローダを使って、さまざまなソフトウェアコンポーネントに個別の名前空間を提供できます。たとえば、ブラウザは、異なる Web ページのアプレットを別々のクラスローダを使ってロードできるため、これらのアプレットクラス間である程度の遮断レベルが維持されます。実際、これらのアプレットに同じ名前のクラスが含まれていてもかまいません。それらは、Java 仮想マシンによって異なる種類として扱われます。クラスローディングの機構は、Java プログラミング言語の動的性質の中心となっているだけではありません。クラスファイルの検索と取得、セキュリティポリシーの参照、および適切なアクセス権を持たせたクラスオブジェクトの定義は、クラスローダが行うため、この機構は、セキュリティの提供にも中心的な役割を果たしています。
1 つの Java 仮想マシンに、クラスローダオブジェクトの複数のインスタンスが存在する可能性があるため、クラスをロードするときに、使用するクラスローダをどのように決定するかは重要な問題です。Java 2 SDK では、異なる属性を持った複数のクラスローダのクラスが導入されたため、どの種類のクラスローダを使用するのかも大きな問題です。クラスローダのクラス階層のルートは、java.lang.ClassLoader という抽象クラスで、JDK 1.0 で定義されて以来拡張されてきました。Java 2 SDK v 1.2 で導入された java.security.SecureClassLoader クラスは、抽象 ClassLoader クラスのサブクラスおよび固定実装です。java.net.URLClassLoader クラスは、SecureClassLoader のサブクラスです。
Appletviewer というユーティリティプログラムは、private クラスである sun.applet.AppletClassLoader を使ってアプレットをロードします。JDK 1.0 では、AppletClassLoader は ClassLoader のサブクラスおよび固定実装です。Java 2 SDK では、AppletClassLoader は URLClassLoader のサブクラスです。
クラスローダのカスタムクラスを作成する場合は、そのカスタムクラスローダ特有の必要に応じて、上記の任意のクラスローダクラスのサブクラスにすることも可能です。AppletClassLoader は、sun.* package 内で定義された private クラスで、サポートされておらず変更される可能性もあるので、AppletClassLoader のサブクラス化は行わないでください。
すべてのクラスは、クラスローダによってロードされます。クラスローダ自体もクラスで、別のクラスローダによってロードされる必要があるため、当然、最初のクラスローダはどこからロードされるかという「鶏と卵の問題」のような問題が考えられます。このため、クラスのローディングプロセスをブートストラップする「初期」クラスローダがあります。初期クラスローダは、一般的には C などのネイティブ言語で記述されており、Java コンテキスト内には現れません。初期クラスローダは、ローカルのファイルシステムからプラットフォームに依存した方法でクラスをロードする場合がよくあります。クラスには、java.* package 内で定義されたクラスのように、Java 仮想マシンと実行システムが正しく機能するために不可欠なものがあります。そのようなクラスはよく、基底クラスと呼ばれます。歴史的な理由から、このようなすべてのクラスには、NULL のクラスローダが存在します。おそらく、この NULL クラスローダが、初期クラスローダの存在を示す唯一のしるしです。実際、NULL クラスローダを、単に初期クラスローダとしてみなす方が簡単です。
1 つの Java アプリケーション環境にすべてのクラスがあれば、クラスのローディング関係を反映するクラスローディングのツリーを簡単に形成できます。クラスローダでないクラスは、すべて葉ノードです。各クラスの親ノードはそのクラスのクラスローダで、NULL クラスローダはルートクラスです。クラスローダが自分の祖先のクラスローダをロードするという循環はあり得ないため、この構造はツリー構造になります。
クラスローダにクラスのロードが要求されると、そのクラスローダ自体がクラスをロードする場合と、そのクラスが別のクラスローダにロードを行うように要求する場合があります。つまり、1 つ目のクラスローダは、2 つ目のクラスローダに委譲することができます。委譲関係は、どのクラスローダがほかのどのクラスローダをロードするかには関係ないという点では仮想的です。しかしむしろ委譲関係は、クラスローダオブジェクトの作成時に、親子関係として形成されます。ただし、システムクラスローダは、すべてのクラスローダの委譲ルートの原型です。委譲関係に循環がないように注意してください。循環があると、委譲プロセスが無限ループに入ることがあります。
Java 2 SDK ClassLoader メソッドのデフォルト実装では、クラスをロードする際、次の順序でクラスが検索されます。
最初のステップでは、クラスローダのローカルキャッシュ (または機能的にそれと同等な、グローバルキャッシュなど) で、ロードされたクラスがターゲットクラスと一致するかどうかを調べます。最後のステップでは、クラスの検索機構のカスタマイズ機能が提供されています。このため、カスタムクラスローダで、このメソッドをオーバーライドして、クラスの検索方法を指定できます。たとえば、アプレットのクラスローダでこのメソッドをオーバーライドして、アプレットのホストに戻ってクラスファイルを検索し、ネットワーク越しにそのクラスファイルをロードできます。これらのステップのどこかでクラスが検索されると、それが返されます。上のステップを使ってクラスが見つからない場合は、ClassNotFound 例外がスローされます。
型の安全性のため、同じクラスが同じクラスローダによって 2 回以上ロードされないように注意してください。クラスがすでにロードされたクラスでない場合、現在のクラスローダは、タスクを親クラスローダに委譲しようとします。このプロセスは、再帰的に実行されることもあります。このため、適切なクラスローダが使われることが保証されます。たとえば、システムクラスを検索する場合、システムクラスローダにたどり着くまで委譲プロセスが繰り返されます。
ここまで移譲アルゴリズムを見てきました。では、クラス名を指定されてそのクラスをロードするとき、どのクラスローダから使い始めればよいでしょうか。クラスローダを特定する規則は、次のとおりです。
URLClassLoader のインスタンスおよび AppletClassLoader のインスタンスの使用についての規則には例外があり、特定のシステム環境によっては、規則が変わる場合があります。たとえば、同じ Web ページから複数のアプレットクラスをロードする場合、Web ブラウザが既存の AppletClassLoader を繰り返し使用する場合もあります。クラスローダの能力によって、クラスローダのインスタンスを作成できるプログラムの種類は大きく制限されます。一方、URL の位置を指定し、そこからクラスをロードするための便利な機構をアプリケーションまたはアプレットに備えることは望ましい方法です。どのプログラムでも URLClassLoader クラスのインスタンスを作成できるようにするための静的メソッドが提供されています。ただし、その他の種類のクラスローダの場合には、このことは当てはまりません。