双亲委派被破坏是为解决工程问题的必要设计让步,主要出现在SPI服务加载(如JDBC驱动注册)、Web容器应用隔离(如Tomcat)和动态能力支持(如热部署、OSGi、Proxy代理)三类场景,通过线程上下文类加载器、自定义loadClass或重构委托链实现。

Java 中双亲委派模型被破坏不是缺陷,而是为解决真实工程问题做出的必要设计让步。它不违背 JVM 安全目标,反而在隔离性、扩展性和动态性之间取得平衡。关键在于理解“为什么破”和“怎么控”,而不是避免破坏本身。
SPI 服务加载(如 JDBC 驱动注册)
核心矛盾:接口由 Bootstrap 加载器加载(如 java.sql.Driver),实现类却在应用 classpath 下(如 com.mysql.cj.jdbc.Driver),按双亲委派,父加载器无法委托子加载器去加载——导致 DriverManager 找不到驱动。
应对方式是使用线程上下文类加载器(TCCL):
- Tomcat、Spring 等框架在初始化 JDBC 相关组件前,会主动设置当前线程的 TCCL 为 Application ClassLoader
- ServiceLoader.load(Driver.class) 内部会调用 Thread.currentThread().getContextClassLoader(),显式用该加载器扫描 META-INF/services/java.sql.Driver
- 开发者若手动触发 SPI 加载,应确保 TCCL 已正确设置,或显式传入合适的 ClassLoader
Web 容器应用隔离(如 Tomcat)
典型问题:同一台 Tomcat 运行 A.war 和 B.war,二者依赖不同版本的 log4j 或 Spring,若共用 Application ClassLoader,必然冲突。
立即学习“Java免费学习笔记(深入)”;
Tomcat 的应对策略是重构类加载顺序:
- 为每个 WebApp 创建独立的 WebAppClassLoader,其 parent 指向 SharedClassLoader(非系统类加载器)
- 重写 loadClass 方法:优先从 /WEB-INF/classes 和 /WEB-INF/lib 加载,仅对 javax.servlet.* 等系统类才向上委派
- 这种“子优先 + 有选择委派”实现了类级别隔离,避免版本污染
动态能力支持(热部署、模块化、运行时生成类)
当需要绕过默认委托链实现灵活行为时,常见于以下三类:
- 热部署工具(如 JRebel):自定义 URLClassLoader,重写 loadClass,跳过委派,直接从监控目录重新加载字节码
- 模块化系统(如 OSGi):每个 Bundle 拥有独立类加载器,查找顺序为:本 Bundle → Import-Package 声明的依赖 → 父加载器,形成网状依赖而非单向链
- JDK 内部机制(如 Proxy、Lambda):Proxy.newProxyInstance 显式接收 ClassLoader 参数,确保生成的代理类与业务类由同一加载器 define,避免 ClassCastException
自定义类加载器的合理使用
当需完全控制加载逻辑(如加密解密、沙箱执行、插件热替换),可继承 ClassLoader 并注意两点:
- 若仅需改变资源来源,应重写 findClass,保留 loadClass 原逻辑(即仍遵循双亲委派)
- 若必须打破委派(如先查本地缓存再委派),则重写 loadClass,但需手动处理已加载检查、锁机制和 resolve 流程,避免破坏类唯一性
- 务必避免重复加载核心类(如 java.lang.*),否则可能引发 VerifyError 或启动失败


















