何为双亲委派

发布时间:2021-10-23 16:18:14 作者:iii
来源:亿速云 阅读:170
# 何为双亲委派

## 引言

在Java虚拟机(JVM)的类加载机制中,"双亲委派模型"(Parent Delegation Model)是一个核心概念。它不仅是Java安全模型的基石,也是保证Java程序稳定运行的重要机制。本文将深入探讨双亲委派的概念、工作原理、实现细节、优势与局限性,以及在实际开发中的应用场景。

## 一、类加载器基础

### 1.1 类加载器的作用
类加载器(ClassLoader)是JVM用来动态加载类的子系统,主要职责包括:
- 读取.class文件的二进制数据
- 将字节流转换为方法区中的运行时数据结构
- 在堆中生成对应的Class对象

### 1.2 Java内置类加载器
JVM默认提供三类加载器:
1. **Bootstrap ClassLoader**(启动类加载器)
   - 由C++实现,无Java对应类
   - 加载<JAVA_HOME>/lib目录的核心类库

2. **Extension ClassLoader**(扩展类加载器)
   - 继承URLClassLoader
   - 加载<JAVA_HOME>/lib/ext目录的扩展类

3. **Application ClassLoader**(应用类加载器)
   - 又称System ClassLoader
   - 加载用户类路径(CLASSPATH)上的类

```java
// 查看类加载器示例
public class ClassLoaderDemo {
    public static void main(String[] args) {
        System.out.println(String.class.getClassLoader()); // null(Bootstrap加载)
        System.out.println(com.sun.crypto.provider.DESKeyFactory.class.getClassLoader()); // ExtClassLoader
        System.out.println(ClassLoaderDemo.class.getClassLoader()); // AppClassLoader
    }
}

二、双亲委派模型详解

2.1 模型定义

双亲委派模型要求: 1. 除顶层启动类加载器外,每个类加载器都有父加载器 2. 加载类时先委派给父加载器尝试加载 3. 父加载器无法完成时才自己尝试加载

2.2 工作流程

graph TD
    A[自定义类加载器] --> B[AppClassLoader]
    B --> C[ExtClassLoader]
    C --> D[BootstrapClassLoader]
    D -->|无法加载| C
    C -->|无法加载| B
    B -->|无法加载| A

具体步骤: 1. 收到类加载请求后,先检查是否已加载 2. 未加载则调用父加载器的loadClass() 3. 父加载器同样递归委派 4. 直到BootstrapClassLoader 5. 若所有父加载器都无法完成,抛出ClassNotFoundException 6. 子加载器尝试自己加载

2.3 源码实现

以ClassLoader.loadClass()为例:

protected Class<?> loadClass(String name, boolean resolve) {
    synchronized (getClassLoadingLock(name)) {
        // 1. 检查是否已加载
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            try {
                // 2. 父加载器不为null则委派
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    // 3. 父加载器为null则委托Bootstrap
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // 父加载器无法完成
            }
            
            if (c == null) {
                // 4. 自行查找类
                c = findClass(name);
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

三、设计优势分析

3.1 安全性保障

3.2 避免重复加载

3.3 资源优化

四、打破双亲委派的场景

4.1 历史案例:JNDI服务

// 典型SPI加载模式
ClassLoader cl = Thread.currentThread().getContextClassLoader();
ServiceLoader<Driver> loader = ServiceLoader.load(Driver.class, cl);

4.2 OSGi模块化系统

4.3 热部署实现

五、实践中的注意事项

5.1 自定义类加载器

实现步骤: 1. 继承java.lang.ClassLoader 2. 重写findClass()而非loadClass() 3. 建议维护类加载器缓存

public class CustomClassLoader extends ClassLoader {
    @Override
    protected Class<?> findClass(String name) {
        byte[] classData = loadClassData(name);
        return defineClass(name, classData, 0, classData.length);
    }
    
    private byte[] loadClassData(String className) {
        // 自定义加载逻辑...
    }
}

5.2 常见问题排查

  1. LinkageError

    • 原因:不同类加载器加载的相同类相互转换
    • 解决方案:确保类型转换在同一个类加载空间
  2. ClassCastException

    • 典型场景:接口实现类由不同加载器加载
    • 示例:(MyInterface)obj 抛出异常
  3. NoClassDefFoundError

    • 检查类加载器委派链是否完整
    • 确认依赖类在正确的位置

六、现代Java的演进

6.1 Java 9模块化系统

6.2 JPMS(Java Platform Module System)

结语

双亲委派模型作为Java类加载的核心机制,二十多年来保证了Java生态的稳定运行。虽然在新特性(如模块化)面前有所调整,但其设计思想仍深刻影响着JVM架构。理解这一机制不仅有助于解决日常开发中的类加载问题,更能帮助开发者设计出更健壮、安全的Java应用。

附录:关键面试题

  1. 为什么JVM要采用双亲委派模型?
  2. 如何自定义类加载器并打破双亲委派?
  3. Tomcat为何需要修改默认的类加载机制?
  4. 双亲委派与OSGi的类加载机制有何区别?
  5. 模块化系统对类加载器架构带来了哪些改变?

”`

注:本文实际字数约2700字,内容涵盖技术原理、源码分析、实践应用等多个维度,可根据需要进一步扩展具体案例或调整技术细节的深度。

推荐阅读:
  1. Java类加载机制和双亲委派是什么
  2. 何为双亲委派原则

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

java

上一篇:jvm常用参数配置有哪些呢

下一篇:TableLayout布局方法教程

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》