上一篇加载器与双亲委托介绍对于类加载器和双亲有了大致的了解后,下面我们通过自定义类加载器加深对双亲机制的理解和介绍 ClassLoader抽象类中一些重要的方法。
自定义类加载器
所有自定义加载器都要继承父类:ClassLoader
public class MyParentClassLoader extends ClassLoader {
public String classLoaderName;//为了后面获取classloader名称时使用自定义名称
public ClassLoader parent;//指定使用自己的父类加载器,同时因为双亲模式,如果需要真正使用到自定义的加载器加载,需要将父类加载器置为null,或者将class文件放入到别的路径中。
//自定义父类加载器
public MyParentClassLoader(ClassLoader parent, String classLoaderName) {
super(parent);
this.parent = parent;
this.classLoaderName = classLoaderName;
}
下面通过javaDoc文档,引入其中一个重要的方法:findClass
以上是源码中对于自定义类加载器的描述,需要我们复写findClass方法,对于原因,我们进入ClassLoader中会发现:
首先父类并未真正实现该方法,只是抛出异常,而且注解中说到
- ClassLoader的实现需要复写
- loadClass方法会调用该findClass方法,如果复写,本身也就直接抛出CLassNotFound异常了
该方法也从另一方面说明了类加载器获取字节码内容的方式并未要求,跟上篇提到的加载路径没有规定一样。
我们继续往下自定义累加器的旅程:
/**
*classLoader,根据文档说明,需要复写其findClass方法
*/
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
//1、先要将类的字节名称转换成字节码数组
System.out.println("find class method invoke");
byte[] data = this.loadClassData(name);
//2需要将获取到的字节数组转换成class对象,后续如果需获取到该class文件代表类的对象
return defineClass(name, data, 0, data.length);
}
/**
* 通过类的路径,将名称对应的类文件读取里面的内容,转换成byte数组
*
* @param name
* @return
*/
private byte[] loadClassData(String name) {
System.out.println("start transform file to byte array for class");
InputStream in = null;
ByteArrayOutputStream out = null;
try {
//将二进制名称转换成字节码路径
String replace = name.replace(".", "/");
String target = "target/classes/" + replace + CLASS_PREFIX;
File file = new File(target);
in = new FileInputStream(file);
out = new ByteArrayOutputStream();
int read;
while (-1 != (read = in.read())) {
out.write(read);
}
return out.toByteArray();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
in.close();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("byte array is null");
return null;
}
实现好findClass后,会将class字节码文件内容得到对应的字节数组,然后通过class.newInstance方法,得到字节码对应类的实例对象。
使用自定义加载器
按照常规思路,我们定义好自己的加载器对象后,开始使用loadClass方法获取对应的Class对象,然后查看一下到底是jvm里面的系统类加载器加载的还是我们自定义加载的
出现上述原因就是我们要理解的:双亲委托机制,跟着构造器会发现:
默认情况下未自己指定父加载器,系统都是默认使用getSystemClassLoader即应用类加载,换而言之,我们自定义的加载器根本还没有发挥作用,因此,我们稍作改动:
上述是很简单粗暴的方法,但是原因和目的可以说明问题,没有双亲后,此时只能使用自定义的加载器加载了:
这样我们的加载器就真正起作用了。
PS:平时我们都或多或少自定义过类的加载器,而且也能获取到Class对象,但是我们并没有深究过到底是不是咱们的加载器做的贡献,很可能是被现象蒙蔽了,这也是跟双亲模式有关。
以上并不够,我们继续看下如果加载过程中,存在对其他对象的引用,那么这个是怎样进行加载的