サーチ…


備考

クラスローダは、アプリケーションが使用するクラスの場所とロードを仲介することを主目的とするクラスです。クラスローダーは、 リソースを見つけてロードすることもできます。

標準のクラスローダークラスは、クラスとリソースをファイルシステム内のディレクトリとJARファイルとZIPファイルからロードできます。また、リモートサーバーからJARファイルとZIPファイルをダウンロードしてキャッシュすることもできます。

クラスローダーは通常はチェーンされているため、JVMはアプリケーション提供のソースよりも標準クラス・ライブラリーのクラスをロードしようとします。カスタムクラスローダーを使用すると、プログラマーはこれを変更できます。また、バイトコードファイルの解読やバイトコードの変更なども行うことができます。

クラスローダのインスタンス化と使用

この基本的な例では、アプリケーションがクラスローダをインスタンス化してクラスを動的にロードする方法を示します。

URL[] urls = new URL[] {new URL("file:/home/me/extras.jar")};
Classloader loader = new URLClassLoader(urls);
Class<?> myObjectClass = loader.findClass("com.example.MyObject");

この例で作成されたクラスローダーは、デフォルトのクラスローダーを親として持ち、 "extra.jar"を見る前に親クラスローダーのクラスを見つけようとします。要求されたクラスが既にロードされている場合、 findClassコールは以前にロードされたクラスへの参照を返します。

findClass呼び出しは、さまざまな方法で失敗する可能性があります。最も一般的なのは次のとおりです。

  • 指定されたクラスが見つからない場合、 ClassNotFoundExceptionをスローして呼び出しClassNotFoundException
  • 指定されたクラスが見つからない他のクラスに依存する場合、その呼び出しはNoClassDefFoundErrorをスローします。

カスタムクラスローダーの実装

すべてのカスタムローダーは、 java.lang.ClassLoaderクラスを直接的または間接的に拡張する必要がありjava.lang.ClassLoader 。主な拡張ポイントは、次のメソッドです。

  • findClass(String) - クラスローダーがクラスローディングの標準委譲モデルに従っている場合は、このメソッドをオーバーロードします。
  • loadClass(String, boolean) - 代替の委譲モデルを実装するためにこのメソッドをオーバーロードします。
  • findResourcefindResources - これらのメソッドをオーバーロードして、リソースの読み込みをカスタマイズします。

実際にバイト配列からクラスをロードする役割を担うdefineClassメソッドは、オーバーロードを防ぐためにfinalものです。 defineClassを呼び出す前にカスタムビヘイビアを実行する必要がありdefineClass

バイト配列から特定のクラスをロードする簡単な例を次に示します。

public class ByteArrayClassLoader extends ClassLoader {
    private String classname;
    private byte[] classfile;

    public ByteArrayClassLoader(String classname, byte[] classfile) {
        this.classname = classname;
        this.classfile = classfile.clone();
    }

    @Override
    protected Class findClass(String classname) throws ClassNotFoundException {
        if (classname.equals(this.classname)) {
            return defineClass(classname, classfile, 0, classfile.length);
        } else {
            throw new ClassNotFoundException(classname);
        }
    }
}

findClassメソッドをオーバーライドしただけなので、このカスタムクラスローダーはloadClassが呼び出されたときに次のように動作します。

  1. クラスローダのloadClassメソッドはfindLoadedClassを呼び出して、この名前のクラスがこのクラスローダによってすでにロードされているかどうかを確認します。成功した場合、結果のClassオブジェクトはリクエスタに返されます。
  2. 次に、 loadClassメソッドは、 loadClass呼び出しを呼び出すことによって親クラスローダーに委譲します。親が要求を処理することができる場合、親オブジェクトはClassオブジェクトを返し、要求者に返されます。
  3. 親クラスローダーがクラスをロードできない場合、 findClassはオーバーライドfindClassメソッドを呼び出し、ロードされるクラスの名前を渡します。
  4. 要求された名前がthis.classnameと一致する場合は、 defineClassを呼び出してthis.classfileバイト配列から実際のクラスをロードします。結果のClassオブジェクトが返されます。
  5. 名前が一致しない場合、 ClassNotFoundExceptionをスローしClassNotFoundException

外部.classファイルのロード

クラスをロードするには、最初にクラスを定義する必要があります。クラスはClassLoaderによって定義されます。 1つの問題がありますが、OracleはClassLoaderのコードをこの機能を利用できるように書いていませんでした。クラスを定義するには、 ClassLoaderプライベートメソッドであるdefineClass()という名前のメソッドにアクセスする必要があります。

これにアクセスするには、新しいクラスByteClassLoader作成し、それをClassLoader拡張します。クラスをClassLoader拡張したので、 ClassLoaderのプライベートメソッドにアクセスできます。 defineClass()使用できるようにするために、privateのdefineClass()メソッドのミラーのように動作する新しいメソッドを作成します。私たちは、クラス名、必要になりますプライベートメソッドを呼び出すには、 name 、クラスバイト、 classBytesになります最初のバイトのオフセットを、 0のでclassBytesのデータは、から始まりclassBytes[0]そして最後のバイトのオフセットたのだろう、 classBytes.lenghtはデータのサイズを表すため、最後のオフセットになります。

public class ByteClassLoader extends ClassLoader {

    public Class<?> defineClass(String name, byte[] classBytes) {
        return defineClass(name, classBytes, 0, classBytes.length);
    }

}

さて、私たちはpublic defineClass()メソッドを持っていdefineClass() 。これは、クラスの名前とクラスのバイトを引数として渡すことで呼び出すことができます。

stackoverflowパッケージにMyClassという名前のクラスがあるとしましょう...

メソッドを呼び出すにはクラスバイトが必要です。クラスのPath表すPathオブジェクトをPaths.get()メソッドを使用してPaths.get() 、バイナリクラスのパスを引数として渡します。これで、クラスバイトをFiles.readAllBytes(path)で取得できます。そこで、 ByteClassLoaderインスタンスを作成し、作成したメソッドdefineClass()を使用します。私たちはすでにクラスバイトを持っていますが、メソッドを呼び出すためにはクラス名が必要です。このクラス名はクラス名(この場合はstackoverflow.MyClass )のパッケージ名(ドット)で与えられます。

Path path = Paths.get("MyClass.class");

ByteClassLoader loader = new ByteClassLoader();
loader.defineClass("stackoverflow.MyClass", Files.readAllBytes(path);

注意defineClass()メソッドはClass<?>オブジェクトを返します。あなたが望むなら、それを保存することができます。

クラスをロードするには、 loadClass()を呼び出してクラス名を渡します。このメソッドはClassNotFoundExceptionをスローして、try cathブロックを使用する必要があります

try{
    loader.loadClass("stackoverflow.MyClass");
} catch(ClassNotFoundException e){
    e.printStackTrace();
}


Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow