打破双亲委派机制的核心是控制类加载优先级顺序:重写loadclass方法,先调用findloadedclass和findclass本地加载,失败后再委托父加载器,并同步重写findresource、设置tccl。

打破双亲委派机制的核心,不是“去掉父加载器”,而是**控制类加载的优先级顺序**:让自定义加载器在父加载器之前尝试加载指定类,而不是无条件上抛请求。
重写 loadClass 方法是关键入口
双亲委派逻辑就实现在 ClassLoader.loadClass(String name, boolean resolve) 中。默认实现会先调用父加载器,失败后才调用 findClass。要打破它,必须重写这个方法,并调整执行顺序:
- 开头先调用
findLoadedClass(name)检查是否已加载 - 未加载时,**直接调用
findClass(name)尝试本地加载**(如从特定目录或 JAR) - 仅当
findClass抛出ClassNotFoundException,再调用super.loadClass(name, resolve)委托给父加载器 - 这样就实现了“先自己、后父类”的反向委派,而非默认的“先父类、后自己”
findClass 必须可靠定位并定义字节码
findClass 是真正加载字节码的地方,不能只返回 null 或随意抛异常:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 根据类名(如
org.springframework.core.io.Resource)构造对应路径(如/plugins/spring6/lib/spring-core.jar!/org/springframework/core/io/Resource.class) - 通过
URLClassLoader或自定义 I/O 读取字节流,确保资源可访问且路径隔离 - 调用
defineClass(name, bytes, 0, bytes.length)注册类;注意 JDK 9+ 会对模块和封装做校验,字节码必须合法 - 避免直接 new byte[] + defineClass 而不处理
resolve参数,否则类可能未链接,后续反射调用时报IllegalAccessError
资源查找也要同步切断委派
类加载不只是 loadClass,很多框架依赖 getResource 或 getResourceAsStream 加载配置、元数据(如 META-INF/MANIFEST.MF 或 spring.factories):
-
getResourceAsStream默认走双亲委派链,可能拿到父加载器 jar 中的资源 - 应重写
findResource(String name)和findResources(String name),限定只搜索本加载器的 URL 路径 - 例如 Tomcat 的
WebAppClassLoader就靠这个机制确保应用自己的web.xml和spring-beans.dtd不被容器类加载器覆盖
线程上下文类加载器需显式设置
很多底层 API(如 ServiceLoader、JDBC DriverManager、Spring 的 BeanFactory 初始化)会默认使用当前线程的上下文类加载器(TCCL):
- 创建自定义加载器后,需手动设置:
Thread.currentThread().setContextClassLoader(customLoader) - 否则即使类已由自定义加载器加载,
ServiceLoader.load(YourService.class)仍会用 AppClassLoader 去找,导致找不到实现 - 在多线程场景中(如 Web 容器中的请求线程),建议在入口处切换 TCCL,并在完成后恢复,避免污染其他任务
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










