双亲委派机制通过类加载请求自底向上委派至启动类加载器,确保同一类仅由一个加载器加载,从而天然避免重复加载;其前提是严格遵守委托流程,不绕过super.loadclass,且统一关键类的加载入口。

Java 类加载器通过双亲委派机制天然防止重复加载同一个类——前提是严格遵守该机制。核心不是“阻止加载”,而是让所有请求最终由同一个父加载器完成,从而保证类的唯一性。
确保双亲委派不被绕过
只要每个类加载器在收到加载请求时,先委托父加载器尝试,且父加载器能成功加载,就不会出现重复。
- 检查自定义类加载器的
loadClass方法:必须调用super.loadClass(name, resolve),不能直接调用findClass或跳过委托 - 避免在代码中显式使用
new MyClassLoader().loadClass("X")加载业务类,尤其不在 Servlet、Filter 或静态块中创建新加载器 - 慎用
Class.forName(className, true, cl)指定非上下文/系统加载器;若必须指定,确保cl是统一的(如ClassLoader.getSystemClassLoader())
利用加载器缓存机制
每个类加载器内部维护已加载类的缓存(defineClass 后会注册)。同一加载器第二次请求相同类名时,直接返回已有 Class 对象,不重新解析字节码。
- 无需手动干预,这是 JVM 内置行为
- 但要注意:缓存是按“类名 + 加载器实例”绑定的,不同加载器即使加载相同字节码,也视为不同类
- 可通过
cl.loadClass("X").getClassLoader() == cl验证是否命中本加载器缓存
统一关键类的加载入口
对框架依赖的公共类(如 SPI 接口、序列化类型、模板引擎类),约定统一使用线程上下文类加载器(TCCL)或系统类加载器。
- 在应用启动早期(如 Spring 的
ApplicationContextInitializer)设置好 TCCL - 框架代码中优先使用
Thread.currentThread().getContextClassLoader()而非this.getClass().getClassLoader() - 对外工具类提供加载器参数选项,默认 fallback 到 TCCL,不硬编码当前类的加载器
构建阶段主动拦截重复依赖
编译和打包期就排除多版本同名类,从源头减少运行时冲突可能。
- Maven 项目加入
maven-enforcer-plugin+banDuplicateClasses规则,构建失败即告警 - 使用
mvn dependency:tree -Dverbose检查是否存在多个路径引入同一 artifact(如 slf4j-api 1.7.x 和 2.0.x 并存) - Web 应用中,将容器提供的 API(如 servlet-api.jar)设为
provided,避免打包进 WAR
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











