双亲委派机制不可破坏但可合理扩展,通过重写loadClass方法跳过父委托、直接调用findClass实现自定义加载,或利用TCCL间接绕过;Web容器如Tomcat即按此原则优先加载应用类并委派核心类。

双亲委派机制不能也不该被“破坏”,但可以通过自定义类加载器绕过它——这不是破坏,而是合理扩展。Java 的 ClassLoader 默认遵循双亲委派:先委托父加载器尝试加载,父无法加载时子才自己加载。要让类由当前类加载器优先加载(比如热替换、隔离插件),就得**主动不委托父加载器**。
重写 loadClass 方法,跳过父委托
核心是复写 loadClass(String name, boolean resolve),不调用 super.loadClass() 或 parent.loadClass(),而是直接调用 findClass(name):
public class MyClassLoader extends ClassLoader {
private String baseDir; // 自定义路径
public MyClassLoader(String baseDir, ClassLoader parent) {
super(parent); // 仍可传入父加载器,但后续不委托
this.baseDir = baseDir;
}
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
// ? 关键:不调用 super.loadClass(),也不调用 parent.loadClass()
// 先尝试自己加载(例如从 baseDir 加载)
Class<?> clazz = findLoadedClass(name);
if (clazz == null) {
try {
byte[] bytes = loadClassBytes(name); // 自行读取 .class 文件
clazz = defineClass(name, bytes, 0, bytes.length);
} catch (IOException e) {
throw new ClassNotFoundException(name, e);
}
}
if (resolve && clazz != null) {
resolveClass(clazz);
}
return clazz;
}
private byte[] loadClassBytes(String name) throws IOException {
String path = baseDir + "/" + name.replace('.', '/') + ".class";
return Files.readAllBytes(Paths.get(path));
}
}
使用线程上下文类加载器(TCCL)间接绕过
很多框架(如 JDBC、JNDI)依赖 TCCL 实现“父加载器需加载子加载器可见的类”。这是标准用法,不是 hack:
- 主线程默认 TCCL 是 AppClassLoader
- 启动时显式设置:
Thread.currentThread().setContextClassLoader(new MyClassLoader(...)); - JDBC DriverManager 会用 TCCL 加载驱动类(如
com.mysql.cj.jdbc.Driver),从而让 Web 应用用自己的类加载器加载驱动,避免容器类加载器冲突
注意:不要重写 findClass 去委托父类
findClass 本意就是留给子类实现“如何查找并定义类”,它**不负责委派**。常见错误是这样写:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
立即学习“Java免费学习笔记(深入)”;
@Override
protected Class<?> findClass(String name) {
return getParent().loadClass(name); // ❌ 错!这反而强化了委派,且可能死循环
}
正确做法是:在 findClass 中完成字节码读取 + defineClass,把加载逻辑真正收归自己。
典型场景:Web 容器中的应用类加载器
Tomcat 的 WebAppClassLoader 就是经典实践:
- 对
/WEB-INF/classes和/WEB-INF/lib/*.jar中的类,优先自己加载(打破委派) - 对
java.*、javax.*等核心包,仍强制委派给 Bootstrap/AppClassLoader(安全与兼容) - 通过
getResourceAsStream和findClass组合控制加载顺序,不是全盘否定委派

















