classloader
java的classloader
是实现将java文件编译后产生的字节码加载进内存当中,最终成为可被java虚拟机直接使用的java类型。而原生的java虚拟机中的classloader
,如果我们想直接实现class文件的热替换,是不可能的,因为java虚拟机双亲委派模型的限制;因此,为了实现我们目标——class文件热替换,必须实现一个我们自己的classloader
my classloader
要实现自己的classloader,首先要继承java自有的ClassLoader
,重写父类的findClass(String name)
方法,由前面可知,classloader
是把.class
文件读取加载进内存中,因此,我们可以写一个关于.class
文件的file读取函数,将文件的内容读出并转为byte[]
数组,然后返回这个字节数组即可。
监听文件变化
要实现class文件热替换,那就要实现热这个关键。因此,就需要不断去监听class文件的变化情况,一旦class文件发生改变,立即通知我们自己创建的classloader去重新加载这个.class文件。然后覆盖原本内存中的class文件。从而实现我们热的关键
代码实现
自定义的 ClassLoader
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| public class MyClassLoader extends ClassLoader {
private File file;
public File getFile() { return file; }
public void setFile(File file) { this.file = file; }
protected MyClassLoader(ClassLoader parent) { super(parent); }
@Override public Class<?> loadClass(String name) throws ClassNotFoundException { return super.loadClass(name); }
@Override protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { return super.loadClass(name, resolve); }
@Override protected Class<?> findClass(String name) { Class clazz = null; try { byte[] data = getClassFileBytes(getFile()); clazz = defineClass(name, data, 0, data.length); if (null == clazz) {} } catch (Exception e) { e.printStackTrace(); } return clazz; }
private byte[] getClassFileBytes(File file) throws Exception { FileInputStream fis = new FileInputStream(file); FileChannel fileC = fis.getChannel(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); WritableByteChannel outC = Channels.newChannel(baos); ByteBuffer buffer = ByteBuffer.allocateDirect(1024); while (true) { int i = fileC.read(buffer); if (i == 0 || i == -1) { break; } buffer.flip(); outC.write(buffer); buffer.clear(); } fis.close(); return baos.toByteArray(); }
@Override public String toString() { return super.toString() + new Date().toLocaleString(); } }
|
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| public class Main {
Class aClass;
private void init() { new Thread() { long lastModify = 0; @Override public void run() { while (true) { File f = new File("/media/tensor/resource/code/IdeaProjects/spring-demo/hot-swap/out/production/classes/com/hotswap/org/Test.class"); if (lastModify != f.lastModified()) { lastModify = f.lastModified(); MyClassLoader myClassLoader = new MyClassLoader(this.getContextClassLoader()); myClassLoader.setFile(f); try { Class<Test> clazz = (Class<Test>) myClassLoader.findClass("com.hotswap.org.Test"); aClass = clazz; } catch (ClassNotFoundException e) { e.printStackTrace(); } } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); }
public static void main(String[] args) throws InterruptedException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { Main m = new Main(); m.init(); while (true) { if (m.aClass != null) { Method method = m.aClass.getMethod("log", null); Object o = m.aClass.getConstructor(new Class[]{}).newInstance(new Object[]{}); method.invoke(o, null); } Thread.sleep(5 * 1000); } }
}
|