Java中的双亲委托机制是一种类加载器的工作模式,旨在确保Java类在虚拟机中的唯一性,防止类重复加载,并保护核心API的安全性。下面我们来详细了解这个机制。
类加载器的层次结构
Java中主要有四种类加载器:
- Bootstrap ClassLoader(启动类加载器) :负责加载Java核心类库,如
java.lang.Object
。 - Extension ClassLoader(扩展类加载器) :加载扩展类库,通常位于
jre/lib/ext
目录下。 - Application ClassLoader(应用类加载器) :加载应用程序的类,通常是用户编写的类。
- User ClassLoader(用户自定义类加载器) :用户可以自定义的类加载器,用于特殊场景。
每个类加载器都有一个父类加载器,除了Bootstrap ClassLoader,它没有父类加载器。
委托加载过程
- 请求委托:当一个类加载器收到类加载请求时,它不会直接加载,而是将请求委托给其父类加载器。
- 逐级委托:父类加载器继续将请求委托给其父类加载器,直到Bootstrap ClassLoader。
- 尝试加载:如果Bootstrap ClassLoader无法加载该类,则会逐层向下委托给子类加载器尝试加载。
- 自行加载:只有当父类加载器无法加载时,子类加载器才会尝试自己加载该类。
示例代码
下面是一个简单的自定义类加载器示例,展示如何重写loadClass()
方法:
java
java
import java.lang.reflect.Method;
public class CustomClassLoader extends ClassLoader {
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
// 首先尝试通过父类加载器加载
try {
return super.loadClass(name, resolve);
} catch (ClassNotFoundException e) {
// 如果父类加载器无法加载,则尝试自己加载
try {
byte[] classBytes = getClassBytes(name); // 自定义方法获取类字节码
return defineClass(name, classBytes, 0, classBytes.length);
} catch (Exception ex) {
throw new ClassNotFoundException(name, ex);
}
}
}
// 自定义方法获取类字节码
private byte[] getClassBytes(String className) {
// 这里需要实现获取类字节码的逻辑,例如从文件系统或网络读取
// 这里省略具体实现
return null;
}
public static void main(String[] args) throws Exception {
CustomClassLoader customClassLoader = new CustomClassLoader();
Class<?> clazz = customClassLoader.loadClass("CustomClass");
Method method = clazz.getMethod("customMethod");
Object instance = clazz.newInstance();
method.invoke(instance);
}
}
双亲委托机制的作用
- 防止类重复加载:通过委托父类加载器检查是否已经加载过该类,避免重复加载。
- 保证核心API安全性:防止开发者覆盖核心类库中的类,因为这些类最终都是由Bootstrap ClassLoader加载的。
打破双亲委托机制
如果需要打破双亲委托机制,可以通过自定义类加载器并重写loadClass()
方法来实现。这种方法通常用于特殊场景下需要绕过双亲委托的限制。
示例:打破双亲委托
下面是一个打破双亲委托的示例,直接在子类加载器中加载类,而不委托给父类加载器:
scala
java
public class BreakDelegationClassLoader extends ClassLoader {
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
// 直接尝试自己加载,而不委托给父类加载器
try {
byte[] classBytes = getClassBytes(name); // 自定义方法获取类字节码
return defineClass(name, classBytes, 0, classBytes.length);
} catch (Exception e) {
throw new ClassNotFoundException(name, e);
}
}
// 自定义方法获取类字节码
private byte[] getClassBytes(String className) {
// 这里需要实现获取类字节码的逻辑,例如从文件系统或网络读取
// 这里省略具体实现
return null;
}
}
这种方式通常用于特殊场景,如动态加载类或需要绕过双亲委托的限制时。