自定义类加载器流程
- 继承
java.lang.ClassLoader
- 首先检查请求的类型是否已经被这个类装载器装载到命名空间中了,如果已经装载,直接返回。
- 委派类加载请求给父类加载器,如果父类加载器能够完成,则返回父类加载器加载的Class实例。
- 调用本类加载器的
findClass(…)
方法,试图获取对应的字节码,如果获取的到,则调用defineClass(…)
导入类型到方法区;如果获取不到对应的字节码或者其他原因失败,返回异常给loadClass(…)
,loadClass(…)
转抛异常,终止加载过程。
注意:被两个类加载器加载的同一个类,JVM不认为是相同的类
测试代码
- 编写
FileSystemClassLoader.java
package com.classloader.test;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class FileSystemClassLoader extends ClassLoader {
private String rootDir;
public FileSystemClassLoader(String rootDir) {
this.rootDir = rootDir;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class<?> c = findLoadedClass(name);
//应该先查询有没有加载过这个类。如果已经加载,则直接返回加载好的类。如果没有,则加载心得类。
if (c != null) {
return c;
}else {
ClassLoader parent = this.getParent();
try {
c = parent.loadClass(name);
} catch (ClassNotFoundException e) {
}
if (c != null) {
return c;
}else {
byte[] data = getClassData(name);
if (data == null) {
throw new ClassNotFoundException();
} else {
return defineClass(name, data, 0, data.length);
}
}
}
}
private byte[] getClassData(String name) {
//包名转成文件路径
String path = pathForClassName(name);
FileInputStream fis = null;
try {
fis = new FileInputStream(path);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int len = 0;
byte[] buffer = new byte[1024];
while ((len = fis.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
bos.flush();
return bos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
private String pathForClassName(String className) {
return rootDir + File.separatorChar
+ className.replace('.', File.separatorChar) + ".class";
}
}
- 在 D盘根目录创建
HelloWorld.java
代码如下
package com.classloader.test;
public class HelloWorld {
public static void main(String[] args){
System.out.println("xxx");
}
}
编译生成 class 文件
- 测试主类
package com.classloader.test;
public class FileClassLoaderTest {
public static void main(String[] args){
FileSystemClassLoader loader = new FileSystemClassLoader("D:/");
FileSystemClassLoader loader2 = new FileSystemClassLoader("D:/");
try {
Class<?> c1 = loader.loadClass("com.classloader.test.HelloWorld");
Class<?> c2 = loader.loadClass("com.classloader.test.HelloWorld");
Class<?> c3 = loader2.loadClass("com.classloader.test.HelloWorld");
System.out.println(c1.hashCode());
System.out.println(c2.hashCode());
System.out.println(c3.hashCode());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
输出
21685669
21685669
2133927002
可以看出 c1 跟 c2 的 hashCode 一致,说明是同一个对象,而 c3 不同,这就印证了我们上面所说的:被两个类加载器加载的同一个类,JVM不认为是相同的类
网友评论