目录

java双亲委派机制

java双亲委派机制

Java的双亲委派机制(Parent Delegation Model)是类加载器(ClassLoader)在加载类时采用的一种工作模型。它是Java类加载机制的核心设计之一,主要用于保证类的唯一性和安全性。以下是双亲委派机制的详细说明:


1. 什么是双亲委派机制?

双亲委派机制是指当一个类加载器收到类加载请求时,它不会立即尝试加载该类,而是将请求委派给父类加载器去完成。每一层的类加载器都会依次向上委派,直到启动类加载器(Bootstrap ClassLoader)。如果父类加载器无法完成加载(在自己的搜索范围内找不到该类),子类加载器才会尝试加载。


2. 双亲委派机制的工作流程

  1. 收到类加载请求

    • 当一个类加载器收到类加载请求时,首先检查该类是否已经被加载过。
    • 如果已经加载过,则直接返回对应的 Class 对象。
  2. 委派给父类加载器

    • 如果该类未被加载过,类加载器会将加载请求委派给父类加载器。
    • 父类加载器会重复同样的过程,继续向上委派,直到启动类加载器。
  3. 父类加载器尝试加载

    • 如果父类加载器可以完成加载,则返回对应的 Class 对象。
    • 如果父类加载器无法完成加载(在自己的搜索范围内找不到该类),则子类加载器会尝试加载。
  4. 子类加载器加载

    • 如果子类加载器也无法加载该类,则抛出 ClassNotFoundException

3. 双亲委派机制的类加载器层次结构

Java中的类加载器按照层次结构组织,主要包括以下几种类加载器:

  1. 启动类加载器(Bootstrap ClassLoader)

    • 负责加载JVM核心类库(如 rt.jar ),由C++实现,是JVM的一部分。
    • 是其他类加载器的顶级父加载器。
  2. 扩展类加载器(Extension ClassLoader)

    • 负责加载 <JAVA_HOME>/lib/ext 目录下的类库。
    • sun.misc.Launcher$ExtClassLoader 实现。
  3. 应用程序类加载器(Application ClassLoader)

    • 负责加载用户类路径( classpath )下的类库。
    • sun.misc.Launcher$AppClassLoader 实现。
  4. 自定义类加载器(Custom ClassLoader)

    • 用户可以通过继承 java.lang.ClassLoader 类实现自定义类加载器。
    • 自定义类加载器的父加载器是应用程序类加载器。

4. 双亲委派机制的优点

  1. 避免类的重复加载

    • 通过委派机制,可以确保一个类只会被加载一次,从而避免类的重复加载。
  2. 保护核心类库的安全

    • 核心类库(如 java.lang 包)由启动类加载器加载,用户无法通过自定义类加载器篡改核心类库。
  3. 保证类的唯一性

    • 同一个类在不同的类加载器加载时会被认为是不同的类,双亲委派机制可以确保类的唯一性。

5. 双亲委派机制的实现

双亲委派机制的实现主要体现在 ClassLoaderloadClass 方法中。以下是 loadClass 方法的简化逻辑:

protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
    // 1. 检查类是否已经被加载
    Class<?> c = findLoadedClass(name);
    if (c == null) {
        try {
            // 2. 委派给父类加载器
            if (parent != null) {
                c = parent.loadClass(name, false);
            } else {
                // 3. 如果父类加载器为null,则委派给启动类加载器
                c = findBootstrapClassOrNull(name);
            }
        } catch (ClassNotFoundException e) {
            // 父类加载器无法加载
        }

        if (c == null) {
            // 4. 父类加载器无法加载时,自己尝试加载
            c = findClass(name);
        }
    }
    // 5. 如果需要解析,则解析该类
    if (resolve) {
        resolveClass(c);
    }
    return c;
}

6. 打破双亲委派机制的场景

在某些特殊场景下,可能需要打破双亲委派机制,例如:

  1. SPI(Service Provider Interface)

    • 如JDBC驱动加载,核心类库(如 java.sql.DriverManager )需要调用用户实现的类(如 com.mysql.cj.jdbc.Driver )。
    • 通过 Thread.currentThread().getContextClassLoader() 获取线程上下文类加载器来加载用户类。
  2. OSGi

    • OSGi是一个模块化框架,每个模块有自己的类加载器,模块之间通过类加载器隔离。
  3. 热部署

    • 在应用运行时动态加载新的类文件,通常需要自定义类加载器。

7. 示例:双亲委派机制的工作过程

假设有以下类加载器层次结构:

  • 启动类加载器(Bootstrap ClassLoader)
  • 扩展类加载器(Extension ClassLoader)
  • 应用程序类加载器(Application ClassLoader)

当应用程序类加载器收到加载 java.lang.String 的请求时:

  1. 应用程序类加载器将请求委派给扩展类加载器。
  2. 扩展类加载器将请求委派给启动类加载器。
  3. 启动类加载器成功加载 java.lang.String ,并返回 Class 对象。

如果加载的是用户自定义类 com.example.MyClass

  1. 应用程序类加载器将请求委派给扩展类加载器。
  2. 扩展类加载器将请求委派给启动类加载器。
  3. 启动类加载器无法加载 com.example.MyClass ,扩展类加载器也无法加载。
  4. 应用程序类加载器尝试加载,并成功加载 com.example.MyClass

8. 总结

双亲委派机制是Java类加载机制的核心设计,通过层次化的类加载器结构和委派机制,保证了类的唯一性、安全性和一致性。