最近在和同学聊天的时候,听他们说去面试的时候双亲委派是一个高频知识点,而且基本都是被虐的体无完肤,我也思考了我自己在平时的工作中碰到的和了解过的知识点,这个平时确实是用的不多(大佬请绕过,哈哈),之前看到过一些博客文章也有写,不过看过之后就忘记了。废话不多说下面开始正文:
文章主要从五个部分说起:双亲委派是什么?怎么实现的这部分功能?为什么要有双亲委派?为什么要破坏双亲委派?如何破坏双亲委派?
一、双亲委派是什么?
双亲委派说的是:当一个类加载器在收到加载类的请求时,自己不首先去加载,而是去找他的父加载器去加载,一直向上找,如果父加载器都无法加载,他才自己尝试加载。
Java虚拟机中一般都有的三种加载器:Bootstrap ClassLoader,Extension ClassLoader和Application ClassLoader。
Bootstrap ClassLoader 加载<JAVA_HOME>\lib中的,或者被-Xbootclasspath参数所指定的路径中的,并且能够被虚拟机识别的类(通过名字识别,如果名字对应不上,即使放进去了也不会被加载);
Extension ClassLoader主要是加载<JAVA_HOME>中,或者java.ext.dirs指定路径中的所有类;
Application ClassLoader主要是加载用户类路径下的类,如果没有指定,他就是作为应用程序的默认加载器。
除了上述三种加载器,用户可以自定义类加载器,去实现类的加载。他们的关系如下图所示:
二、为什么要用双亲委派?
Java中有很多的基础类,比如Object类,他是所有对象的父类,而且他的加载最终都是交给了Bootstrap ClassLoader去加载,这样在任何情况下,都是相同的一个类,如果不是这样的话,可能就会有很多这样的类,这样就会很麻烦。而且这样做使得java类天生就带着层级,结构非常的清晰。
三、怎么实现的这部分功能?
类加载主要是通过java.lang.ClassLoader来实现,代码非常简单,如果类已经被加载就立马返回,如果没有被加载并且父加载器不为空,就调用父加载器去加载,如果没有被加载切父加载器为空,就直接用BootstrapClassLoader去加载,如果加载不到就抛出ClassNotFound异常,然后调用自己的类加载器去加载,如下所示:
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
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
}
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();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
四、为什么要破坏双亲委派?
跟中国的文化一样,所有的词都有一个正反,你要执着,你不要钻牛角尖等等还有很多有意思的词语。双亲委派也一样,为什么要破坏双亲委派呢?举一个最常见但是容易被咱们忽视的一个点,tomcat里边的多个应用,一个应用用的是commons-lang-2.4.jar,另一个应用用的是commons-lang-3.4.jar,如果还是按照双亲委派模型去加载,是无论如何都加载不了包路径、类名一样的两个类的。还有很多其他方面的应用,比如说JDBC里边的SPI创建数据库连接的时候,也破坏了双亲委派,有兴趣的话可以去研究一下。
五、如何破坏双亲委派?
我们知道了双亲委派是怎么实现的,那么“破坏”起来就方便了很多,就是重写loadClass就可以了。但是如果我们要自定义一个类加载器,但是不破坏双亲委派模型,那么我们就重写一下findClass方法就可以了。
强烈推荐下图所示公众号: