代码热替换,在不重启服务器的情况下可以修改类的代码并使之生效
1. 自定义类加载器
package com.pibgstar.demo.java;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Set;
/**
* @author pibigstar
* @desc 代码热替换,在不重启服务器的情况下可以修改类的代码并使之生效
**/
public class MyClassLoader extends ClassLoader{
// 定义.class路径
private String swapPath;
// 存储哪些类需要我自身去加载
private Set<String> myselfLoader;
public MyClassLoader(String swapPath, Set<String> myselfLoader) {
this.swapPath = swapPath;
this.myselfLoader = myselfLoader;
}
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
Class<?> loadedClass = findLoadedClass(name);
// 需要我自己去加载
if (loadedClass==null && myselfLoader.contains(name)) {
loadedClass = findClass(name);
if (loadedClass!=null){
return loadedClass;
}
}
return super.loadClass(name, resolve);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// 根据文件系统路径加载class文件,并返回byte数组
byte[] b = getClassByte(name);
// 调用ClassLoader提供的方法,将二进制数组转换成Class类的实例
return defineClass(name,b,0,b.length);
}
private byte[] getClassByte(String name) {
String className = name.substring(name.lastIndexOf(".")+1,
name.length()) + ".class";
try {
FileInputStream fis = new FileInputStream(swapPath + className);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buff = new byte[1024];
int length = 0;
while ((length=fis.read(buff))>0){
baos.write(buff,0,length);
}
return baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return new byte[]{};
}
}
2. 测试
自定义一个每2秒打印调用一次方法打印版本号,先编译一次版本号为2的 MyClassLoaderTest .class类,然后启动版本号为1的,在运行过程中,我们把版本号为1的MyClassLoaderTest .class类替换成版本号为2的MyClassLoaderTest .class类,我们发现控制台立马输出了版本号2
package com.pibgstar.demo.java;
import com.google.common.collect.Sets;
import java.util.Timer;
import java.util.TimerTask;
/**
* @author pibigstar
* @desc
**/
public class MyClassLoaderTest {
public void printVersion() {
System.out.println("版本号1");
}
public static void main(String[] args){
new Timer().schedule(new TimerTask() {
@Override
public void run() {
// target/classes/com/pibgstar/demo/java/swap/
String swapPath = MyClassLoader.class.getResource("").getPath();
String className = "com.pibgstar.demo.java.MyClassLoaderTest";
// 创建自定义的类加载器,将路径和需要我们自己去加载的类名传递进去
MyClassLoader myClassLoader = new MyClassLoader(swapPath, Sets.newHashSet(className));
try {
//使用自定义的ClassLoader加载类,并调用printVersion方法。
Object o = myClassLoader.loadClass(className).newInstance();
o.getClass().getMethod("printVersion").invoke(o);
} catch (Exception e) {
e.printStackTrace();
}
}
},0,2000);
}
}