ClassLoader Javadoc分析

刘胜泫
2023-12-01

ClassLoader Javadoc

基本信息

位于java.lang包下,从jdk1.0开始出现,抽象类,即需要通过子类实现的方式进行实例化。

package java.lang;
/**
 * ... 这里的内容将是分析的重点
 * @since 1.0
 */
public abstract class ClassLoader {
    ...
}

Javadoc分析

A class loader is an object that is responsible for loading classes.The class ClassLoader is an abstract class.
一个类加载器是用于负责加载类的对象。类ClassLoader是一个抽象类。

从整体上描述了类加载器的作用。

Given the binary name of a class, a class loader should attempt to locate or generate data that constitutes a definition for the class.
给定一个类的“binary name”,类加载应该尝试去定位或生成数据,这些数据构成了类的定义。

  • binary name
    首先,看下这个binary name,这个“binary name”在javadoc中的下方有相关解释说明

Any class name provided as a String parameter to methods in ClassLoader must be a binary name as defined by The Java™ Language Specification.
以字符串参数的形式向ClassLoader类中的方法所提供的任意一个类名,必须是一个在Java语言规范中所定义的二进制的名称。

即一个类的名字是要以字符串参数的形式提供出来,这个字符串参数形式的类名在Java语言规范中有所定义。
一些有效的类名示例:
“java.lang.String”,String类名;
“javax.swing.JSpinner$ DefaultEditor”,类JSpinner的内部类DefaultEditor类名($ 后的空格是为了排版,实际没有空格)
“java.security.KeyStore$ Builder$FileBuilder$1”,类KeyStore的内部类Builder的内部类FileBuilder中的第一个匿名内部类( $后的空格是为了排版,实际没有空格)
“java.net.URLClassLoader$3$1”,URLClassLoader中第三个匿名内部类中的第一个匿名内部类

  • locate or generate data
    这里有两种情形,一种是类加载器去定位数据,还有一种是类加载器生成数据。其实根据实际情况很好理解,定位数据即class文件已经实际存在,如java.lang.String已经存在磁盘上,类加载器只需要去定位类在文件系统中的位置;生成数据的情况,Java在运行期间会动态生成,如动态代理。

A typical strategy is to transform the name into a file name and then read a “class file” of that name from a file system.
一个典型的方式,将给定的类二进制名字转换成文件名,然后从文件系统中去读取这个文件名所对应的class file信息。

这个只是一种常见的加载类的方式,因为Java语言规范中并未指明采取何种方式去加载类,所以各个JVM厂商可以自行实现。比较常见的方式有:

  • 从本地系统中直接加载,如本地磁盘,classpath
  • 通过网络下载.class文件
  • 从zip、jar等归档文件中加载.class文件
  • 从专有的数据库中提取.class文件
  • 将Java源文件动态编译成.class文件,如动态代理,jsp等

Every Class object contains a reference to the ClassLoader that defined it.
每个Class对象包含了定义它的类加载器的引用。

可以从Class类源码中看出,定义了一个ClassLoader类型的成员常量:

// Initialized in JVM not by private constructor
// This field is filtered from reflection access, i.e. getDeclaredField
// will throw NoSuchFieldException
private final ClassLoader classLoader;

同时Class类还提供了相应的get方法来获取这个定义类的类加载器对象

Class objects for array classes are not created by class loaders, but are created automatically as required by the Java runtime.
对于数组类的Class对象并不是由类加载器来创建的,而是由Java运行期间需要这个数组的时候自动创建的。

