作者:清雅竹gf_644 | 来源:互联网 | 2023-09-16 17:45
不同的classloader加载的相同的类,会被jvm认为是不同的类要想实现热加载,几个原则是要记住的:每次实例化新的classloader动态加载类文件,比如rul或者文件等等记
不同的classloader加载的相同的类,会被jvm认为是不同的类
要想实现热加载,几个原则是要记住的:
每次实例化新的classloader
动态加载类文件,比如rul或者文件等等
记载的类使用反射进行方法调用,或者上溯为接口进行调用。
下面看一个例子:
首先定义一个被调用的简单类AppObject:
package com.dataguru.jvm.classloader;
public class AppObject {
public void sayHello(){
System.out.println("Hello 1.");
}
}
然后自定义一个ClassLoader的子类HotClassLoader:
package com.dataguru.jvm.classloader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.Class;
import java.lang.ClassLoader;
import java.lang.ClassNotFoundException;
import java.lang.Override;
import java.lang.String;
import java.lang.System;
public class HotClassLoader extends ClassLoader {
public HotClassLoader() {
// TODO Auto-generated constructor stub
}
public HotClassLoader(ClassLoader cl) {
super(cl);
}
@Override
public Class> loadClass(String name,boolean resolve) throws ClassNotFoundException {
{
// First, check if the class has already been loaded
Class> re = null;
try{
re = findClass(name);
}catch(SecurityException se){
System.out.println(se.getMessage());
}
if(re == null){
System.out.println("无法载入类:"+name+" 需要请求父加载器");
return super.loadClass(name,resolve);
}
return re;
}
}
@Override
protected Class> findClass(String name) throws ClassNotFoundException {
Class> cl = null;
try {
byte[] data = loadClassBytes(name);
cl = super.defineClass(name, data, 0, data.length);
} catch (IOException e) {
e.printStackTrace();
}
return cl;
}
public static byte[] loadClassBytes(String clazzName) throws IOException{
String resourceName = "/".concat(clazzName.replaceAll("[.]", "/")).concat(".class");
System.out.println("resource Location:"+resourceName);
InputStream is = TestMain.class.getResourceAsStream(resourceName);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] bt = new byte[1024];
int l = 0;
while((l = is.read(bt)) > 0 ){
baos.write(bt,0,l);
}
byte[] rst= baos.toByteArray();
is.close();
baos.close();
return rst;
}
}
最后写测试类进行测试:
/**
*
*/
package com.dataguru.jvm.classloader;
import java.lang.reflect.Method;
/**
* @author ShenLi
*
*/
public class TestHotLoad {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
HotClassLoader mcl = null;
while(true){
mcl = new HotClassLoader();
Class> clz = mcl.loadClass("com.dataguru.jvm.classloader.AppObject", true);
mcl = new HotClassLoader();
Object o = clz.newInstance();
System.out.println(o);
Method m = o.getClass().getMethod("sayHello", new Class[] {});
m.invoke(o, new Object[] {});
Thread.sleep(5000);
}
}
}
注意,每次生成新的classLoader实例,是因为通一个classLoader不能两次加载相同的类,否则会报错。生成的类实例,也不能直接new AppObject()而是要通过反射来调用,或者上溯为接口(还未测试)
测试输出:
resource Location:/com/dataguru/jvm/classloader/AppObject.class
resource Location:/java/lang/Object.class
Prohibited package name: java.lang
无法载入类:java.lang.Object 需要请求父加载器
com.dataguru.jvm.classloader.AppObject@33909752
resource Location:/java/lang/System.class
Prohibited package name: java.lang
无法载入类:java.lang.System 需要请求父加载器
resource Location:/java/io/PrintStream.class
Prohibited package name: java.io
无法载入类:java.io.PrintStream 需要请求父加载器
Hello 1.
将程序改为hello 2后保存编译
这时测试程序并没有退出,而是继续输出,但是输出改变了:
resource Location:/com/dataguru/jvm/classloader/AppObject.class
resource Location:/java/lang/Object.class
Prohibited package name: java.lang
无法载入类:java.lang.Object 需要请求父加载器
com.dataguru.jvm.classloader.AppObject@7f31245a
resource Location:/java/lang/System.class
Prohibited package name: java.lang
无法载入类:java.lang.System 需要请求父加载器
resource Location:/java/io/PrintStream.class
Prohibited package name: java.io
无法载入类:java.io.PrintStream 需要请求父加载器
Hello 2.
之后反复修改类,均可以实现动态加载。
自定义 ClassLoader 实现动态加载