双亲委派机制

发布于:2023-01-04 ⋅ 阅读:(480) ⋅ 点赞:(0)

1、类加载的生命周期

2、什么是类加载

通过javac将.java文件编译成.class字节码文件后,由加载器将该字节码文件加载到JVM的过程叫类加载:

  • 通过全类名获取定义此类的二进制字节流;
  • 将字节流所代表的静态存储结构转换为方法区的运行时数据结构;
  • 在内存中生成一个代表该类的 Class 对象,作为方法区这些数据的访问入口。

 3、三层类加载器

  • 启动类加载器(Bootstrap ClassLoader):虚拟机的一部分,使用 C++ 实现,加载 <JAVA_HOME>/jre/lib 目录中,或者被 -Xbootclasspath 参数指定的路径中存放的类库。
  • 扩展类加载器(Extension ClassLoader):独立于虚拟机外部,使用 Java 实现,加载 <JAVA_HOME>/jre/lib/ext 目录中,或者被 java.ext.dirs 系统变量所指定的路径中所有的类库。
  • 应用程序加载器(Application ClassLoader):独立于虚拟机外部,使用 Java 实现,加载应用的 CLASSPATH 目录的所有类库。 
import sun.misc.Launcher;
import java.net.URL;
/**
 * 创建者:zjm
 * 创建时间:2022/8/24
 * 描述:
 * 启动类加载器(Bootstrap ClassLoader), 
 * 扩展类加载器(Extension ClassLoader), 
 * 应用程序类加载器(Application ClassLoader)加载的目录
 */
public class JDKClassLoader {
    public static void main(String[] args) {
        System.out.println("bootstrapLoader加载以下文件:");
        URL[] urls = Launcher.getBootstrapClassPath().getURLs();
        for (URL url : urls) {
            System.out.println(url);
        }
        System.out.println("ExtClassLoader加载以下文件:");
        System.out.println(System.getProperty("java.ext.dirs"));

        System.out.println("AppClassLoader加载以下文件:");
        System.out.println(System.getProperty("java.class.path"));
    }
}

 输出:

BootstrapLoader加载以下文件:
file:/D:/Program%20Files/Java/jdk1.8.0_181/jre/lib/resources.jar
file:/D:/Program%20Files/Java/jdk1.8.0_181/jre/lib/rt.jar
file:/D:/Program%20Files/Java/jdk1.8.0_181/jre/lib/sunrsasign.jar
file:/D:/Program%20Files/Java/jdk1.8.0_181/jre/lib/jsse.jar
file:/D:/Program%20Files/Java/jdk1.8.0_181/jre/lib/jce.jar
file:/D:/Program%20Files/Java/jdk1.8.0_181/jre/lib/charsets.jar
file:/D:/Program%20Files/Java/jdk1.8.0_181/jre/lib/jfr.jar
file:/D:/Program%20Files/Java/jdk1.8.0_181/jre/classes

ExtClassLoader加载以下文件:
D:\Program Files\Java\jdk1.8.0_181\jre\lib\ext;
C:\Windows\Sun\Java\lib\ext

AppClassLoader加载以下文件:
D:\Program Files\Java\jdk1.8.0_181\jre\lib\charsets.jar;
D:\Program Files\Java\jdk1.8.0_181\jre\lib\deploy.jar;
D:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\access-bridge-64.jar;
D:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\cldrdata.jar;
D:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\dnsns.jar;
D:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jaccess.jar;
D:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jfxrt.jar;
D:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\localedata.jar;
D:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\nashorn.jar;
D:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunec.jar;
D:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunjce_provider.jar;
D:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunmscapi.jar;
D:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunpkcs11.jar;
D:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\zipfs.jar;
D:\Program Files\Java\jdk1.8.0_181\jre\lib\javaws.jar;
D:\Program Files\Java\jdk1.8.0_181\jre\lib\jce.jar;
D:\Program Files\Java\jdk1.8.0_181\jre\lib\jfr.jar;
D:\Program Files\Java\jdk1.8.0_181\jre\lib\jfxswt.jar;
D:\Program Files\Java\jdk1.8.0_181\jre\lib\jsse.jar;
D:\Program Files\Java\jdk1.8.0_181\jre\lib\management-agent.jar;
D:\Program Files\Java\jdk1.8.0_181\jre\lib\plugin.jar;
D:\Program Files\Java\jdk1.8.0_181\jre\lib\resources.jar;
D:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar;
D:\ideaWorkSpace\out\production\jvmDemo;
D:\Program Files\JetBrains\IntelliJ IDEA 2019.2.4\lib\idea_rt.jar

进程已结束,退出代码 0

4、类加载核心源码(双亲委派如何实现):

当一个类加载器收到加载请求时,它不会直接去加载指定的类,而是把这个请求委托给自己的父加载器去加载(各加载器之间不是继承关系,而是通过parent 参数来确定上下级关系),即所有加载请求最终都会委派到最顶层的加载器(Bootstrap ClassLoader)去尝试类的加载,只有当父类无法加载时,才会由子类尝试加载,如果都无法加载时,则抛出异常。

双亲委派机制实现源码:

子类并不做加载操作,只是向上委派:return super.loadClass(var1, var2);

public Class<?> loadClass(String var1, boolean var2) throws ClassNotFoundException {
    int var3 = var1.lastIndexOf(46);
    if (var3 != -1) {
        SecurityManager var4 = System.getSecurityManager();
        if (var4 != null) {
            var4.checkPackageAccess(var1.substring(0, var3));
        }
    }

    if (this.ucp.knownToNotExist(var1)) {
        Class var5 = this.findLoadedClass(var1);
        if (var5 != null) {
            if (var2) {
                this.resolveClass(var5);
            }

            return var5;
        } else {
            throw new ClassNotFoundException(var1);
        }
    } else {
        return super.loadClass(var1, var2);
    }
}
//super.loadClass(var1, var2)
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;
    }
}

双亲委派机制图解: 

5、为什么要使用双亲委派机制

安全:就算自己定义了一个Java.lang.String,加载器也会通过

AppClassLoader--->ExtClassLoader-->BootstrapLoader路径加载到核心jar包。可以防止核心 API库被随意篡改。

避免类重复加载:当父亲已经加载了该类时,子ClassLoader就不会再加载。

举例:

6、如何破坏双亲委派机制:

双亲委派机制是在java.lang.ClassLoader类中的loadClass方法实现的,只要自定义类加载器,重写loadClass方法就可以了,tomcat就破坏了双亲委派机制。


网站公告

今日签到

点亮在社区的每一天
去签到