1. 程式人生 > 其它 >JVM熱載入class檔案(findLoadedClass)

JVM熱載入class檔案(findLoadedClass)

package geym.zbase.ch10.clshot;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.WritableByteChannel;

public class MyClassLoader extends ClassLoader { private String fileName; public MyClassLoader(String fileName) { this.fileName = fileName; } protected Class<?> findClass(String className) throws ClassNotFoundException { Class clazz = this.findLoadedClass(className);
if (null == clazz) { try { String classFile = getClassFile(className); FileInputStream fis = new FileInputStream(classFile); 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(); byte[] bytes = baos.toByteArray(); clazz = defineClass(className, bytes, 0, bytes.length); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } return clazz; } private byte[] loadClassBytes(String className) throws ClassNotFoundException { try { String classFile = getClassFile(className); FileInputStream fis = new FileInputStream(classFile); 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(); } catch (IOException fnfe) { throw new ClassNotFoundException(className); } } private String getClassFile(String name) { StringBuffer sb = new StringBuffer(fileName); name = name.replace('.', File.separatorChar) + ".class"; sb.append(File.separator + name); return sb.toString(); } }
package geym.zbase.ch10.clshot;

import java.lang.reflect.Method;

public class DoopRun {
    public static void main(String args[]) {
        while(true){
            try{
                MyClassLoader loader = new MyClassLoader("D:/tmp/clz");
                Class cls = loader.loadClass("geym.zbase.ch10.clshot.DemoA");
                Object demo = cls.newInstance();
        
                Method m = demo.getClass().getMethod("hot", new Class[] {});
                m.invoke(demo, new Object[] {});
                Thread.sleep(10000);
            }catch(Exception e){
                System.out.println("not find");
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e1) {
                }
            }
        }
    }
}
package geym.zbase.ch10.clshot;

public class DemoA {
    public void hot(){
        System.out.println("NewDemoA");
    }
}

以上程式複製於(《實戰JAVA虛擬機器》)

MyClassLoader是自定義載入器。
DoopRun用於執行熱載入檔案中的hot方法。
DemoA用於提供hot方法,不同DemoA中的hot方法可以列印不同的內容。

將DemoA.class檔案放到D:/tmp/clz下面,需保持DemoA的全限定名的目錄結構。另外Java程式的啟動引數需要設定為-Xbootclasspath/a:/tmp/clz 。這樣替換DemoA.java 中的hot方法,可以列印不同的內容,不需要重啟Java程式。

MyClassLoader loader = new MyClassLoader("D:/tmp/clz"); 另外這一句非常重要,這一句要放到while中,放到while外的話,即使替換Demo.java也不會列印新的內容。原因是findClass方法中的Class clazz = this.findLoadedClass(className); 會判斷class檔案是否已經載入。如果已經載入,則使用之前載入過的class檔案,而不會重新載入。而放到while中,相當於每次重新new一個MyClassLoader,生成了不同的載入器,不同的載入器對應的是不同的class檔案,此時this.findLoadedClass(className)每次返回的都是null,都會去重新讀取DemoA.class檔案,重新載入,從而實現了熱載入。在JVM虛擬機器中,載入器和類的全限定名結合起來才能唯一確定一個類。