双亲委派机制
双亲委派机制 是Java类加载器(ClassLoader)的核心工作机制,它通过层次化的类加载模型保障了Java程序的稳定性和安全性。
1. 双亲委派的核心机制
双亲委派的核心机制是:当一个类加载器需要加载类时,不会立即尝试自己加载,而是先将请求委派给父类加载器处理。流程如下:
- 自底向上委派:子类加载器逐层向上委托,直到启动类加载器(Bootstrap ClassLoader)。
- 自顶向下尝试加载:若父类加载器无法加载(在自己的搜索范围内未找到类),子类加载器才会尝试加载。
2. 代码示例:ClassLoader.loadClass()
Java
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
// 1. 检查类是否已加载
Class<?> c = findLoadedClass(name);
if (c == null) {
try {
// 2. 委派给父类加载器
if (parent != null) {
c = parent.loadClass(name, false);
} else {
// 3. 父类为null时,使用Bootstrap ClassLoader(由JVM实现)
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// 父类加载失败,不立即抛出异常
}
if (c == null) {
// 4. 父类未找到,调用自身的findClass()加载
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}3. 类加载器的层次结构
Java类加载器分为四层,形成树状结构:
- Bootstrap ClassLoader(启动类加载器):
- 由C++实现,负责加载
JAVA_HOME/lib下的核心类(如java.lang.*)。 - 唯一没有父类的加载器,是其他所有类加载器的根。
- 由C++实现,负责加载
- Extension ClassLoader(扩展类加载器):
- 加载 JAVA_HOME/lib/ext 目录或 java.ext.dirs 指定的类库。
- Application ClassLoader(应用类加载器):
- 加载用户路径(ClassPath)下的类,是 ClassLoader.getSystemClassLoader() 的默认返回值。
- 自定义ClassLoader:
- 用户可继承
ClassLoader,重写findClass()实现自定义加载逻辑(如从网络、加密文件中加载类)。
- 用户可继承
4. 双亲委派的优势
安全性:
- 防止核心API被篡改:例如自定义
java.lang.Object类时,由于 Bootstrap ClassLoader已加载核心类,自定义类不会被加载,避免了恶意代码注入。 - 沙箱保护机制:通过层级隔离,不同层次的类加载器加载的类无法互相访问(扩展类无法访问应用类)。
- 防止核心API被篡改:例如自定义
避免重复加载:
- 类的唯一性标识:一个类由加载它的类加载器和全限定名(如
java.lang.Object)共同确定。双亲委派确保类只被加载一次,避免内存浪费。
- 类的唯一性标识:一个类由加载它的类加载器和全限定名(如
保证基础类的一致性:
- 所有子类加载器最终都会委托到Bootstrap ClassLoader。确保核心类(如Object、String)全局唯一,避免多版本冲突。
打破双亲委派的场景 虽然双亲委派是默认机制,但在某些场景下需要逆向委派或平级委派:
SPI(Service Provider Interface):JDBC、JNDI等接口由Bootstrap ClassLoader加载,但具体实现(如MySQL驱动)需由线程上下文类加载器(TCCL)加载,打破了双亲委派。OSGi模块化: 每个
Bundle使用独立的类加载器,通过图结构的依赖关系实现类共享和隔离。热部署: 通过自定义类加载器重新加载修改后的类(如
Tomcat对JSP的热更新)。
5. 自定义类加载器
自定义类加载器需遵循双亲委派:
继承ClassLoader,重写findClass()(而非loadClass(),否则需手动实现委派逻辑)。 在findClass()中从指定路径(如文件、网络)读取类字节码,调用defineClass()生成类对象。
Java
public class CustomClassLoader extends ClassLoader {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] classData = loadClassData(name); // 从自定义路径加载字节码
return defineClass(name, classData, 0, classData.length);
}
}