如何实现一个类加载器?
要实现一个类加载器,可以继承 ClassLoader
类并根据需求重写相关方法。
以下是具体实现方式:
遵循双亲委派模型
如果希望类加载器遵循双亲委派原则,只需重写 findClass
方法即可。
public class MyClassLoader extends ClassLoader {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// 自定义加载逻辑,例如从文件中加载类的字节码
byte[] classBytes = loadClassData(name);
if (classBytes == null) {
throw new ClassNotFoundException(name);
}
return defineClass(name, classBytes, 0, classBytes.length);
}
private byte[] loadClassData(String name) {
String filePath = name.replace('.', '/').concat(".class");
try (InputStream input = new FileInputStream(filePath);
ByteArrayOutputStream output = new ByteArrayOutputStream()) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = input.read(buffer)) != -1) {
output.write(buffer, 0, bytesRead);
}
return output.toByteArray();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
特点:findClass
方法只在父类加载器无法加载目标类时调用,因此遵循双亲委派模型。
破坏双亲委派模型
如果需要实现自己的类加载逻辑而不遵循双亲委派原则,可以重写 loadClass
方法。
public class CustomClassLoader extends ClassLoader {
@Override
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
// 检查类是否已加载
Class<?> loadedClass = findLoadedClass(name);
if (loadedClass == null) {
if (shouldUseParent(name)) {
try {
loadedClass = getParent().loadClass(name);
} catch (ClassNotFoundException ignored) {
}
}
if (loadedClass == null) {
loadedClass = findClass(name); // 调用自定义加载逻辑
}
}
if (resolve) {
resolveClass(loadedClass);
}
return loadedClass;
}
}
private boolean shouldUseParent(String name) {
// 可根据类名决定是否优先使用父类加载器
return !name.startsWith("com.example.custom");
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] classBytes = loadClassData(name);
if (classBytes == null) {
throw new ClassNotFoundException(name);
}
return defineClass(name, classBytes, 0, classBytes.length);
}
}
特点:通过重写 loadClass
方法实现完全的类加载逻辑控制,可以选择不优先使用父类加载器,从而破坏双亲委派模型。
为什么是 loadClass
而不是 findClass
?
loadClass
的职责
loadClass
是类加载过程的入口方法,它控制整个类加载流程,包括:
检查类是否已加载(调用
findLoadedClass
)。优先委托给父类加载器(调用
getParent().loadClass
)。自定义加载逻辑(调用
findClass
)。
findClass
的职责
findClass
是 ClassLoader
的钩子方法,仅在父类加载器无法加载类时调用,用于实现自定义加载逻辑。
它不负责双亲委派,只关注加载字节码并定义类。
重写方法的区别
如果重写
findClass
,依然遵循双亲委派模型,因为loadClass
的逻辑不变。如果重写
loadClass
,可以完全控制类加载流程,包括是否调用父类加载器。
总结
如何实现类加载器
遵循双亲委派:重写
findClass
。破坏双亲委派:重写
loadClass
。
为什么是
loadClass
而不是findClass
loadClass
是类加载的总控方法,负责协调整个加载流程,包括双亲委派。findClass
仅实现具体的类加载逻辑,是loadClass
流程的一部分。
选择依据
如果希望仅定制加载逻辑并保留双亲委派,重写
findClass
。如果需要完全自定义类加载流程,重写
loadClass
。