自定义类加载器
自定义类加载器的流程
1.首先检查请求的类型是否已经被这个类装载器装载到命名空间中了,如果已经装载,直接返回;否则转入步骤2
2.委派类加载请求给父类加载器(更准确的说应该是双亲类加载器,真个虚拟机中各种类加载器最终会呈现树状结构),如果父类加载器能够完成,则返回父类加载器加载的Class实例;否则转入步骤3
3.调用本类加载器的findClass(…)方法,试图获取对应的字节码,如果获取的到,则调用defineClass(…)导入类型到方法区;如果获取不到对应的字节码或者其他原因失败,返回异常给loadClass(…), loadClass(…)转抛异常,终止加载过程(注意:这里的
异常种类不止一种)。
注意:被两个类加载器加载的同一个类,JVM不认为是相同的类。
自定义"文件系统类加载器"代码实现如下:
package 测试类加载全过程;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
/**
* 自定义文件系统类加载器
*/
public class FileSystemClassLoader extends ClassLoader{
private String rootDir;
public FileSystemClassLoader(String rootDir) {
this.rootDir=rootDir;
}
//com.xyj.test.User --> f:/MyJava/com/xyj/test/User.class
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
//先查找已加载的类,看有没有已经被加载过
Class<?> c = findLoadedClass(name);
//如果已经加载过 就return回去
if(c!=null) {
return c;
}else {
//如果没有被加载过 委派父类去加载
ClassLoader parent = this.getParent();
try {
//此try-catch的作用是出了异常继续往下走
c=parent.loadClass(name);//委派父类去加载
} catch (Exception e) {
//e.printStackTrace();
}
if(c!=null) {
return c;
}else {
//如果还是没找到 则读取路径下的class文件转换为字节数组
byte [] classData=getClassData(name);
if(classData==null) {
throw new ClassNotFoundException();
}else {
c=defineClass(name,classData,0,classData.length);//将字节数组转换为类别 Class的实例
}
}
}
return c;
}
private byte[] getClassData(String className) {
//com.xyj.test.User --> f:/MyJava/com/xyj/test/User.class
String path=rootDir+"/"+className.replace('.','/')+".class";
InputStream is=null;
ByteArrayOutputStream baos=new ByteArrayOutputStream();
try {
is=new FileInputStream(path);
byte [] buffer=new byte[1024];
int len=-1;
while((len=is.read(buffer))!=-1) {
baos.write(buffer, 0, len);
}
baos.flush();
return baos.toByteArray();