一、ClassLoader加载类的顺序
* 1.调用 findLoadedClass(String) 来检查是否已经加载类。
* 2.在父类加载器上调用 loadClass 方法。如果父类加载器为 null,则使用虚拟机的内置类加载器。
* 3.调用 findClass(String) 方法查找类。 *
二、实现自己的类加载器 * 1.获取类的class文件的字节数组 * 2.将字节数组转换为Class类的实例 * * */
以下是自定交代码
package com.myloader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class MyClassLoader extends ClassLoader {
// 类存放的路径
private String path = "E:/";
public MyClassLoader() {
};
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] data = loadClassData(name);
return this.defineClass(name, data, 0, data.length);
}
// public Class<?> loadClass(String name) throws ClassNotFoundException {
// try {
// String fileName = name.substring(name.lastIndexOf(".") + 1)
// + ".class";
// InputStream is = getClass().getResourceAsStream(fileName);
// if (is == null)
// return super.loadClass(name);
// byte[] b = new byte[is.available()];
// is.read(b);
// return defineClass(name, b, 0, b.length);
// } catch (IOException e) {
// e.printStackTrace();
// }
// return super.loadClass(name);
// }
public byte[] loadClassData(String name) {
try {
name = name.replace(".", "//");
File testClassFile = new File(path + name + ".class");
System.out.println(testClassFile.getAbsolutePath());
FileInputStream is = new FileInputStream(testClassFile);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int b = 0;
while ((b = is.read()) != -1) {
baos.write(b);
}
baos.flush();
is.close();
return baos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) throws ClassNotFoundException,
InstantiationException, IllegalAccessException, Exception {
MyClassLoader cl = new MyClassLoader();
Object obj = cl.loadClass("com.myloader.Test").newInstance();
System.out.println(obj.getClass());
// Thread.currentThread().setContextClassLoader(cl);
Method method = obj.getClass().getMethod("say", null);
method.invoke(obj, null);
// Thread t = new Thread(){
// @Override
// public void run() {
// Thread.currentThread().setContextClassLoader(cl);
// Test t = new Test();
// ClassLoader c1 = t.getClass().getClassLoader();
// System.out.println(c1);
// super.run();
// }
// };
// t.start();
}
}
Test类代码如下
package com.myloader;
public class Test {
public void say() {
System.out.println("hello world!");
Test t = new Test();
ClassLoader cl = t.getClass().getClassLoader();
System.out.println(cl);
}
}
写好后直接用ecliplse运行,
效果如下:
class com.myloader.Test
hello world!
sun.misc.Launcher$AppClassLoader@192d342
移动ecliplse工程下的class 文件,
需要把把包结构和类一起移动,我这儿是E盘:
移动后,工程的下Test.class文件就不见,E盘有一份
目录结构:E:\com\myloader\Test.class
运行后效果如下:
E:\com\myloader\Test.class
class com.myloader.Test
hello world!
com.myloader.MyClassLoader@1bc4459
比较结果:由于AppClassLoader现在没有找到,就调用了我findClass 去E盘目录去找,找到看看现在Test t = new test() 中实现的ClassLoader为
com.myloader.MyClassLoader@1bc4459 ,这个是我自定义的类加载器,可以看出如果如果想到加载我们自已写的就可以这样干,其实这就是一种简单的插件加载方式,
我们只需要写一个插件的加载器就可以了。
另外线程上下文类加载器 ,setContextClassLoader()这个方法可以设计类加载,会破坏双亲委派模型。这个设计后,我们在该线程任意一个地方加载我们需要的类。主要体现在SPI这些类上,列如,JDNI,JDBC,JXAB hotswap 。