即使是同个ClassLoader类的两个是实例,加载同个类也会不一样。所以实现类的热部署可以创建不同ClassLoader的实例对象,通过这两个不同的实例对象加载同名的类。发现xx.class文件被修改就执行加载。
package dou_dir_list;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class ClassReloader extends ClassLoader {
private String classPath;
String classname = "dou_dir_list.ListTest";
public ClassReloader(String classPath){
this.classPath = classPath;
}
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] classData = getData(name);
if(classData == null){
throw new ClassNotFoundException();
}else{
return defineClass(classname, classData, 0, classData.length);
}
}
private byte[] getData(String className) {
String path = classPath + className;
try {
InputStream is = new FileInputStream(path);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
byte[] buffer = new byte[2048];
int num = 0;
while((num = is.read(buffer)) != -1){
stream.write(buffer,0,num);
}
return stream.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
/**
* 同路径同名的某xx.class文件由两个不同的ClassLoader实例对象分别加载后再实例出的对象是两个不同的实例
*/
try {
String path = "E:/develop_workspaces/ssm/my_linked_list/bin/dou_dir_list/";
ClassReloader reloader = new ClassReloader(path);
Class r = reloader.findClass("ListTest.class");
System.out.println(r.newInstance());
ClassReloader reloader2 = new ClassReloader(path);
Class r2 = reloader2.findClass("ListTest.class");
System.out.println(r2.newInstance());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
原理是这样,这种方式可以为在开发时修改类频繁重启节省些时间。但是原来已经被引用的对象就不好让JVM去栈中替换了,这违反JVM的设计原则。还有点问题是加载的类会进入PermGen,在Full GC时才回收,所以改动频繁时间长了可能会有溢出问题,所以也就开发时用用。JVM不知道运行时类型,只知道编译类型。可以像JSP一样不保存对象状态,创建使用后立即释放,再修改又是新的。