代码咖啡因的个人博客 代码咖啡因的个人博客

记录精彩的程序人生

目录
双亲委派模型
/  

双亲委派模型

双亲委派模型

双亲委派模型(Parent Delegation Model) 是 Java 类加载器(ClassLoader)的一种工作机制,它是 Java 类加载机制的核心设计之一。理解双亲委派模型对于掌握 Java 类加载机制、解决类冲突问题以及实现自定义类加载器都非常重要。


1. 什么是双亲委派模型?

双亲委派模型是指:当一个类加载器需要加载某个类时,它首先会委托给它的父类加载器去加载,只有在父类加载器无法加载时,才会自己尝试加载。

核心思想

  • 自上而下:类加载请求首先由最顶层的类加载器处理。
  • 自下而上:如果父类加载器无法加载,才会由子类加载器处理。

2. 类加载器的层次结构

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

  1. Bootstrap ClassLoader(启动类加载器)
    • 最顶层的类加载器。
    • 负责加载 JVM 核心类库(如 java.lang.*java.util.* 等),这些类通常位于 jre/lib/rt.jar 中。
    • 由 C/C++ 实现,是 JVM 的一部分。
  2. Extension ClassLoader(扩展类加载器)
    • 负责加载扩展类库(如jre/lib/ext 目录下的类)。
    • 是 Java 实现的类加载器。
  3. Application ClassLoader(应用程序类加载器)
    • 也称为系统类加载器(System ClassLoader)。
    • 负责加载应用程序类路径(Classpath)下的类。
    • 是 Java 实现的类加载器。
  4. Custom ClassLoader(自定义类加载器)
    • 用户自定义的类加载器。
    • 可以继承 ClassLoader 类,实现自定义的类加载逻辑。

3. 双亲委派的工作流程

当一个类加载器收到类加载请求时,它会按照以下步骤处理:

  1. 委托父类加载器
    • 首先检查是否已经加载过该类,如果已经加载,则直接返回。
    • 如果没有加载过,则将加载请求委托给父类加载器。
  2. 父类加载器处理
    • 父类加载器重复同样的过程,继续向上委托,直到到达 Bootstrap ClassLoader。
  3. Bootstrap ClassLoader 尝试加载
    • 如果 Bootstrap ClassLoader 能够加载该类,则返回该类。
    • 如果无法加载,则向下传递加载请求。
  4. 子类加载器尝试加载
    • 如果父类加载器无法加载该类,则由子类加载器尝试加载。
    • 如果子类加载器也无法加载,则抛出ClassNotFoundException

4. 双亲委派的代码实现

双亲委派模型的实现主要在ClassLoader 类的 loadClass 方法中。以下是简化后的逻辑:

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
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // 父类加载器无法加载,忽略异常
            }

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

5. 双亲委派模型的优点

  1. 避免重复加载
    • 父类加载器已经加载的类,子类加载器不会重复加载,确保类的唯一性。
  2. 安全性
    • 防止核心类库被篡改。例如,用户自定义的java.lang.String 类不会被加载,因为 Bootstrap ClassLoader 已经加载了核心的 String 类。
  3. 类的隔离性
    • 不同类加载器加载的类相互隔离,避免类冲突。

6. 双亲委派模型的缺点

  1. 灵活性不足
    • 在某些场景下,双亲委派模型可能限制了类的加载方式。例如,某些框架(如 Tomcat)需要打破双亲委派模型,以实现类加载的隔离和动态加载。
  2. 无法加载非标准类
    • 如果父类加载器无法加载某个类,子类加载器也无法加载,即使子类加载器能够找到该类。

7. 打破双亲委派模型

在某些场景下,需要打破双亲委派模型。例如:

  • Tomcat 的类加载器
    • Tomcat 使用自定义的类加载器,优先加载 Web 应用中的类,而不是委托给父类加载器。
  • OSGi 框架
    • OSGi 使用复杂的类加载机制,允许模块之间的类隔离和动态加载。

如何打破双亲委派模型?

  • 重写ClassLoaderloadClass 方法,改变类的加载顺序。
  • 例如,优先加载自己的类,而不是委托给父类加载器。

8. 双亲委派模型的实际应用

  1. JDBC 驱动的加载
    • JDBC 驱动的加载使用了双亲委派模型。DriverManager 会通过 ServiceLoader 加载所有可用的驱动类。
  2. Spring 的类加载
    • Spring 使用自定义的类加载器,优先加载应用中的类,而不是委托给父类加载器。

9. 总结

  • 双亲委派模型 是 Java 类加载机制的核心设计,通过自上而下的委托机制,确保类的唯一性和安全性。
  • 优点:避免重复加载、提高安全性、隔离类加载环境。
  • 缺点:灵活性不足,某些场景需要打破双亲委派模型。
  • 实际应用:广泛应用于 JDBC、Tomcat、Spring 等框架中。