Java破坏双亲委派的典型实现核心是让子类加载器优先自己加载类,而非先委托父加载器;最经典案例是Tomcat的WebAppClassLoader(本地优先加载)和JDBC通过TCCL实现父加载器调用子加载器加载驱动。

Java 破坏双亲委派的典型实现,核心在于让子类加载器**优先自己加载类,而不是先委托给父加载器**。最经典、最常被引用的案例就是 Tomcat 的 WebAppClassLoader 和 JDBC 的 DriverManager + ServiceLoader 机制(尤其是 com.mysql.cj.jdbc.Driver 在 JDK 6+ 中的自动注册)。下面以两个真正落地、可理解、可复现的经典方式展开:
1. 自定义 ClassLoader 覆盖 loadClass 方法(最直接)
标准双亲委派逻辑在 ClassLoader.loadClass() 中体现:先查缓存 → 再委托父类 → 最后自己 findClass。要破坏它,只需重写该方法,把“自己先尝试加载”提前到委托之前。
示例代码(简化版):
public class MyClassLoader extends ClassLoader {
private String classPath;
public MyClassLoader(String classPath) {
this.classPath = classPath;
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
// ✅ 关键:不走 super.loadClass,跳过双亲委派
// 先尝试自己加载(比如只加载特定包下的类)
if (name.startsWith("com.example.mypackage.")) {
return findClass(name);
}
// ❌ 否则才走默认委派(也可改成完全不委派)
return super.loadClass(name);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] data = loadByteCodeFromPath(name);
return defineClass(name, data, 0, data.length);
}
private byte[] loadByteCodeFromPath(String name) {
// 从指定路径读取 .class 文件字节(略)
return new byte[0];
}
}
这样,当调用 myClassLoader.loadClass("com.example.mypackage.MyService") 时,就会绕过 AppClassLoader 和 BootstrapClassLoader,直接由自定义类加载器加载——这就是对双亲委派的明确破坏。
立即学习“Java免费学习笔记(深入)”;
2. JDBC 驱动加载(真实世界中最隐蔽的破坏)
JDK 1.6+ 引入 ServiceLoader,DriverManager 在静态块中调用 loadInitialDrivers(),其中会通过 当前线程上下文类加载器(Thread Context ClassLoader, TCCL) 去加载 META-INF/services/java.sql.Driver 中声明的驱动类。
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
关键点:
- Web 容器(如 Tomcat)启动时,会把
WebAppClassLoader设置为线程上下文类加载器 -
DriverManager是 Bootstrap 类(由启动类加载器加载),但它却用 TCCL(即 Web 应用自己的类加载器)去加载驱动类 - 这就导致:本该由 AppClassLoader 加载的
mysql-connector-java.jar中的Driver,实际由 WebAppClassLoader 加载 —— 父加载器(Bootstrap)反而依赖子加载器的结果
这种“父委托子”的行为,是典型的、被 JVM 官方默许的双亲委派破坏,目的是支持插件化和应用隔离。
3. Tomcat 的 WebAppClassLoader(生产级实践)
Tomcat 为每个 Web 应用创建独立的 WebAppClassLoader,其设计明确打破双亲委派:
- 优先从
WEB-INF/classes和WEB-INF/lib/加载类(即自己先找) - 仅对
java.*、javax.*、org.apache.catalina.*等少数包才委托给父加载器(SharedClassLoader / CommonClassLoader) - 避免不同应用间的类冲突,也防止 Web 应用覆盖 JRE 核心类
这并非 bug,而是容器为实现“应用隔离”做的主动设计性破坏。
注意事项
破坏双亲委派不是随意而为,需满足明确目的:
- 热部署、模块化(OSGi)、插件系统需要类隔离
- 运行时动态加载用户代码(如脚本引擎、规则引擎)
- 避免类版本冲突(如不同应用依赖不同版本的 Log4j)
- 务必小心类加载泄漏(尤其在 Web 容器中未正确 stop classloader)
不推荐无理由覆盖 loadClass;更安全的做法是仅重写 findClass 并配合 TCCL 或显式使用特定 ClassLoader 实例。

















