クラスローダー (ClassLoader) は、クラスのバイトコードを Java 仮想マシンにロードするために使用されます。一般に、Java 仮想マシンは次のように Java クラスを使用します。 Java ソース ファイルは、Javac を通過した後、Java バイトコード ファイル (.class ファイル) に変換されます。クラス ローダーは、Java バイトコードを読み取り、それを java.lang.Class クラスのインスタンスに変換する役割を果たします。このような各インスタンスは Java クラスを表します。たとえば、Java バイト コードはツールを通じて動的に生成されたり、ネットワークを通じてダウンロードされたりする場合があります。
クラスローダーはクラスのロードアクションを実装するためにのみ使用されますが、Javaプログラムにおけるその役割はクラスロード段階に限定されるわけではありません。どのクラスでも、それをロードするクラスローダーとクラス自体は、Java 仮想世界でその一意性を確立する必要があります。より簡単に言うと、2 つのクラスが「等しい」かどうかを比較することは、2 つのクラスが同じクラス ローダーによってロードされた場合にのみ意味を持ちます。そうでない場合は、2 つのクラスが同じクラス ファイルからのものであっても、ロードされている限り意味があります。クラスローダーが異なる場合、2 つのクラスは等しくありません。ここでいう「等価性」には、equalメソッド、isAssignableFrom()、isInstance()メソッド、およびクラスを表すClassオブジェクトのinstanceキーワードで返される結果が含まれます。
主に、ブートストラップクラスローダー、拡張クラスローダー、アプリケーションクラスローダー、ユーザー定義クラスローダーに分かれています。
ブートストラップ ClassLoader:
このクラス ローダーは C++ 言語で実装されており、ClassLoader のサブクラスではありません。主に、JAVA_HOME/jre/lib/rt.jar に保存されているすべてのクラス ファイル、または -Xbootclasspath パラメーターで指定されたパスにある rt.jar という名前のファイルをロードします。
拡張クラスローダー:
このローダーは、AVA_HOME / lib / ext ディレクトリのロードを担当する sun.misc.Launcher$ExtClassLoader によって実装されるか、または java.ext.dirs システム変数によって実装されます。指定されたディレクトリ内のすべてのライブラリパス。
アプリケーション クラスローダー:
このローダーは、クラスパスに対応する jar とディレクトリをロードする役割を担う sun.misc.Launcher$AppClassLoader によって実装されます。通常、これはプログラムのデフォルトのクラスローダーです。
カスタム クラス ローダー (ユーザー定義のクラス ローダー):
開発者は、ClassLoader 抽象クラスを継承し、独自に開発した ClassLoader に基づいて、クラスパス上のクラス以外のクラスをロードするために使用できます (インターネットからダウンロードした jar やバイナリ バイトコードなど)、クラス ファイルをロードする前に暗号化などの小さな操作を実行することもできます。
上図に示すクラスローダー間のこの階層関係を、クラスローダーの親委任モデルと呼びます。親委任モデルでは、トップレベルの起動クラス ローダーに加えて、他のすべてのクラス ローダーにも独自の親クラス ローダーが必要です。ここでのクラス ローダー間の親子関係は、通常、継承を通じて実装されず、代わりに、組み合わせ関係が親ローダーのコードを再利用するために使用されます。
双亲委托的工作过程:如果一个类加载器收到了一个类加载请求,它首先不会自己去加载这个类,而是把这个请求委托给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成加载请求(它管理的范围之中没有这个类)时,子加载器才会尝试着自己去加载。
使用双亲委托模型来组织类加载器之间的关系,有一个显而易见的好处就是Java类随着它的类加载器一起具备了一种带有优先级的层次关系,例如java.lang.Object存放在rt.jar之中,无论那个类加载器要加载这个类,最终都是委托给启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是同一个类,相反,如果没有双亲委托模型,由各个类加载器去完成的话,如果用户自己写一个名为java.lang.Object的类,并放在classpath中,应用程序中可能会出现多个不同的Object类,java类型体系中最基本安全行为也就无法保证。
java.lang.ClassLoader 类提供的几个关键方法:
loadClass: 此方法负责加载指定名字的类,首先会从已加载的类中去寻找,如果没有找到;从parent ClassLoader[ExtClassLoader]中加载;如果没有加载到,则从Bootstrap ClassLoader中尝试加载(findBootstrapClassOrNull方法), 如果还是加载失败,则抛出异常ClassNotFoundException, 在调用自己的findClass方法进行加载。如果要改变类的加载顺序可以覆盖此方法;如果加载顺序相同,则可以通过覆盖findClass方法来做特殊处理,例如:解密,固定路径寻找等。当通过整个寻找类的过程仍然未获取Class对象,则抛出ClassNotFoundException异常。
如果类需要resolve,在调用resolveClass进行链接。
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { // First, check if the class has already been loaded Class c = findLoadedClass(name); if (c == null) { try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class. c = findClass(name); } } if (resolve) { resolveClass(c); } return c; }
findLoadedClass 此方法负责从当前ClassLoader实例对象的缓存中寻找已加载的类,调用的为native方法。
protected final Class<?> findLoadedClass(String name) { if (!checkName(name)) return null; return findLoadedClass0(name); } private native final Class findLoadedClass0(String name);
findClass 此方法直接抛出ClassNotFoundException异常,因此要通过覆盖loadClass或此方法来以自定义的方式加载相应的类。
protected Class<?> findClass(String name) throws ClassNotFoundException { throw new ClassNotFoundException(name); }
findSystemClass 此方法是从sun.misc.Launcher$AppClassLoader中寻找类,如果未找到,则继续从BootstrapClassLoader中寻找,如果仍然未找到,返回null
protected final Class<?> findSystemClass(String name) throws ClassNotFoundException { ClassLoader system = getSystemClassLoader(); if (system == null) { if (!checkName(name)) throw new ClassNotFoundException(name); Class cls = findBootstrapClass(name); if (cls == null) { throw new ClassNotFoundException(name); } return cls; } return system.loadClass(name); }
defineClass 此方法负责将二进制字节流转换为Class对象,这个方法对于自定义类加载器而言非常重要。如果二进制的字节码的格式不符合jvm class文件格式规范,则抛出ClassFormatError异常;如果生成的类名和二进制字节码不同,则抛出NoClassDefFoundError;如果加载的class是受保护的、采用不同签名的,或者类名是以java.开头的,则抛出SecurityException异常。
protected final Class<?> defineClass(String name, byte[] b, int off, int len, ProtectionDomain protectionDomain) throws ClassFormatError { return defineClassCond(name, b, off, len, protectionDomain, true); } // Private method w/ an extra argument for skipping class verification private final Class<?> defineClassCond(String name, byte[] b, int off, int len, ProtectionDomain protectionDomain, boolean verify) throws ClassFormatError { protectionDomain = preDefineClass(name, protectionDomain); Class c = null; String source = defineClassSourceLocation(protectionDomain); try { c = defineClass1(name, b, off, len, protectionDomain, source, verify); } catch (ClassFormatError cfe) { c = defineTransformedClass(name, b, off, len, protectionDomain, cfe, source, verify); } postDefineClass(c, protectionDomain); return c; }
resolveClass 此方法负责完成Class对象的链接,如果链接过,则直接返回。
ClassNotFoundException この例外の理由は、現在の ClassLoader でクラスをロードするときにクラス ファイルが見つからなかったことです。
NoClassDefFoundError この例外は、ロードされたクラスが参照されているためです。さらに、クラスが存在しない場合、たとえば、A をロードしたいが、B が A から盗まれた場合、B が存在しない場合、または現在の ClassLoader が B をロードできない場合、この例外がスローされます。
LinkageError この例外は、カスタム ClassLoader の場合に発生する可能性が高くなります。主な理由は、このクラスがすでに ClassLoader にロードされており、繰り返しロードするとこの例外が発生することです。
上記は Java Virtual Machine Learning - ClassLoader の内容です。その他の関連コンテンツについては、PHP 中国語 Web サイト (www.php.cn) に注目してください。