双亲委派模型

双亲委派模型

双亲委派模型(Parent Delegation Model)是 Java 类加载器(ClassLoader)的核心设计模式,用于​保证类加载的安全性和唯一性,避免类重复加载和核心类被篡改。

一、核心原理

当一个类加载器收到类加载请求时,它不会立即自己加载,而是​先委派给父类加载器,逐层向上传递请求;只有当父类加载器无法加载该类(在其搜索范围内找不到)时,子类加载器才会尝试自己加载。

简单来说:​先找爹,爹不行,自己上

二、类加载器层级结构

Java 中的类加载器按层级从上到下分为:

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

    • 最顶层,由 C++ 实现(非 Java 类),负责加载$JAVA_HOME/jre/lib/rt.jar​(核心类库,如java.lang.String)。
    • 无父类加载器,也无法通过 Java 代码直接引用。
  2. 扩展类加载器(Extension ClassLoader)

    • 父类是启动类加载器,负责加载$JAVA_HOME/jre/lib/ext/*.jar(扩展类库)。
    • sun.misc.Launcher$ExtClassLoader实现。
  3. 应用程序类加载器(Application ClassLoader)

    • 父类是扩展类加载器,负责加载用户类路径(ClassPath)下的类(项目代码、第三方依赖)。
    • sun.misc.Launcher$AppClassLoader实现,也是默认的系统类加载器。
  4. 自定义类加载器(Custom ClassLoader)

    • 开发者继承java.lang.ClassLoader实现,用于加载自定义路径的类(如 Tomcat 的 WebAppClassLoader)。

三、工作流程

以加载com.example.Test类为例:

  1. 应用程序类加载器收到请求,先委派给父类​扩展类加载器
  2. 扩展类加载器再委派给父类​启动类加载器
  3. 启动类加载器检查核心类库,发现没有com.example.Test,返回 “无法加载”。
  4. 扩展类加载器检查扩展类库,也没有,返回 “无法加载”。
  5. 应用程序类加载器ClassPath​下查找并加载com.example.Test类。

四、优势

  1. 安全性​:核心类(如java.lang.String)只能由启动类加载器加载,避免用户自定义同名类篡改核心 API(防止 “String 类被替换” 等安全问题)。
  2. 唯一性:同一个类只会被加载一次(由最顶层的类加载器加载),保证类的全局唯一性。
  3. 层级隔离:不同层级的类加载器负责不同范围的类,避免类冲突。

五、打破双亲委派模型的场景

并非所有场景都严格遵循双亲委派,以下情况会 “反向委派” 或直接加载:

  1. SPI 机制(Service Provider Interface) 例如 JDBC 驱动(java.sql.Driver​):核心类Driver​由启动类加载器加载,但具体驱动实现(如 MySQL 驱动)在ClassPath​下,需由应用程序类加载器加载 → 启动类加载器会委派子类加载器加载(线程上下文类加载器实现)。
  2. Tomcat 等容器Tomcat 为每个 Web 应用创建独立的类加载器(WebAppClassLoader),优先加载应用内的类(而非委派父类),避免不同应用的类冲突。
  3. 热部署 / 动态加载自定义类加载器需实时加载新类,可能绕过双亲委派直接加载。
上一篇
下一篇