核心是重写loadClass方法跳过super.loadClass调用,自主判断包名(如com.example.hotswap.*)后直接findClass→defineClass加载,其余类可抛ClassNotFoundException,从而彻底绕过双亲委派。

要让自定义类加载器打破双亲委派机制,核心是在重写 loadClass 方法时,**不调用父类的 loadClass(即跳过双亲委派逻辑)**,而是直接用自己的逻辑去加载类(比如从指定路径、网络、加密字节码等位置加载),并在必要时调用 defineClass 完成类的定义。
重写 loadClass 方法,绕过父类委派
默认的 ClassLoader.loadClass 实现会先委托给父加载器,找不到才自己加载。要打破它,就不能沿用默认流程:
- 不要调用
super.loadClass(name)或getParent().loadClass(name) - 直接在自己的方法里判断是否该由本加载器加载(例如根据包名、前缀、配置规则)
- 若匹配,则调用
findClass(name)→ 自定义字节码获取逻辑 →defineClass - 若不匹配,可选择抛异常、返回 null,或按需委托(此时是“有选择地委派”,而非强制双亲委派)
典型实现示例(仅加载特定包下的类)
比如只加载 com.example.hotswap.* 下的类,其余一律不处理:
public class HotSwapClassLoader extends ClassLoader {
private final String classPath;
public HotSwapClassLoader(String classPath, ClassLoader parent) {
super(parent); // 仍可持有 parent,但不强制委派
this.classPath = classPath;
}
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
// 【关键】不走 super.loadClass,完全自主控制
if (name.startsWith("com.example.hotswap.")) {
return findClass(name);
}
// 其他类:可以选择抛异常、返回 null,或显式委派给 parent(非必须)
throw new ClassNotFoundException(name + " not supported by this loader");
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] bytes = loadClassBytes(name); // 自定义读取字节码(如从文件、jar、内存等)
if (bytes == null) {
throw new ClassNotFoundException(name);
}
return defineClass(name, bytes, 0, bytes.length);
}
private byte[] loadClassBytes(String name) {
String path = classPath + "/" + name.replace('.', '/') + ".class";
try (InputStream is = new FileInputStream(path)) {
return is.readAllBytes();
} catch (IOException e) {
return null;
}
}
}
注意 defineClass 的安全与可见性
defineClass 是受保护方法,子类可调用,但要注意:
立即学习“Java免费学习笔记(深入)”;
- 它不会自动链接(linking),如需立即验证、准备、初始化,需配合
resolveClass - 若类已由其他加载器定义过(相同全限定名 + 不同加载器),JVM 会拒绝重复定义,抛
LinkageError - 确保字节码格式合法,否则抛
ClassFormatError - 如果涉及模块系统(Java 9+),还需考虑
ModuleLayer和defineClass的模块归属问题
常见用途和风险提醒
打破双亲委派常用于:
- 热部署/热替换(如 Spring Boot DevTools、OSGi、JRebel)
- 隔离插件或沙箱环境(不同插件用不同加载器,避免类冲突)
- 动态加载加密或远程字节码(需解密后再 defineClass)
但需警惕:
- 破坏了 JVM 类型一致性基础,容易引发
NoClassDefFoundError、ClassCastException(同一类被多个加载器加载,视为不同类型) - 可能绕过安全管理器或模块约束,带来安全隐患
- 线程上下文类加载器(TCCL)被误设时,可能导致框架(如 JNDI、JAX-WS)行为异常


















