热部署指能够在运行时实时修改类文件,只需要重新编译更改的类文件,而不用重新启动项目。可以用自定义类加载器实现。
1.创建自己的类加载器,继承ClassLoader并重写findClass方法。
public class MyClassLoader extends ClassLoader {
//项目路径
private String classPath;
public MyClassLoader(String classPath) {
this.classPath = classPath;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
//获取类的字节码文件
byte[] data = loadClassData(name.substring(name.lastIndexOf(".") + 1));
//重新加载类文件
return defineClass(name, data, 0, data.length);
} catch (IOException e) {
throw new ClassNotFoundException();
}
}
/**
* 读取类的字节码文件
*
* @param name
* @return
* @throws IOException
*/
private byte[] loadClassData(String name) throws IOException {
String path = classPath + "/" + name.replace('.', File.separatorChar) + ".class";
try (InputStream is = new FileInputStream(path)) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int b;
while ((b = is.read()) != -1) {
baos.write(b);
}
return baos.toByteArray();
}
}
}
2.测试代码
每隔5s执行一次myClass的print方法,并打印自己的类加载器。
public static void main(String[] args) throws Exception {
while (true) {
// 创建一个自定义类加载器实例
MyClassLoader myClassLoader = new MyClassLoader(Test.class.getResource("").getPath());
// 使用自定义类加载器重新加载 MyClass 类
Class<?> myClass = myClassLoader.findClass("com.cg.demo.test.MyClass");
// 使用重新加载后的类创建新的对象,并调用它的方法
Object myObject = myClass.newInstance();
Method doSomethingMethod = myClass.getMethod("print");
doSomethingMethod.invoke(myObject);
System.out.println(myObject.getClass().getClassLoader());
Thread.sleep(5000);
}
}
public class MyClass {
public void print() {
System.out.println("Hello World!");
}
}
在测试时,我们需要在5s中之内将MyClass进行重新编译,因为重新编译的过程中是没有MyClass类的,此时执行类加载会报错。
可以看到我们在不重启代码的情况下成功修改了MyClass类的print方法。