这句可以理解成,除了数组类的其他类都是由类加载器来创建的。
对于数据类型是由jvm运行期动态生成的,表示为***[Lcom.test.Test;***(注:后面有一个因为分号)这种形式。
动态生成的类型,父类型就是Object
对于数组来说,JavaDoc经常将构成数组的元素为Component,实际上是将数组降低一个维度后的类型
助记符:
anewarray,表示创建一个引用类型的数组,并将其引用值压入栈顶
newarray,表示创建一个指定的原生类型(如:int,char,long,float等)的数组,并将其引用值压入栈顶

The class loader for an array class, as returned by Class.getClassLoader() is the same as the class loader for its element type; if the element type is a primitive type, then the array class has no class loader.
对于数组类的类加载器,可以通过调用 Class.getClassLoader() 方法来获取,这个加载器是与加载数组中元素类型的类加载器是一样的,如果数组元素的类型是一个原生类型,那么这个数组类是没有类加载器的。

原生类型有:

  • 整型(int)
  • 字节型(byte)
  • 短整型(short)
  • 长整型(long)
  • 字符型(char)
  • 单精度浮点型(float)
  • 双精度浮点型(double)
  • 布尔型(bool)

示例:

public static void main(String[] args) {
     String[] strings = new String[2];
     log.info("String[] class: {}, classLoader: {}", strings.getClass(), strings.getClass().getClassLoader());
     // 输出的结果是:String[] class: class [Ljava.lang.String;, classLoader: null
     // 数组元素类型是java.lang.String是由根类加载器加载的,所以String[]的类加载器是null

     ArrayTypeClassLoader[] objArr = new ArrayTypeClassLoader[2];
     log.info("Object[] class: {}, classLoader: {}", objArr.getClass(), objArr.getClass().getClassLoader());
     // 输出的结果是:Object[] class: class [Lcom.soup.memo.jvm8.classloader.ArrayTypeClassLoader;, classLoader: sun.misc.Launcher$AppClassLoader@18b4aac2
     // 数组元素类型是ArrayTypeClassLoader,是自定义的类,是由应用类加载器加载的,所以对象数组的类加载器是AppClassLoader

    int[] intArr = new int[2];
	log.info("int[] class: {}, classLoader: {}", intArr.getClass(), intArr.getClass().getClassLoader());
	// 输出的结果是:int[] class: class [I, classLoader: null
    // 数组元素类型是int,int原生类型,所以数组的类加载器没有,这里的null不是根类加载器加载
}

Applications implement subclasses of ClassLoader in order to extend the manner in which the Java virtual machine dynamically loads classes.
应用实现ClassLoader的子类,是为了扩展JVM动态加载类的方式。

由于JVM加载类的默认方式是双亲委托机制,这种机制是安全性极好的方式,但是在某些场景需要改变这个方式来加载类,这时就需要创建自定义类加载器。

Class loaders may typically be used by security managers to indicate security domains.
类加载典型被安全管理器所使用来标识安全域问题。

The ClassLoader class uses a delegation model to search for classes and resources.
类加载器类使用了一种委托模型来寻找类和资源。

Each instance of ClassLoader has an associated parent class loader.
类加载器的每一个实例都会有一个与之关联的父亲类加载器。

When requested to find a class or resource, a ClassLoader instance will delegate the search for the class or resource to its parent class loader before attempting to find the class or resource itself.
当请求去寻找一个类或资源的时候,类加载器的实例就会将这个请求委托给父类加载器,这个委托发生在自己去寻找这个类或资源之前。

The virtual machine’s built-in class loader, called the “bootstrap class loader”, does not itself have a parent but may serve as the parent of a ClassLoader instance.
虚拟机内建的类加载器称之为启动类加载器(根类加载器),启动类加载器是没有父亲类加载器,当它可以作为其他类加载器的父亲类加载器。

Class loaders that support concurrent loading of classes are known as parallel capable class loaders and are required to register themselves at their class initialization time by invoking the ClassLoader.registerAsParallelCapable method.
类加载器如果支持并发加载类,称之为拥有并行能力的类加载器,要求这些类加载器在初始化的时候,去通过调用 ClassLoader.registerAsParallelCapable 方法来注册自己,注册的作用就是为了表明这个类加载器拥有并发类加载的能力。

Note that the ClassLoader class is registered as parallel capable by default. However, its subclasses still need to register themselves if they are parallel capable.
需要注意的是 ClassLoader 类默认是注册为并行加载能力,然后如果 ClassLoader 的子类也有并行加载能力仍需要进行注册。

In environments in which the delegation model is not strictly hierarchical, class loaders need to be parallel capable, otherwise class loading can lead to deadlocks because the loader lock is held for the duration of the class loading process (see loadClass methods).
在委托模型不是严格层次化的环境下(可以理解成不是JVM提供的类加载器的情况),类加载器需要拥有并行能力,否则类加载中可能会导致死锁发生,因为一个类加载过程中会持有加载锁。

Normally, the Java virtual machine loads classes from the local file system in a platform-dependent manner. For example, on UNIX systems, the virtual machine loads classes from the directory defined by the CLASSPATH environment variable.
通常情况下,JVM是以平台相关的方式从本地文件系统中加载类。例如,在UNIX系统中,JVM从CLASSPATH环境变量所定义的文件路径来加载类。

However, some classes may not originate from a file; they may originate from other sources, such as the network, or they could be constructed by an application.
然而,有些类并不是来源一个文件,可能来自其他来源,如网络,应用本身(动态代理)。

The method defineClass converts an array of bytes into an instance of class Class. Instances of this newly defined class can be created using Class.newInstance.
defineClass方法将字节数组转换成Class对象的实例。这个Class对象的实例可以通过 Class.newInstance 来创建。

这句包含了2个操作:
先将字节数组转换成Class对象,然后再调用 Class对象的newInstance来创建对象

The methods and constructors of objects created by a class loader may reference other classes.
由类加载器所创建的对象,这些对象的方法和构造方法还可能会引用其他的类。
To determine the class(es) referred to, the Java virtual machine invokes the loadClass method of the class loader that originally created the class.
为了确定这些引用的类是哪些,JVM会调用 类加载器 的loadClass方法来去创建

For example, an application could create a network class loader to download class files from a server. Sample code might look like:
比如,一个应用可以创建一个网络类加载器,通过下载类文件来加载类,示例:

ClassLoader loader = new NetworkClassLoader(host, port);
Object main = loader.loadClass("Main", true).newInstance();
. . .

The network class loader subclass must define the methods findClass and loadClassData to load a class from the network. Once it has downloaded the bytes that make up the class, it should use the method defineClass to create a class instance.
这个ClassLoader的子类NetworkClassLoader类加载器必须要定义 findClass 和 loadClassData 方法,来从网络中加载一个类。
一旦将这个构成类的字节码文件下载好后,应该使用 defineClass 方法来创建Class的实例。

class NetworkClassLoader extends ClassLoader {
	 String host;
	 int port;
	
	 // 根据类的名字去去找类
	 public Class findClass(String name) {
		 byte[] b = loadClassData(name);
		 // 通过这个方法创建Class实例返回
		 return defineClass(name, b, 0, b.length);
	 }
	 // 寻找类,并将构成类的字节数据返回
	 private byte[] loadClassData(String name) {
		 // load the class data from the connection
		  . . .
	 }
 }
 类似资料: