自定义类加载器
项目结构
准备一个User类和MyClassLoader类,项目结构如下
MyClassLoader代码
package com.cwf.resource.jvm;
import java.io.FileInputStream;
public class MyClassLoader extends ClassLoader {
// 自定义加载路径
private String classPath;
//构造方法,初始化加载路径
public MyClassLoader( String classPath) {
this.classPath = classPath;
}
/*
在自定义路径下,加载字节码文件到内存中
*/
private byte[] loadByte(String className) throws Exception{
className=className.replaceAll("\\.","/");
FileInputStream fis=new FileInputStream(classPath+"/"+className+".class");
int len=fis.available();
byte[] data= new byte[len];
fis.read(data);
fis.close();
return data;
}
/*
重写findClass方法
*/
protected Class<?> findClass(String name)throws ClassNotFoundException {
try {
byte[] data = loadByte(name);
//defineClass这个方法会将一个字节数组转为Class对象。
return defineClass(name, data, 0, data.length);
} catch (Exception e) {
e.printStackTrace();
throw new ClassNotFoundException();
}
}
//在main方法中调用loadClass方法去加载User类
public static void main(String[] args) throws Exception {
MyClassLoader loader = new MyClassLoader("F:\\class");
Class<?> userClass = loader.loadClass("com.cwf.resource.jvm.User");
System.out.println(userClass.getClassLoader().getClass().getName());
}
}
将User.class文件拷贝一份到我们定义的路径中,然后执行main()方法
main方法执行结果
我们可以看到虽然我们使用的我们自定义的类加载器去加载我们的类但是最中加载User类的却是AppClassLoader,这就是由于双亲委派机制的原因。
指定自定义类加载器parent源码分析
自定义类加载器是何时指定parent的呢?
在调用MyclassLoader的构造方法前会先调用ClassLoader的构造方法 ClassLoader() , ClassLoader() 又调用了ClassLoader(Void unused, ClassLoader parent)这个有参构造方法,传入的parent参数就是通过getSystemClassLoader()方法获取的系统默认类加载器,
所以就指定了我们MyClassLoader的parent为AppClassLoader
protected ClassLoader() {
this(checkCreateClassLoader(), getSystemClassLoader());
}
private ClassLoader(Void unused, ClassLoader parent) {
this.parent = parent;
if (ParallelLoaders.isRegistered(this.getClass())) {
parallelLockMap = new ConcurrentHashMap<>();
package2certs = new ConcurrentHashMap<>();
domains =
Collections.synchronizedSet(new HashSet<ProtectionDomain>());
assertionLock = new Object();
} else {
// no finer-grained lock; lock on the classloader instance
parallelLockMap = null;
package2certs = new Hashtable<>();
domains = new HashSet<>();
assertionLock = this;
}
}
接下来我们删掉项目中的User类再执行一次main()方法,执行结果如下
说明如果父 类加载器指定路径下没有同名的类最中会由子类加载器自己加载