📅  最后修改于: 2023-12-03 15:41:24.369000             🧑  作者: Mango
Java中,类加载器是将类字节码数据从不同来源转换成可执行代码的核心组件。系统默认提供的类加载器有三种,分别是:
如果系统自带的类加载器不能满足我们的需求,就需要自定义类加载器来实现自己的需求。
Java虚拟机自带的类加载器都有固定的加载策略,无法满足特定的需求,比如:
这些特殊的类文件都需要自定义类加载器才能够被加载到内存中。另外,如果有多个版本的Jar包,想让JVM同时加载两个版本的Jar包,也需要自定义类加载器来实现。
自定义类加载器需要实现ClassLoader抽象类,一般的实现方式有两种:
实现代码:
public class MyClassLoader extends ClassLoader {
private String baseDir;// 加载类的基础路径
public MyClassLoader(String baseDir) {
this.baseDir = baseDir;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] data = loadClassData(name);// 读取class文件的字节码数据
return defineClass(name, data, 0, data.length);
}
private byte[] loadClassData(String name) throws ClassNotFoundException {
name = baseDir + File.separatorChar + name.replace('.', File.separatorChar) + ".class";
try (InputStream in = new FileInputStream(name)) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length = -1;
while ((length = in.read(buffer)) != -1) {
out.write(buffer, 0, length);
}
return out.toByteArray();
} catch (IOException e) {
throw new ClassNotFoundException("Class " + name + " not found", e);
}
}
}
使用自定义类加载器需要注意以下问题:
示例代码:
MyClassLoader classLoader = new MyClassLoader("path/to/classes");
Class<?> clazz = classLoader.loadClass("com.demo.MyClass");
Object obj = clazz.newInstance();
在ClassLoader的加载过程中,会交由父类加载器来先尝试加载指定的类,只有在父类加载器无法加载时,才会交由自定义类加载器来加载。
双亲委派模型的优点是防止类的冲突和避免类的重复加载,缺点是无法加载自定义的类和第三方类。
为了避免自定义类被父类加载器加载,需要在自定义类加载器中覆盖loadClass()方法,禁止使用双亲委派模型。
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
Class<?> clazz = findLoadedClass(name);
if (clazz == null) {
if (name.startsWith("com.demo")) {
clazz = findClass(name);
} else {
clazz = super.loadClass(name, resolve);
}
}
if (resolve) {
resolveClass(clazz);
}
return clazz;
}
自定义类加载器可以解决部分特殊场景的需求,但是使用时也需要注意双亲委派模型的问题。除非有确切的需求否则不建议使用,避免引入不必要的麻烦。