[五]类加载机制双亲委派机制 底层代码实现原理 源码分析 java类加载双亲委派机制是如何实现的
程序员文章站
2023-04-06 11:12:46
类加载机制中的双亲委派模型是非常重要的,本文从源码的角度对双亲委派模式进行了解析,源码调用基本逻辑很简单. ......
launcher启动类
本文是双亲委派机制的源码分析部分,类加载机制中的双亲委派模型对于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")
classloader的构造方法
前面说过,对于虚拟机来说只有两种类加载器
启动类加载器以及其他所有,而其他所有都是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
上一篇: fabric Report API
下一篇: 转载Linux下开启MySQL日志