双亲委派模型:为什么要这样设计?
引言 为什么要学习这个主题? 在上一篇文章中,我们了解了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:检查缓存 ...