前言~
最近个人在研究SPI机制的底层,SPI实现中一大重点就是打破了双亲委派模型.既然要学会SPI,那就必须了解什么是双亲委派模型,SPI为什么要打破双亲委派模型?
而这一切的前提都是要了解双亲委派模型.故这就是本博客书写的原因~~
以下是正文
要介绍双亲委派模型,自然是要先介绍其概念~~
双亲委派模型(Parent-Delegation Model)是Java类加载机制中的一种模型,它用于描述Java虚拟机在加载类时的行为.
在Java虚拟机中,每个类都由一个类加载器来加载,让类加载器之间形成了一种层次分明的结构~根加载器位于最顶层,而Ext,App等类加载则依次往下,负责加载各自的类.Java虚拟机会按照一定的顺序查询每个类加载器,查看是否能找到该类,这种查询顺序我们一般将其称为双亲委派模型.
双亲委派机制的工作原理如上图,当一个类加载器需要加载一个类时,它会检查该级虚拟机内有无加载此类,若无,则会把该类委托给它的父加载器,父加载器重复这个流程,直到委派给最顶端的根加载器.如若根加载器无法加载,则由下一级的类加载器加载,依次循环至最下层的类加载器为止.
对于这一部分,我做了一部分的源码探究,首先,在类加载器加载类(loadClass)时,代码会运行至java.lang.ClassLoader(抽象类)中如图所示的位置:
继续向下追踪,可以看到这段核心代码:
加锁什么的我就不解释了,第一句重点代码:
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
其上的注释也很好解释了它的作用:先查找该类是否已经被加载,能在虚拟机中找到.并将查找的结果存至c.请注意!这一段起的作用更类似于缓存的效果,并非直接用该类加载器直接加载!
之后的重点便是这一段:
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
首先判断c是否为空(若已经被虚拟机加载c便会非空),(中间的时间记录啥的不算重点),然后判断其是否拥有父加载器.若有,则递归至父加载器重新执行该段函数.直至根加载下一级为止,此时,parent为空,便执行else中语句,在根加载器中尝试加载该类.至于catch,emmm注释上也说得很清楚,如果没有从非空的父类加载器中找到类,则抛出ClassNotFoundException.不过catch到这个异常后并没有什么处理罢了~
之后看看这一段代码:
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
在确认c为空(未在虚拟机中加载且父加载器类未能加载该类)后,便会执行findClass方法,尝试加载该类,之后哪些时间统计啥的不做描述,自行查看注释即可~
之后该段递归结束,将c返回子类加载器,就在这:
若是父加载器加载成功自不必多说, 若是父加载器没有加载到~~那就再来一遍吧!
至此,我基本对双亲委派机制的源码有了部分初略的理解~(//TODO:研究findClass具体实现)
而双亲委派的优点也很明显:保证类的唯一性.
仔细设想:当一个类被某个类加载器加载时,它能确保从根加载器类开始,且加载完毕会"缓存"至虚拟机中,在下次加载时可直接取用,自然保证了其唯一性,让系统更加安全.
那么这种方式有没有缺点?有!其优点某种意义上正是其缺点,再次设想一下:就比如我新增一个tomcat容器,它能和我在java本身共用类加载器吗?这样的话如何保证相互间的独立性?毕竟不同的应用程序可能会依赖一个第三方类库的不同版本.此时再用双亲委派模型自然不妥.这时,我们便需要打破双亲委派模型,保证互相隔离.
至此,初探双亲委派模型才算结束,双亲委派模型解释,为什么要打破双亲委派模型的目的正式达成~