Wrayの知识库 Wrayの知识库
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • 计算机基础
  • 操作系统原理
  • Linux
  • MacOS
  • Windows
  • 系统工程与研究专题
  • AI 基础
  • 大模型基础
  • Prompt 工程
  • RAG 检索增强生成
  • Agent 智能体
  • AI 应用开发
  • AI 工程化
  • AI 安全与治理
  • AI 面试与设计题
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • 计算机基础
  • 操作系统原理
  • Linux
  • MacOS
  • Windows
  • 系统工程与研究专题
  • AI 基础
  • 大模型基础
  • Prompt 工程
  • RAG 检索增强生成
  • Agent 智能体
  • AI 应用开发
  • AI 工程化
  • AI 安全与治理
  • AI 面试与设计题
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
  • Java章节编写规范
  • Java基础

  • Java集合

  • Java并发

  • Java IO

  • JVM

    • JVM概述
    • JVM基础

    • 类加载机制

      • 类的生命周期
      • 类加载器
      • 双亲委派模型
        • 1. 工作流程
        • 2. 为什么需要双亲委派
          • 2.1 避免核心类被篡改
          • 2.2 避免类重复加载
        • 3. loadClass 的基本逻辑
        • 4. 双亲委派不是强制规范
        • 5. Tomcat 为什么要打破双亲委派
        • 6. 常见问题
        • 7. 双亲委派流程图
        • 8. 打破双亲委派的典型模式
        • 9. 安全边界
        • 10. 开发中常见问题
          • 10.1 为什么依赖冲突会运行时报错
          • 10.2 为什么容器里同一个 jar 放错位置会出问题
        • 11. Tips 快问快答
        • 12. 总结
      • 类初始化顺序
      • 类加载问题与SPI
    • 运行时内存

    • 垃圾回收

    • 执行引擎与优化

    • 工具与问题排查

    • 调优实践与面试

目录

双亲委派模型

双亲委派模型是 Java 类加载机制的核心原则之一。它规定类加载器收到加载请求后,先把请求交给父加载器处理,父加载器无法加载时,当前加载器才尝试加载。

# 1. 工作流程

类加载请求的大致流程:

应用类加载器 -> 平台类加载器 -> 启动类加载器

加载时先向上委派,真正加载时再从上往下尝试。

先问父亲能不能加载
父亲不能加载
自己再加载

# 2. 为什么需要双亲委派

双亲委派有两个核心价值:

# 2.1 避免核心类被篡改

如果用户自己写一个类:

package java.lang;

public class String {
}

双亲委派会优先让启动类加载器加载 JDK 自带的 java.lang.String,避免核心类库被替换。

# 2.2 避免类重复加载

如果父加载器已经加载过某个类,子加载器就不需要重复加载。

这保证了核心类在 JVM 中的唯一性。

# 3. loadClass 的基本逻辑

ClassLoader.loadClass 的典型逻辑:

  1. 检查类是否已经加载。
  2. 委派给父加载器。
  3. 父加载失败后调用 findClass。
  4. 必要时进行链接。

因此自定义类加载器通常重写 findClass,不要轻易重写 loadClass。

# 4. 双亲委派不是强制规范

双亲委派是推荐模型,不是 JVM 强制所有类加载器必须遵守的规则。

一些框架和容器会有意打破或调整双亲委派。

常见场景:

  • JDBC SPI。
  • JNDI。
  • OSGi。
  • Tomcat Web 应用类加载。
  • 插件系统。

# 5. Tomcat 为什么要打破双亲委派

Tomcat 需要实现 Web 应用隔离。

不同 Web 应用可能依赖不同版本的同一个类:

app1: commons-lang 2.x
app2: commons-lang 3.x

如果完全遵守双亲委派,公共父加载器加载后,多个应用可能无法隔离依赖。因此容器会设计自己的类加载策略。

# 6. 常见问题

  • ClassNotFoundException:类加载器找不到类。
  • NoClassDefFoundError:编译时存在,运行时加载失败。
  • ClassCastException: A cannot be cast to A:同名类由不同类加载器加载。
  • LinkageError:类链接阶段出现冲突。

# 7. 双亲委派流程图

应用类加载器收到加载请求
        │
        ▼
是否已经加载过?
        │
  ┌─────┴─────┐
  │是         │否
  ▼           ▼
返回 Class    委派给父加载器
              │
              ▼
        父加载器能加载?
              │
       ┌──────┴──────┐
       │能           │不能
       ▼             ▼
   返回父加载结果   当前加载器 findClass

这个流程优先保证父加载器的类优先级,因此 Java 核心类库不会被应用类随意替换。

# 8. 打破双亲委派的典型模式

双亲委派不是绝对不能破坏,关键是为什么破坏。

场景 为什么需要调整
SPI 父层接口需要加载子层实现
Tomcat 不同 Web 应用需要依赖隔离
OSGi 模块之间需要精细化导入导出
插件系统 插件依赖不能污染主应用
热部署 新旧版本类需要隔离

破坏双亲委派不是目的,类隔离和扩展能力才是目的。

# 9. 安全边界

双亲委派的安全意义可以用 java.lang.String 举例。

如果没有双亲委派,应用 classpath 中伪造一个:

package java.lang;

public class String {
}

可能会尝试替换核心类。双亲委派让启动类加载器优先加载 JDK 自带 String,避免基础类型体系被破坏。

# 10. 开发中常见问题

# 10.1 为什么依赖冲突会运行时报错

编译和运行加载到的类版本不同,可能导致:

  • NoSuchMethodError
  • NoSuchFieldError
  • LinkageError

排查时要看最终运行时 classpath,而不是只看 IDE 中能否编译。

# 10.2 为什么容器里同一个 jar 放错位置会出问题

如果 jar 放在容器公共 lib 下,可能由父加载器加载,所有应用共享;放在 Web 应用内,则由应用类加载器加载。位置不同,类隔离和版本冲突表现完全不同。

# 11. Tips 快问快答

Q:双亲委派中的“父”是父类吗?

A:不是,是父类加载器,不是 Java 继承意义上的父类。

Q:为什么自定义类加载器一般重写 findClass?

A:loadClass 默认包含双亲委派逻辑,重写 findClass 可以复用默认委派流程。

Q:Tomcat 完全不使用双亲委派吗?

A:不是。Tomcat 有自己的类加载策略,在 Web 应用类加载上做了调整,但不是所有加载都完全反过来。

Q:双亲委派能解决所有类冲突吗?

A:不能。它保证核心类优先和部分唯一性,但复杂应用仍可能有依赖版本冲突。

# 12. 总结

双亲委派保证了核心类库安全和类加载稳定性,但在 SPI、容器和插件化场景中会被有意识地调整。理解它不是为了死记规则,而是为了能解释类冲突和框架类加载行为。

上次更新: 2026/06/24, 16:03:01
类加载器
类初始化顺序

← 类加载器 类初始化顺序→

Copyright © 2023-2026 Wray | 鄂ICP备2024050235号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式