它们都不是完全抛弃双亲委派机制,而是在特定场景下“打破”或“绕过”了部分双亲委派流程,以满足各自的运行需求。
🔍 先回顾:双亲委派机制
- 标准流程:类加载请求先委托给父加载器,父加载器找不到才由自己加载
- 好处:安全(防止核心类被篡改)、避免重复加载、类隔离
🛠 WebAppClassLoader(Tomcat)
- 用途:隔离不同 Web 应用的类,支持热部署
- 破坏点:
- Tomcat 的
WebAppClassLoader
在加载类时,会先尝试自己加载(尤其是WEB-INF/classes
和WEB-INF/lib
下的类),再委托父加载器 - 这样可以让 Web 应用自己的类覆盖容器或 JDK 提供的同名类(除了一些受保护的包)
- Tomcat 的
- 原因:
- 支持应用自带依赖版本
- 实现应用之间的类隔离
- 支持热部署(重新加载类)
🛠 LaunchedURLClassLoader(Spring Boot)
- 用途:支持 Spring Boot 可执行 JAR(fat jar)结构,从
BOOT-INF/classes
和BOOT-INF/lib
中加载类 - 破坏点:
- 它继承自
URLClassLoader
,但加载逻辑会优先从自身的 URL 列表(fat jar 内部路径)找类,而不是严格先委托父加载器 - 这样可以让应用打包在 JAR 内的类优先于外部类加载
- 它继承自
- 原因:
- 让 Spring Boot 内嵌依赖优先加载
- 保证 fat jar 内部的类路径结构可直接运行
- 避免依赖冲突
📌 总结对比
类加载器 | 是否完全跳过双亲委派 | 破坏方式 | 目的 |
---|---|---|---|
WebAppClassLoader | ❌(部分破坏) | 部分包优先自己加载 | 应用隔离、热部署 |
LaunchedURLClassLoader | ❌(部分破坏) | 优先加载 fat jar 内部类 | 支持可执行 JAR、自带依赖优先 |
💡 一句话总结
它们都保留了双亲委派的大框架,但在类查找顺序上做了“反转”或“短路”,以满足类隔离、热部署、可执行 JAR等特殊需求。
🎨 类加载流程对比图
🟦 标准双亲委派机制(父优先)
[请求加载类]
⬇️ 委托父加载器
⬇️ 父找到 → ✅ 返回类
⬇️ 父找不到 → 自己加载
🟩 WebAppClassLoader(Tomcat,子优先)
[请求加载类]
⬇️ 先检查自己(WEB-INF/classes, WEB-INF/lib)
⬇️ 找到 → ✅ 返回类
⬇️ 找不到 → 委托父加载器
(部分包仍父优先,保护核心类)
🟨 LaunchedURLClassLoader(Spring Boot,子优先)
[请求加载类]
⬇️ 先检查自己(BOOT-INF/classes, BOOT-INF/lib)
⬇️ 找到 → ✅ 返回类
⬇️ 找不到 → 委托父加载器
(支持 Fat JAR 结构,内嵌依赖优先)
🔍 关键差异
- 标准双亲委派:父优先,安全性高,避免重复加载
- WebAppClassLoader:对子类路径(
WEB-INF/classes
、WEB-INF/lib
)优先加载,实现应用隔离和热部署 - LaunchedURLClassLoader:对子类路径(
BOOT-INF/classes
、BOOT-INF/lib
)优先加载,支持 Spring Boot 可执行 JAR