必须重写loadclass以控制委托边界:先按包名白名单判断是否本地加载(如com.myapp.module.v2.),是则调findclass;否则立即super.loadclass委派,尤其java.等系统类不可自行加载,否则引发linkageerror或jvm崩溃。

直接重写 loadClass 是打破双亲委派的必要动作,但关键不在“重写”本身,而在重写时如何控制委托边界——既要让业务类由当前加载器独占加载,又要确保系统类(如 java.lang.*)仍能安全委派给父加载器。否则要么隔离失效,要么 JVM 崩溃。
必须在 loadClass 开头做包名判断
不能无差别跳过父委派,而是先检查类名是否属于应由自己加载的范围(比如 com.myapp.module.v2.),再决定是否调用 findClass;其余情况(尤其是 java.、javax.、sun. 等)必须立刻调用 super.loadClass 向上委派。
- 漏掉
java.*的强制委派,JVM 会在 defineClass 阶段抛SecurityException或LinkageError - 把
org.apache.catalina.*这类容器内部类也自己加载,会导致 Tomcat 组件无法识别自身类型,出现ClassCastException - 建议白名单式判断:只对明确归属插件/模块的包路径走本地加载,其余一律委派
findClass 只负责读字节码,不参与委派决策
findClass 的职责很单纯:从指定路径(如 /plugins/kafka-0.11/)读取 .class 文件字节流,然后调用 defineClass。它不该包含任何委派逻辑,也不该捕获 ClassNotFoundException 并自行向上查找。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 所有“是否该自己加载”的判断,必须集中在
loadClass方法开头完成 -
findClass中若读不到文件,就直接抛出ClassNotFoundException,由loadClass决定是否继续委派 - 重复调用
defineClass加载同一名字的类(哪怕字节码一致),会触发LinkageError: duplicate class definition
每个版本必须使用独立的 ClassLoader 实例
定义一个 PluginClassLoader 类不等于实现了隔离。真正起作用的是运行时创建的多个实例,例如:
new PluginClassLoader("/plugins/kafka-0.10/")new PluginClassLoader("/plugins/kafka-0.11/")
JVM 判定类是否相同,依据是「类全名 + 加载它的 ClassLoader 实例」。两个实例加载的 KafkaProducer,即使源码完全一样,在 JVM 眼里也是两个互不可转换的类型,静态变量、方法表、字段内存全部隔离。
注意线程上下文与资源加载一致性
仅靠 loadClass 改写还不够。如果插件代码里调用 Class.getResource() 或依赖线程上下文类加载器(Thread.currentThread().getContextClassLoader()),必须同步重写 getResource 和 getResources 方法,确保资源查找也走当前加载器路径。
- 否则可能出现类能加载成功,但配套配置文件或模板找不到的情况
- JNI 库绑定也需注意:不同 ClassLoader 加载的类,若都调用
System.loadLibrary,可能因库已加载而失败 - 避免在
static块中引用跨模块类,否则会提前触发委派,破坏隔离意图
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










