本文是双亲委派机制的源码分析部分,类加载机制中的双亲委派模型对于jvm的稳定运行是很是重要的
不过源码其实比较简单,接下来简单介绍一下
咱们先从启动类提及
有一个Launcher类 sun.misc.Launcher;
仔细看下这简短的几行注释,能够获得有用的信息
ps:直接IDE里面查看反编译的,看不到注释的,能够下载openJDK查看源码,个人这个版本是openjdk-8-src-b132-03_mar_2014
sun.misc.Launcher 这个类是系统用于启动主应用的启动器 |
构造方法 Launcher() 中作了四件事情
建立 扩展 类加载器 |
建立 应用程序 类加载器 |
设置ContextClassLoader |
若是须要安装安全管理器 security manager |
其中launcher是staitc的,因此初始化的时候就会建立对象,也就是触发了构造方法,因此初始化的时候就会执行上面四个步骤
ExtClassLoader 和 AppClassLoader 都是Launcher的静态内部类
并且,他们也都是ClassLoader的实现类
看下ExtClassLoader的建立中的关键几步
也在看下AppClassLoader的建立中的关键几步
另外还有
Launcher类中的静态变量
你应该能够想获得下面这三个究竟是什么东西,若是真不懂,你须要再去研究下
System.getProperty("sun.boot.class.path")
System.getProperty("java.ext.dirs")
System.getProperty("java.class.path")
前面说过,对于虚拟机来讲只有两种类加载器
启动类加载器以及其余全部,而其余全部都是java.lang.ClassLoader的子类
因此想要自定义类加载器,必需要继承实现ClassLoader
并且,咱们上面说到的,java给咱们提供的AppClassLoader 和 ExtClassLoader 也都是ClassLoader的子类
看下ClassLoader的构造方法 和变量parent
你会发现,其实构造方法实际上只有双参数版本这一种
第二个参数为parent,这个parent是一个ClassLoader, 用于记录他的 父 类加载器
无论调用哪一个构造方法
parent必然会被初始化
要么是你调用带参数的构造方法, 显式指定一个来设置parent
若是你不指定,默认的构造方法,会使用 getSystemClassLoader返回的AppClassLoader 设置parent
|
ps:
本文中的很多地方,我都在"父类加载器" 的"父 "和"类加载器"中间加了几个空格
千万不要理解成父类加载器 ,<父 类加载器> 指的是类加载器的加载顺序层级结构的优先顺序 而不是平时说的继承关系中的父类 父 意味着他的上一层级
getSystemClassLoader 获取AppClassLoader 的过程
|
那么再回头看一眼 应用程序 类加载器的构造
扩展 类加载器做为参数传递给了他,他最终调用的就是ClassLoader 的一个参数的构造方法
将ExtClassLoader 设置为 AppClassLoader 的parent
而ExtClassLoader,他的parent 是null
ps:启动 类加载器 是虚拟机的一部分,可能c/c++/java实现的,因此不是java语言的一部分
因此对于java自己来讲,能够说他是不存在的,可是JVM是知道他的
因此说,此处为null ,parent为null说明他的父 类加载器是启动类加载器 或者可能就是启动 类加载器自己
loadClass与findClass
想要实现类 加载器,须要继承ClassLoader
而且有两个重要的方法
看下两个重要方法的声明,你可能就感受出来了,想一想public 和 protected都是啥意思?
loadClass方法是类加载器执行 加载类逻辑 的方法,包括检查是否已经加载,调用父类加载,失败则本身尝试使用 findClass方法加载 |
findClass当前类加载器 实际执行加载二进制流的具体行为方法 |
Launcher.APPClassLoader中的loadClass方法,最终调用的是super.loadClass , 实际上就是ClassLoader的loadClass方法
Launcher.ExtClassLoader 根本就没有实现本身的loadClass 方法,因此使用的也是ClassLoader中的
|
再来看看ClassLoader的loadClass方法
他会调用parent的loadClass方法,若是他的parent不为空,将会一直调用父 类加载器, 直到最顶级的 启动 类加载器
若是 启动 类加载器仍旧找寻不到, 那么调用自身的findClass
若是本身调用findClass加载失败呢?
很显然, 函数调用结束以后,会返回到调用点位置,调用栈的形式嘛
也就是通过
必然要继续执行他的下一段
若是没抛出异常的话,就会走到下面这里
显然这就完成了一整个的双亲委派的类加载模式
总结
Launcher做为启动器
建立了ExtClassLoader 以及AppClassLoader
他们都是ClassLoader的子类,而且ClassLoader有一个parent 指向他的父 类加载器
正是这个属性完成了自顶而下的 优先级层级顺序的肯定
对于sun内置的ExtClassLoader 以及AppClassLoader 以及启动 类加载器 Bootstrap 他们的层级为
Bootstrap>ExtClassLoader>ExtClassLoader
而且,他们各自有不一样的分工
经过ClassLoader的loadClass方法,肯定了他们的调用逻辑,也就是双亲委派机制
每一个层级都会向上传递类加载请求,只有上层 父 类加载器调用失败,才会本身尝试加载
双亲委派机制的意义重大,带来了更高的安全性等优势
不过他的实现逻辑倒是的确很简单
一个loadClass就搞定了
findClass是类加载器自身加载类的具体行为
因此,若是你不须要破坏双亲委派机制,只须要覆盖这个方法便可
若是你想要彻底自定义你的类加载器的逻辑机制,直接覆盖loadClass,固然,你可能还须要继续覆盖findClass