想象一个大家族处理传家宝:
爷爷(启动类加载器)→ “先看看我有没有这个传家宝”
爸爸(扩展类加载器)→ “没有的话我再找找我的收藏”
儿子(应用类加载器)→ “最后才轮到我来处理”
Java的双亲委派模型就是这样的"责任传递链",它保证了类加载的安全性和唯一性。今天我们就来拆解这套精妙的机制!
加载器 | 加载路径 | 典型加载内容 |
---|---|---|
启动类加载器 | JAVA_HOME/lib | rt.jar等核心类 |
扩展类加载器 | JAVA_HOME/lib/ext | 扩展功能包 |
应用类加载器 | classpath | 用户自定义类 |
// 保证java.lang.Object只被加载一次
// 无论多少加载器请求,最终都委派给启动类加载器
// 用户自定义java.lang.String类不会被加载
// 因为父加载器已加载了核心String类
// 恶意类无法冒充核心库类
protected Class<?> loadClass(String name, boolean resolve) {
synchronized (getClassLoadingLock(name)) {
// 1. 检查是否已加载
Class<?> c = findLoadedClass(name);
if (c == null) {
try {
// 2. 先委托父加载器
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// 父类加载器无法完成加载
}
// 3. 父类无法加载时才自己加载
if (c == null) {
c = findClass(name);
}
}
return c;
}
}
// DriverManager需要加载厂商实现
// 但核心类要调用应用类加载器加载的驱动
Thread.currentThread().setContextClassLoader(...);
// 自定义类加载器不委派父加载器
protected Class<?> loadClass(String name, boolean resolve) {
// 直接自己加载,不委派
Class<?> c = findClass(name);
...
}
// 每个Bundle有自己的类加载器
// 网状结构的类加载关系
答案:当高层需要调用低层实现时(如JDBC需要加载不同厂商驱动)
关键步骤:
原理:每个WebApp使用独立的类加载器,优先自己加载
class HotSwapLoader extends ClassLoader {
@Override
protected Class<?> findClass(String name) {
byte[] classData = loadByte(name); // 从文件读取
return defineClass(name, classData, 0, classData.length);
}
}
双亲委派口诀:
类加载器有三亲,层层委托保安全;
启动扩展应用序,防止篡改核心篇;
特殊场景需打破,SPI热载显神通;
自定义器要谨慎,遵循规范最关键!
记住:双亲委派是Java安全的基石,理解它才能真正掌握类加载机制!