先来认识下我们今天的主角 public abstract class ClassLoader 类, 他是一个抽象类, sun 公司是这么解释这个类的:
大致意思如下:
类加载器 (class loader) 是一个负责加载 JAVA 类 (classes) 的对象, ClassLoader 类是一个抽象类, 需要给出类的二进制名称, class loader 尝试定位或者产生一个 class 的数据, 一个典型的策略是把二进制名字转换成文件名然后到文件系统中找到该文件
接下来我们看 loadClass 方法的实现方式:
这个是整个方法的实现, 后面我们会对他做分解, 还是来看 sun 公司对该方法的解释:
大致内容如下:
使用指定的二进制名称来加载类, 这个方法的默认实现按照以下顺序查找类:
调用 findLoadedClass(String) 方法检查这个类是否被加载过
使用父加载器调用 loadClass(String) 方法, 如果父加载器为 Null, 类加载器装载虚拟机内置的加载器
调用 findClass(String) 方法装载类
如果, 按照以上的步骤成功的找到对应的类, 并且该方法接收的 resolve 参数的值为 true, 那么就调用 resolveClass(Class) 方法来处理类 ClassLoader 的子类最好覆盖 findClass(String) 而不是这个方法 (loadClass) 除非被重写, 这个方法默认在整个装载过程中都是同步的 (线程安全的)
接下来, 我们开始分析该方法
protected Class<?> loadClass(String name, boolean resolve) 该方法的访问控制符是 protected, 也就是说该方法同包内和派生类中可用返回值类型 Class <?>, 这里用到泛型这里使用通配符? 作为泛型实参表示对象可以接受任何类型 (类类型) 因为该方法不知道要加载的类到底是什么类, 所以就用了通用的泛型 String name 要查找的类的名字, boolean resolve, 一个标志, true 表示将调用 resolveClass(c) 处理该类
throws ClassNotFoundException 该方法会抛出找不到该类的异常, 这是一个非运行时异常
synchronized (getClassLoadingLock(name)) 看到这行代码, 我们能知道的是, 这是一个同步代码块, 那么 synchronized 的括号中放的应该是一个对象我们来看 getClassLoadingLock(name) 方法的作用是什么:
以上是 getClassLoadingLock(name) 方法的实现细节, 我们看到这里用到变量 parallelLockMap, 根据这个变量的值进行不同的操作, 如果这个变量是 Null, 那么直接返回 this, 如果这个属性不为 Null, 那么就新建一个对象, 然后在调用一个 putIfAbsent(className, newLock); 方法来给刚刚创建好的对象赋值, 这个方法的作用我们一会讲那么这个 parallelLockMap 变量又是哪来的那, 我们发现这个变量是 ClassLoader 类的成员变量:
private final ConcurrentHashMap<String, Object> parallelLockMap;
这个变量的初始化工作在 ClassLoader 的构造函数中:
这里我们可以看到构造函数根据一个属性 ParallelLoaders 的 Registered 状态的不同来给 parallelLockMap 赋值 我去, 隐藏的好深, 好, 我们继续挖, 看看这个 ParallelLoaders 又是在哪赋值的呢? 我们发现, 在 ClassLoader 类中包含一个静态内部类 private static class ParallelLoaders, 在 ClassLoader 被加载的时候这个静态内部类就被初始化这个静态内部类的代码我就不贴了, 直接告诉大家什么意思, sun 公司是这么说的: Encapsulates the set of parallel capable loader types, 意识就是说: 封装了并行的可装载的类型的集合
上面这个说的是不是有点乱, 那让我们来整理一下:
首先, 在 ClassLoader 类中有一个静态内部类 ParallelLoaders, 他会指定的类的并行能力, 如果当前的加载器被定位为具有并行能力, 那么他就给 parallelLockMap 定义, 就是 new 一个 ConcurrentHashMap<>(), 那么这个时候, 我们知道如果当前的加载器是具有并行能力的, 那么 parallelLockMap 就不是 Null, 这个时候, 我们判断 parallelLockMap 是不是 Null, 如果他是 null, 说明该加载器没有注册并行能力, 那么我们没有必要给他一个加锁的对象, getClassLoadingLock 方法直接返回 this, 就是当前的加载器的一个实例
如果这个 parallelLockMap 不是 null, 那就说明该加载器是有并行能力的, 那么就可能有并行情况, 那就需要返回一个锁对象然后就是创建一个新的 Object 对象, 调用 parallelLockMap 的 putIfAbsent(className, newLock) 方法, 这个方法的作用是: 首先根据传进来的 className, 检查该名字是否已经关联了一个 value 值, 如果已经关联过 value 值, 那么直接把他关联的值返回, 如果没有关联过值的话, 那就把我们传进来的 Object 对象作为 value 值, className 作为 Key 值组成一个 map 返回然后无论 putIfAbsent 方法的返回值是什么, 都把它赋值给我们刚刚生成的那个 Object 对象
这个时候, 我们来简单说明下 getClassLoadingLock(String className) 的作用, 就是: 为类的加载操作返回一个锁对象为了向后兼容, 这个方法这样实现: 如果当前的 classloader 对象注册了并行能力, 方法返回一个与指定的名字 className 相关联的特定对象, 否则, 直接返回当前的 ClassLoader 对象
接着, 我们的代码分析到 Class c = findLoadedClass(name); 在这里, 在加载类之前先调用该方法检查该类是否已经被加载过, findLoadedClass 会返回一个 Class 类型的对象, 如果该类已经被加载过, 那么就可以直接返回该对象 (在返回之前会根据 resolve 的值来决定是否处理该对象, 具体的怎么处理后面会讲) 如果, 该类没有被加载过, 那么执行以下的加载过程
如果父加载器不为空, 那么调用父加载器的 loadClass 方法加载类, 如果父加载器为空, 那么调用虚拟机的加载器来加载类
如果以上两个步骤都没有成功的加载到类那么执行以下过程:
c = findClass(name) 表示当前 classloader 自己来加载类
这个时候, 我们已经得到了加载之后的类, 那么就根据 resolve 的值决定是否调用 resolveClass 方法 resolveClass 方法的作用是:
链接指定的类这个方法给 Classloader 用来链接一个类, 如果这个类已经被链接过了, 那么这个方法只做一个简单的返回否则, 这个类将被按照 Java 规范中的 Execution 描述进行链接
至此, ClassLoader 类以及 loadClass 方法的源码我们已经分析完了, 那么结合源码的分析, 我们来总结一下
来源: https://mp.weixin.qq.com/s?__biz=MzI3NzE0NjcwMg==&mid=2650120630&idx=1&sn=41e8910a58d8547959c873a97ee9b825&chksm=f36bbc97c41c35812882b1d2d4a1f4903080c3f7cda6b26a9b24b07faade745f900cb7f82da0&scene=38#wechat_redirect