Skip to content

双亲委派机制

双亲委派机制 是Java类加载器(ClassLoader)的核心工作机制,它通过层次化的类加载模型保障了Java程序的稳定性和安全性。

1. 双亲委派的核心机制

双亲委派的核心机制是:当一个类加载器需要加载类时,不会立即尝试自己加载,而是先将请求委派给父类加载器处理。流程如下:

  1. 自底向上委派:子类加载器逐层向上委托,直到启动类加载器(Bootstrap ClassLoader)。
  2. 自顶向下尝试加载:若父类加载器无法加载(在自己的搜索范围内未找到类),子类加载器才会尝试加载。

2. 代码示例:ClassLoader.loadClass()

Java
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
    synchronized (getClassLoadingLock(name)) {
        // 1. 检查类是否已加载
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            try {
                // 2. 委派给父类加载器
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    // 3. 父类为null时,使用Bootstrap ClassLoader(由JVM实现)
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // 父类加载失败,不立即抛出异常
            }

            if (c == null) {
                // 4. 父类未找到,调用自身的findClass()加载
                c = findClass(name);
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

3. 类加载器的层次结构

Java类加载器分为四层,形成树状结构:

  1. Bootstrap ClassLoader(启动类加载器)
    • 由C++实现,负责加载 JAVA_HOME/lib 下的核心类(如java.lang.*)。
    • 唯一没有父类的加载器,是其他所有类加载器的根。
  2. Extension ClassLoader(扩展类加载器)
    1. 加载 JAVA_HOME/lib/ext 目录或 java.ext.dirs 指定的类库。
  3. Application ClassLoader(应用类加载器):
    1. 加载用户路径(ClassPath)下的类,是 ClassLoader.getSystemClassLoader() 的默认返回值。
  4. 自定义ClassLoader:
    1. 用户可继承ClassLoader,重写 findClass() 实现自定义加载逻辑(如从网络、加密文件中加载类)。

4. 双亲委派的优势

  1. 安全性

    1. 防止核心API被篡改:例如自定义 java.lang.Object 类时,由于 Bootstrap ClassLoader已加载核心类,自定义类不会被加载,避免了恶意代码注入。
    2. 沙箱保护机制:通过层级隔离,不同层次的类加载器加载的类无法互相访问(扩展类无法访问应用类)。
  2. 避免重复加载

    1. 类的唯一性标识:一个类由加载它的类加载器和全限定名(如 java.lang.Object)共同确定。双亲委派确保类只被加载一次,避免内存浪费。
  3. 保证基础类的一致性

    1. 所有子类加载器最终都会委托到Bootstrap ClassLoader。确保核心类(如Object、String)全局唯一,避免多版本冲突。
  4. 打破双亲委派的场景 虽然双亲委派是默认机制,但在某些场景下需要逆向委派或平级委派:

  5. SPI(Service Provider Interface): JDBCJNDI等接口由Bootstrap ClassLoader加载,但具体实现(如MySQL驱动)需由线程上下文类加载器(TCCL)加载,打破了双亲委派。

  6. OSGi模块化: 每个Bundle使用独立的类加载器,通过图结构的依赖关系实现类共享和隔离。

  7. 热部署: 通过自定义类加载器重新加载修改后的类(如TomcatJSP的热更新)。

5. 自定义类加载器

自定义类加载器需遵循双亲委派:

继承ClassLoader,重写findClass()(而非loadClass(),否则需手动实现委派逻辑)。 在findClass()中从指定路径(如文件、网络)读取类字节码,调用defineClass()生成类对象。

Java
public class CustomClassLoader extends ClassLoader {
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] classData = loadClassData(name); // 从自定义路径加载字节码
        return defineClass(name, classData, 0, classData.length);
    }
}

Released under the MIT License.