除了顶层的启动类加载器外,其他的类加载器都有父类加载器(备注:这里所说的父类加载器,并不是Java中所指的继承关系的父类)。
工作原理描述:如果一个类加载器收到了类加载的请求,这个类加载器并不会立即去加载,而是会把这个请求委托给父类加载器去完成,如果父类加载器还存在父类加载器,则会进一步向上委托,依次递归,请求最终到达启动类加载器。
如果启动类加载器可以完成类加载的任务,就会成功返回,倘若完成不了加载,子类加载器才会尝试自已去加载,这既是所谓的双亲委派。
从之前的文章我们体会到,不同的类加载器对于类和路径的加载方式是有所不同的,并且各自负责各自的区块,使得逻辑更加的明确,但是这相互的协调工作,如果我们没有一定的方法,就会造成一定的问题,于是就有了双亲委派机制,能够让这些类加载器各司其职,互不干扰。
首先我们可以来看看双亲委派机制的原理框图
然后我来解释一下这张图,
- 在类被加载的时候,是自底向上去执行的,考量是否以前被加载过,如果加载过,直接返回,反之就进行第二步。
- 如果没有加载过,就交给AppClassLoader,也就是Custom ClassLoader的父类,看看AppClassLoader有没有加载过。
- 循环一直到Bootstrap ClassLoader,如果都没有加载过,就会自顶向下,去加载类。
- 首先来到的是rt.jar或者看看是否是自己导入进来的jar包,去加载一下
- 如果没有,就会依次按照所给路径,按照Bootstrap ClassLoader->ExtClassLoader->AppClassLoader->CustomClassLoader去负责寻找他们对应所管辖的加载类的路径去负责加载对应的类文件。
- 需要注意的是,类加载器本身都是用同步锁包裹着的,防止多线程同步加载类。
从逻辑上讲,是很有意思的,可以看看源代码:
不同的类加载器对于类和路径的加载方式是有区别的,并且各自负责各自的部分使逻辑变得更加明确但是需要相互之间的协调工作才可以,如果没有一定的规则和方法就会造成问题,这时双亲委派机制就出现了,它能够使这些类加载器各司其职互不干扰。
首选先来看一下双亲委派机制原理框图:
简要解释一下:
- 类被加载的时候自底向上执行考虑之前是否被加载过,如果被加载过直接返回否则进行第二步;
- 如果没有被加载过,交给AppClassLoader是Custom ClassLoader父类,查看AppClassLoader是否加载过;
- 一直到Bootstrap ClassLoader,如果都没有加载过就会自顶向下尝试去加载类;
- 首先看rt.jar或者看是否有自己导进来的jar包去进行加载;
- 如果没有的话就会依次按照所给路径,按照Bootstrap ClassLoader->ExtClassLoader->AppClassLoader->CustomClassLoader顺序去负责寻找对应所管的加载类的路径去加载对应的类文件;
- 类加载器是用同步锁锁上的,目的是防止多线程同步加载类。
来一下源码:
双亲委派模型
双亲委派的意思是如果一个类加载器需要加载类,那么首先它会把这个类请求委派给父类加载器去完成,每一层都是如此。一直递归到顶层,当父加载器无法完成这个请求时,子类才会尝试去加载。这里的双亲其实就指的是父类,没有mother。父类也不是我们平日所说的那种继承关系,只是调用逻辑是这样。
双亲委派模型不是一种强制性约束,也就是你不这么做也不会报错怎样的,它是一种JAVA设计者推荐使用类加载器的方式。双亲委派有啥好处呢?它使得类有了层次的划分。就拿java.lang.Object来说,你加载它经过一层层委托最终是由Bootstrap ClassLoader来加载的,也就是最终都是由Bootstrap ClassLoader去找<JAVA_HOME>\lib中rt.jar里面的java.lang.Object加载到JVM中。
这样如果有不法分子自己造了个java.lang.Object,里面嵌了不好的代码,如果我们是按照双亲委派模型来实现的话,最终加载到JVM中的只会是我们rt.jar里面的东西,也就是这些核心的基础类代码得到了保护。因为这个机制使得系统中只会出现一个java.lang.Object。不会乱套了。你想想如果我们JVM里面有两个Object,那岂不是天下大乱了。
因此既然推荐使用这种模型当然是有道理了。
破坏双亲委派模型
但是人生不如意事十之八九,有些情况不得不违反这个约束,例如JDBC。
你先得知道SPI(Service Provider Interface),这玩意和API不一样,它是面向拓展的,也就是我定义了这个SPI,具体如何实现由扩展者实现。我就是定了个规矩。
JDBC就是如此,在rt里面定义了这个SPI,那mysql有mysql的jdbc实现,oracle有oracle的jdbc实现,反正我java不管你内部如何实现的,反正你们都得统一按我这个来,这样我们java开发者才能容易的调用数据库操作。
所以因为这样那就不得不违反这个约束啊,Bootstrap ClassLoader就得委托子类来加载数据库厂商们提供的具体实现。因为它的手只能摸到<JAVA_HOME>\lib中,其他的它无能为力。这就违反了自下而上的委托机制了。
Java就搞了个线程上下文类加载器,通过setContextClassLoader()默认情况就是应用程序类加载器然后Thread.current.currentThread().getContextClassLoader()获得类加载器来加载。
除了顶层的启动类加载器外,其他的类加载器都有父类加载器(备注:这里所说的父类加载器,并不是Java中所指的继承关系的父类)。
工作原理描述:如果一个类加载器收到了类加载的请求,这个类加载器并不会立即去加载,而是会把这个请求委托给父类加载器去完成,如果父类加载器还存在父类加载器,则会进一步向上委托,依次递归,请求最终到达启动类加载器。
如果启动类加载器可以完成类加载的任务,就会成功返回,倘若完成不了加载,子类加载器才会尝试自已去加载,这既是所谓的双亲委派。
转载于:https://www.cnblogs.com/chaiming520/p/10900514.html