双亲委派模型是设计约定而非强制规范,核心是类加载器先委托父加载器加载,父无法加载时才调用自身findClass();合法自定义逻辑仅限重写findClass()或谨慎重写loadClass(),其余位置均不可插入。

双亲委派模型不是强制规范,而是一种设计约定。它的核心逻辑是:每个类加载器在尝试加载类时,先不自己动手,而是把请求“向上委托”给父加载器;只有当父加载器无法完成(即 findClass() 返回 null),才由当前加载器真正去查找并定义类。
标准双亲委派流程(自底向上)
以一个自定义的 MyClassLoader(继承 ClassLoader)尝试加载 com.example.Service 为例,手绘逻辑可简化为一条垂直链:
MyClassLoader
↓(调用 parent.loadClass())
AppClassLoader(系统类加载器)
↓(调用 parent.loadClass())
ExtClassLoader(扩展类加载器)
↓(调用 parent.loadClass())
BootstrapClassLoader(启动类加载器,C++ 实现,Java 层为 null)
→ 若找到,直接返回 Class;否则逐级向下回退
回退路径:Bootstrap → Ext → App → MyClassLoader(此时才真正执行 findClass("com.example.Service"))
自定义加载逻辑的合法插入点
不能破坏委派链条本身,但可在两个位置安全注入逻辑:
-
重写
findClass(String name):这是标准且推荐的位置。父加载器全部失败后,JVM 会调用当前加载器的findClass()。你在这里解析字节码(如从网络、加密文件、数据库读取),再调用defineClass()返回Class对象。不碰loadClass(),不干扰委派。 -
重写
loadClass(String name, boolean resolve),但慎用:若需绕过委派(如热替换、模块隔离),可在此方法中提前拦截特定类名,直接调用findClass();其余情况仍应显式调用super.loadClass()保持委派。跳过父委托属于“破坏双亲委派”,仅限特殊场景(如 OSGi、Tomcat 的 WebAppClassLoader)。
哪些位置 不能 插入自定义逻辑
- 不要在
ClassLoader构造器里加载类 —— 此时委派链尚未建立,易引发NoClassDefFoundError。 - 不要重写
defineClass()或resolveClass()来实现加载逻辑 —— 它们是 JVM 验证和链接阶段的底层方法,非加载入口。 - 不要在
getResource()或getResources()中隐式触发类加载 —— 这些方法不参与委派,且可能绕过安全检查。
一句话总结关键位置
标准做法是守住 findClass();破例做法是在 loadClass() 中做有意识的拦截 —— 前者守规矩,后者担风险,二者都发生在委派链最末端(即当前加载器真正出手的地方)。

















