程序员潇然 发表于 2022-11-8 15:00:21

java 类加载机制 双亲委派机制源码 分析(六)

本文是双亲委派机制的源码分析部分,类加载机制中的双亲委派模型对于jvm的稳定运行是非常重要的
不过源码其实比较简单,接下来简单介绍一下

我们先从启动类说起

### Launcher启动类

有一个Launcher类   sun.misc.Launcher;

!(data/attachment/forum/202211/08/144840ohabf5peb2a67f11.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

仔细看下这简短的几行注释,可以得到有用的信息
ps:

如果没下载源码,直接IDE里面查看反编译的,看不到注释的,可以下载openJDK查看源码,我的这个版本是openjdk-8-src-b132-03_mar_2014


sun.misc.Launcher
这个类是系统用于启动主应用的启动器

构造方法 Launcher() 中做了四件事情


| 创建          扩展          类加载器    |
| --------------------------------------- |
| 创建          应用程序    类加载器      |
| 设置ContextClassLoader                  |
| 如果需要安装安全管理器 security manager |

其中launcher是staitc的,所以初始化的时候就会创建对象,也就是触发了构造方法,所以初始化的时候就会执行上面四个步骤

!(data/attachment/forum/202211/08/145000dhzfobhpoxpxkgku.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

ExtClassLoader 和AppClassLoader都是Launcher的静态内部类

!(data/attachment/forum/202211/08/145014vkqv6kuiru3i3vk4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

他们也都是ClassLoader的实现类

!(data/attachment/forum/202211/08/145030ztmghtkwu5dnythu.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

看下ExtClassLoader的创建中的关键几步

!(data/attachment/forum/202211/08/145045p3vd3444imdmo4me.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

也在看下AppClassLoader的创建中的关键几步

!(data/attachment/forum/202211/08/145057hqwedqqyq5uluuqu.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")


另外还有

Launcher类中的静态变量

!(data/attachment/forum/202211/08/145129vzuyybxp62623ps5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

你应该可以想得到下面这三个到底是什么东西,如果真不懂,你需要再去研究下

```java
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

!(data/attachment/forum/202211/08/145240kabrrs27hshj7yh7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")



ps:

本文中的不少地方,我都在"父类加载器" 的"父 "和"类加载器"中间加了几个空格

千万不要理解成父类加载器,<父    类加载器> 指的是类加载器的加载顺序层级结构的优先顺序   而不是平时说的继承关系中的父类 父 意味着他的上一层级

!(data/attachment/forum/202211/08/145336kzeu3udleeddrrlq.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")



再回头看一眼应用程序   类加载器的构造

扩展类加载器作为参数传递给了他,他最终调用的就是ClassLoader 的一个参数的构造方法

**将ExtClassLoader 设置为 AppClassLoader的parent**

!(data/attachment/forum/202211/08/145353en36nineg6edv6ef.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")


而ExtClassLoader,他的parent 是null

!(data/attachment/forum/202211/08/145405xpop1rfrfy3cpwxm.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")


ps:启动类加载器 是虚拟机的一部分,可能c/c++/java实现的,所以不是java语言的一部分

所以对于java本身来说,可以说他是不存在的,但是JVM是知道他的

所以说,此处为null ,parent为null说明他的父    类加载器是启动类加载器   或者可能就是启动类加载器本身

### loadClass与findClass

想要实现类 加载器,需要继承ClassLoader
并且有两个重要的方法
看下两个重要方法的声明,你可能就感觉出来了,想想public 和 protected都是啥意思?

!(data/attachment/forum/202211/08/145439mhxfroj4is4ll8kl.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")


| **loadClass方法是类加载器执行   加载类逻辑   的方法,包括检查是否已经加载,调用父类加载,失败则自己尝试使用 findClass方法加载** |
| ---------------------------------------------------------------------------------------------------------------------------------- |
| **findClass当前类加载器 实际执行加载二进制流的具体行为方法**                                                               |


Launcher.APPClassLoader中的loadClass方法,最终调用的是super.loadClass, 实际上就是ClassLoader的loadClass方法

Launcher.ExtClassLoader根本就没有实现自己的loadClass 方法,所以使用的也是ClassLoader中的

!(data/attachment/forum/202211/08/145735j01ms76n68ypsmz0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")


再来看看ClassLoader的loadClass方法
他会调用parent的loadClass方法,如果他的parent不为空,将会一直调用父 类加载器, 直到最顶级的启动   类加载器
如果 启动   类加载器仍旧找寻不到, 那么调用自身的findClass

!(data/attachment/forum/202211/08/145750ydkg4zpgl14g4741.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

如果自己调用findClass加载失败呢?
很显然, 函数调用结束之后,会返回到调用点位置,调用栈的形式嘛
也就是经过

!(data/attachment/forum/202211/08/145804hxstikbstmfjsjn6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

必然要继续执行他的下一段
如果没抛出异常的话,就会走到下面这里

!(data/attachment/forum/202211/08/145818o6ee1ltll22si2nz.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

显然这就完成了一整个的双亲委派的类加载模式

!(data/attachment/forum/202211/08/145837hzr1xztqtb10qqbs.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

### 总结

Launcher作为启动器
创建了ExtClassLoader 以及AppClassLoader
他们都是ClassLoader的子类,并且ClassLoader有一个parent指向他的父   类加载器
正是这个属性完成了自顶而下的 优先级层级顺序的确定
对于sun内置的ExtClassLoader 以及AppClassLoader以及启动类加载器 Bootstrap他们的层级为
Bootstrap>ExtClassLoader>ExtClassLoader
并且,他们各自有不同的分工
通过ClassLoader的loadClass方法,确定了他们的调用逻辑,也就是双亲委派机制
每个层级都会向上传递类加载请求,只有上层父   类加载器调用失败,才会自己尝试加载
双亲委派机制的意义重大,带来了更高的安全性等优点
不过他的实现逻辑却是的确很简单
一个loadClass就搞定了
findClass是类加载器自身加载类的具体行为
所以,如果你不需要破坏双亲委派机制,只需要覆盖这个方法即可
如果你想要完全自定义你的类加载器的逻辑机制,直接覆盖loadClass,当然,你可能还需要继续覆盖findClass

!(data/attachment/forum/202206/16/141330jha7st9soow8772i.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "common_log.png")
`转载务必注明出处:程序员潇然,疯狂的字节X,https://crazybytex.com/thread-211-1-1.html `
页: [1]
查看完整版本: java 类加载机制 双亲委派机制源码 分析(六)