适合人群: Java中高级工程师、框架开发者
难度等级: ⭐⭐⭐⭐ (中高级)
阅读时间: 18分钟
📖 引言
双亲委派模型是Java类加载机制的核心,但JDBC、Tomcat等又必须打破它。今天彻底搞懂这个矛盾!
🏗️ 第一章:双亲委派模型
1.1 三层类加载器 🎯
Bootstrap ClassLoader (启动类加载器)
↑ 父加载器
Extension ClassLoader (扩展类加载器)
↑ 父加载器
Application ClassLoader (应用类加载器)
↑ 父加载器
Custom ClassLoader (自定义类加载器)
1.2 工作流程 🔄
1. 收到加载类请求
2. 委托给父加载器
3. 父加载器加载失败,自己加载
4. 自己也失败,抛出ClassNotFoundException
1.3 为什么要双亲委派?✅
1. 避免类重复加载
2. 保护核心类库(java.lang.String不能被篡改)
3. 保证类的唯一性
🔓 第二章:为什么要打破?
2.1 JDBC的困境 🎯
// rt.jar中的DriverManager(Bootstrap加载)
// 需要加载com.mysql.cj.jdbc.Driver(Application加载)
// 问题:父加载器不能访问子加载器加载的类!
// 解决:线程上下文类加载器
Thread.currentThread().setContextClassLoader(appClassLoader);
2.2 SPI机制 (Service Provider Interface) 🔌
SPI = 接口 + 配置文件 + ServiceLoader
核心思想:
- JDK定义接口
- 第三方提供实现
- 通过配置文件声明
- ServiceLoader动态加载
示例:
META-INF/services/java.sql.Driver
↓
com.mysql.cj.jdbc.Driver
com.postgresql.Driver
2.3 Tomcat的需求 🚢
同一个Tomcat运行多个应用:
- App A: Spring 4.0
- App B: Spring 5.0
需求:隔离!每个应用独立加载类
解决:自定义WebAppClassLoader
- 优先加载自己的类
- 再委托给父加载器
🎓 第三章:如何打破双亲委派?
方法1:重写loadClass() 🛠️
public class MyClassLoader extends ClassLoader {
@Override
public Class<?> loadClass(String name) {
// 不委托给父加载器,直接自己加载
return findClass(name);
}
}
方法2:使用线程上下文类加载器 🧵
ClassLoader contextCL = Thread.currentThread().getContextClassLoader();
Class<?> clazz = contextCL.loadClass("com.example.MyClass");
方法3:OSGi的网状结构 🕸️
不是树状结构,而是网状
每个模块有自己的类加载器
可以互相委托
💡 总结
双亲委派:保证核心类安全
打破双亲委派:满足特殊需求(SPI、Tomcat等)
线程上下文类加载器:父加载器访问子加载器的桥梁
下一篇: 字节码增强技术