什么是双亲委派机制?分析源代码来一探究竟!

什么是双亲委派机制?

  • 当某个类加载器需要加载某个.class文件时,它首先把这个任务委托给他的上级类加载器,递归这个操作,如果上级的类加载器没有加载,自己才会去加载这个类。

类加载器有哪几种?

  1. BootstrapClassLoader(启动类加载器)
    c++编写,加载java核心库 java.*,构造ExtClassLoaderAppClassLoader。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作
  2. ExtClassLoader (标准扩展类加载器)
    java编写,加载扩展库,如classpath中的jrejavax.*或者 java.ext.dir 指定位置中的类,开发者可以直接使用标准扩展类加载器。主要负责加载jre/lib/ext目录下的一些扩展的jar
  3. AppClassLoader(系统类加载器)
    java编写,加载程序所在的目录,如user.dir所在的位置的class
  4. CustomClassLoader(用户自定义类加载器)
    java编写,用户自定义的类加载器,可加载指定路径的class文件

咱们来看一下源代码

在这里插入图片描述

/**
 * 上面那张图是原版的注释,翻译后为以下内容
 * 
 * 用指定的二进制名称加载类。 此方法的默认实现按以下顺序搜索类:
 *   1.调用 findLoadedClass 来检查一下这个类是否已经被加载了
 *   2.在父加载器上面调用 loadClass 方法,如果父加载器为空,取而代之的是使用虚拟机内置的类加载器
 *   3.调用 fildClass 方法去寻找这个类
 * 如果这个类通过以上的步骤找到了,并且 resolve 标志为 true ,
 * 则此方法将在生成的 Class 对象上调用 resolveClass 方法  (resolve含有解析的意思)
 * 鼓励 ClassLoader 的子类重写 findClass ,而不是此方法。
 * 除非被覆盖,否则此方法将在整个类加载过程中同步 getClassLoadingLock 方法的结果
 */
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;
    }
}

为了更容易理解上面的代码,用一张图来描述一下:

在这里插入图片描述

当一个.class这样的文件要被加载时。先假设没有自定义类加载器,那么就会首先会在AppClassLoader中检查是否加载过,如果有那就无需再加载了。如果没有,那么会去到父加载器,然后调用父加载器的loadClass方法。父加载器中同理会先检查自己是否已经加载过,如果没有再往上去找。
注意这个过程,在到达BootstrapClassLoader之前,都是没有哪个加载器会自己去加载的。而是当父加载器无法加载的时候,才会下沉到子加载器去加载,一直到最底层,如果没有任何加载器能加载,就会抛出ClassNotFoundException异常。

双亲委派机制的作用

  1. 防止重复加载同一个.class。通过委托去向上面问一问,加载过了,就不用再加载一遍。保证数据安全。
  2. 保证核心.class不能被篡改。通过委托方式,不会去篡改核心.class,即使篡改也不会去加载,即使加载也不会是同一个.class对象了。不同的加载器加载同一个.class也不是同一个Class对象。这样保证了Class执行安全。
  3. 举个例子:如果有人写了一个类:String.java,而且还是建在自己项目中自己写的java.lang包下(注意,这里不是指卸载JDK的包,而是自己创建的同名的包),那么在这种机制下,这些系统的类会已经被BootstrapClassLoader加载过了,所以并不会再去加载,从一定程度上防止了危险代码的植入。

咱们来写个测试类测试一下吧

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Kobe_k/article/details/105807622