从源码上理解双亲委派机制

63 阅读2分钟

双亲委派模型(Parent Delegation Model)是Java类加载器(ClassLoader)使用的一种机制,在这个模型中,类加载器在尝试加载一个类时,会先委派给其父加载器去尝试加载,如果父加载器无法完成加载任务,才由自己去加载。这个模型的好处是避免了类的重复加载,并保证了Java核心库的类型安全。

// Launcher类的构造器
public Launcher() {
    Launcher.ExtClassLoader extClassLoader;
    try {
        // 尝试创建扩展类加载器(ExtClassLoader)
        extClassLoader = Launcher.ExtClassLoader.getExtClassLoader();
    } catch (IOException var10) {
        throw new InternalError("Could not create extension class loader", var10);
    }

    try {
        // 创建应用程序类加载器(AppClassLoader),并将扩展类加载器作为其父加载器
        this.loader = Launcher.AppClassLoader.getAppClassLoader(extClassLoader);
    } catch (IOException var9) {
        throw new InternalError("Could not create application class loader", var9);
    }
}

// ClassLoader的私有构造器
private ClassLoader(Void unused, ClassLoader parent) {
    this.parent = parent;
    // ...其他初始化代码
}

// loadClass方法的实现
protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // 首先检查这个类是否已经被加载过了
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                // 如果有父加载器,就先让父加载器尝试加载这个类
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    // 如果没有父加载器,则尝试用Bootstrap类加载器来加载这个类
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // 如果父加载器抛出ClassNotFoundException,说明它无法加载这个类
            }

            if (c == null) {
                // 如果类还没有被加载,就调用findClass来尝试加载它
                long t1 = System.nanoTime();
                c = findClass(name);

                // 记录性能监控数据
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        // 如果需要,解析这个类
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

这段代码的流程是:

  1. Launcher构造器中,首先创建了扩展类加载器ExtClassLoader
  2. 然后创建应用程序类加载器AppClassLoader,并将ExtClassLoader作为其父加载器。
  3. ClassLoaderloadClass方法中,首先会检查这个类是否已经加载。
  4. 如果没有加载,会先让父加载器尝试加载这个类。
  5. 如果父加载器无法加载这个类,那么当前加载器会尝试自己加载。
  6. 如果需要,最后会解析这个类。

通过这种方式,Java确保了每个类最多只被加载一次,并且保护了Java运行时环境的核心类库不会被随意覆盖。