双亲委派模型:为什么要这样设计?

引言 为什么要学习这个主题? 在上一篇文章中,我们了解了JVM的三层类加载器体系。但你是否想过: 为什么要设计"父子"关系?直接加载不行吗? 如果我自己写一个java.lang.String类会发生什么? 双亲委派模型是如何保证Java核心类库不被篡改的? 双亲委派模型是类加载机制的核心,也是JVM安全性的基础。理解它,就能理解Java为什么是一个安全的平台。 你将学到什么? ✅ 双亲委派模型的完整工作流程 ✅ 为什么要设计双亲委派模型 ✅ 双亲委派模型带来的好处 ✅ 如何验证双亲委派模型 ✅ 双亲委派模型的局限性 一、什么是双亲委派模型? 1.1 定义 当一个类加载器收到类加载请求时,它首先不会自己尝试加载,而是把这个请求委派给父加载器完成。只有当父加载器无法完成加载时,子加载器才会尝试自己加载。 1.2 工作流程 应用类加载器收到加载请求:加载 com.example.User ↓ 委派给父加载器 扩展类加载器尝试加载 com.example.User ↓ 委派给父加载器 启动类加载器尝试加载 com.example.User ↓ 在核心类库中找不到 ✗ 返回"无法加载" ↓ 扩展类加载器尝试自己加载 ↓ 在扩展库中找不到 ✗ 返回"无法加载" ↓ 应用类加载器尝试自己加载 ↓ 在classpath中找到 ✓ 加载成功 1.3 流程图 ┌────────────────────────────────────────────┐ │ 1. 应用类加载器收到请求 │ │ "加载 com.example.User" │ └──────────────┬─────────────────────────────┘ │ 委派给父加载器 ┌──────────────▼─────────────────────────────┐ │ 2. 扩展类加载器收到请求 │ │ 检查:是否在 lib/ext 中? │ └──────────────┬─────────────────────────────┘ │ 委派给父加载器 ┌──────────────▼─────────────────────────────┐ │ 3. 启动类加载器收到请求 │ │ 检查:是否在 rt.jar 中? │ │ 结果:✗ 没找到 │ └──────────────┬─────────────────────────────┘ │ 返回失败 ┌──────────────▼─────────────────────────────┐ │ 4. 扩展类加载器尝试加载 │ │ 在 lib/ext 中查找 │ │ 结果:✗ 没找到 │ └──────────────┬─────────────────────────────┘ │ 返回失败 ┌──────────────▼─────────────────────────────┐ │ 5. 应用类加载器尝试加载 │ │ 在 classpath 中查找 │ │ 结果:✓ 找到并加载 │ └────────────────────────────────────────────┘ 二、双亲委派模型的源码实现 2.1 ClassLoader.loadClass() 源码 public abstract class ClassLoader { 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. 如果没有父加载器,委派给启动类加载器 c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // 父加载器无法加载,不做处理 } if (c == null) { // 4. 父加载器无法加载时,调用自己的findClass c = findClass(name); } } // 5. 如果需要,解析类 if (resolve) { resolveClass(c); } return c; } } // 子类需要重写这个方法 protected Class<?> findClass(String name) throws ClassNotFoundException { throw new ClassNotFoundException(name); } } 2.2 关键步骤解析 步骤1:检查缓存 ...

2025-11-20 · maneng

类加载器家族:启动、扩展、应用类加载器

引言 为什么要学习这个主题? 在上一篇文章中,我们了解了类加载的完整生命周期。但你是否想过: JVM中有多少个类加载器? 为什么要设计多个类加载器? java.lang.String和我们自己写的类是由同一个加载器加载的吗? 理解类加载器的层次结构,就像理解一个组织的部门分工。不同的类加载器负责加载不同来源的类,这种设计既保证了安全性,又提供了灵活性。 你将学到什么? ✅ JVM的三层类加载器体系 ✅ 每个类加载器的职责和加载路径 ✅ 类加载器的父子关系(委派关系) ✅ 如何查看类是由哪个加载器加载的 ✅ 线程上下文类加载器 一、类加载器的层次结构 1.1 三层体系 ┌────────────────────────────────────────┐ │ 启动类加载器 │ Bootstrap ClassLoader │ (Bootstrap ClassLoader) │ C++实现,加载核心类库 │ 加载:rt.jar、核心API │ └──────────────┬─────────────────────────┘ │ 父加载器 ┌──────────────▼─────────────────────────┐ │ 扩展类加载器 │ Extension ClassLoader │ (Extension ClassLoader) │ Java实现,加载扩展库 │ 加载:lib/ext/*.jar │ └──────────────┬─────────────────────────┘ │ 父加载器 ┌──────────────▼─────────────────────────┐ │ 应用类加载器 │ Application ClassLoader │ (Application ClassLoader) │ Java实现,加载应用类 │ 加载:classpath下的类 │ └────────────────────────────────────────┘ │ 父加载器(可自定义) ┌──────────────▼─────────────────────────┐ │ 自定义类加载器 │ User ClassLoader │ (Custom ClassLoader) │ 继承ClassLoader │ 加载:自定义路径的类 │ └────────────────────────────────────────┘ 关键理解: ...

2025-11-20 · maneng

JVM架构全景图:五大核心组件详解

引言 为什么要学习这个主题? 在前两篇文章中,我们知道了Java程序如何运行,以及JVM的本质。但JVM内部到底是怎么工作的? 想象一下: 一个.class文件是如何被加载到JVM中的? 对象和变量存储在哪里? 字节码是如何被"翻译"成机器码的? 理解JVM的架构,就像理解一台计算机的组成(CPU、内存、硬盘)一样重要。这是后续学习类加载、内存管理、GC调优的基础。 你将学到什么? ✅ JVM的整体架构图 ✅ 五大核心组件的职责 ✅ 各组件如何协作运行Java程序 ✅ JVM的完整执行流程 一、JVM架构全景图 1.1 整体架构 ┌─────────────────────────────────────────────────────────────┐ │ Java应用程序 │ │ (.java → .class) │ └──────────────────────┬──────────────────────────────────────┘ │ 字节码 ↓ ┌─────────────────────────────────────────────────────────────┐ │ JVM(Java虚拟机) │ │ │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ 1️⃣ 类加载子系统 (Class Loader Subsystem) │ │ │ │ - 加载 (Loading) │ │ │ │ - 链接 (Linking): 验证、准备、解析 │ │ │ │ - 初始化 (Initialization) │ │ │ └────────────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ 2️⃣ 运行时数据区 (Runtime Data Areas) │ │ │ │ │ │ │ │ 【线程共享】 【线程私有】 │ │ │ │ - 堆 (Heap) - 程序计数器 (PC Register)│ │ │ │ - 方法区 (Method Area) - 虚拟机栈 (VM Stack) │ │ │ │ - 本地方法栈 (Native Stack)│ │ │ └────────────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ 3️⃣ 执行引擎 (Execution Engine) │ │ │ │ - 解释器 (Interpreter) │ │ │ │ - JIT编译器 (Just-In-Time Compiler) │ │ │ │ - 垃圾收集器 (Garbage Collector) │ │ │ └────────────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ 4️⃣ 本地接口 (Native Interface - JNI) │ │ │ └────────────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ 5️⃣ 本地方法库 (Native Method Libraries) │ │ │ └────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────┐ │ 操作系统 & 硬件 │ └─────────────────────────────────────────────────────────────┘ 1.2 核心组件概览 组件 作用 类比 类加载子系统 加载、链接、初始化类 快递员(把包裹送到仓库) 运行时数据区 存储数据(对象、变量、代码) 内存/仓库 执行引擎 执行字节码 CPU(执行指令) 本地接口 调用操作系统API 操作系统接口 垃圾收集器 自动回收无用对象 清洁工(清理垃圾) 二、类加载子系统(Class Loader Subsystem) 2.1 职责 负责将.class文件加载到JVM内存中,并准备好供使用。 ...

2025-11-20 · maneng

如约数科科技工作室

浙ICP备2025203501号

👀 本站总访问量 ...| 👤 访客数 ...| 📅 今日访问 ...