動態重新載入Class機制之代碼測試

來源:互聯網
上載者:User
V1.1 修改了主程式,調用帶參數的建構函式
package com.ailk;import java.lang.reflect.*;import java.util.ArrayList;import java.util.List;import com.ailk.dynamic.Demo;public class DemoMain {    static public void main( String args[] ) throws Exception {        String progClass ="com.ailk.dynamic.Demo";        // 建立CompilingClassLoader        Class c = Class.forName(progClass, true, new CompilingClassLoader());        //DemoInterface i=(DemoInterface)c.newInstance();        //cl1和cl2是兩個不同的ClassLoader        ClassLoader cl1=c.getClassLoader();        ClassLoader cl2=Demo.class.getClassLoader();        ClassLoader cl3=DemoInterface.class.getClassLoader();        int ii=0;        List<DemoInterface> objList=new ArrayList();        while(true){            ii++;            CompilingClassLoader ccl = new CompilingClassLoader();            // 通過CCL載入主函數類。            Class clas = ccl.loadClass( progClass,true);            try{                Constructor c1=c.getDeclaredConstructor(new Class[]{String.class});                 c1.setAccessible(true);                   DemoInterface a1=(DemoInterface)c1.newInstance(new Object[]{"Demo"});            }catch(NoSuchMethodException e){                System.out.println("建構函式不存在");                e.printStackTrace();            }            DemoInterface instance=null;            try{                Constructor c0=clas.getDeclaredConstructor();                c0.setAccessible(true);                   instance=(DemoInterface)c0.newInstance();              }catch(NoSuchMethodException e){                System.out.println("建構函式不存在");                e.printStackTrace();            }            ccl=null;//這裡講主動釋放cc1            //DemoInterface instance=(DemoInterface)clas.newInstance();            if (instance!=null)            {                objList.add(instance);                instance.print("demo");                // 利用反射調用它的函數和傳遞參數。                // 產生一個代表主函數的參數類型的類對象。                Class mainArgType[] = { String.class };                //在類中找函數。                Method method = clas.getMethod( "print", mainArgType );                Object[] argsArray={"Demo"};                //調用方法。                method.invoke(instance, argsArray );                            }            if (ii>20)            {                ii=0;                objList.clear();            }            Thread.sleep(500);            //強制gc,只有objList清空後 CompilingClassLoader的執行個體才會釋放。            //因為只有由CompilingClassLoader載入的class的執行個體全部釋放後,CompilingClassLoader才能被釋放            System.gc();        }    }}

V1.0 在運行過程住動態編譯並重新載入Class 需要繼承ClassLoader ,沒有不同的CompilingClassLoader 載入的class是不相同的不能相互轉換。 對於CompilingClassLoader 的執行個體,只是簡單的賦null是會自動釋放的,只有由其載入的class的全部執行個體都釋放後, CompilingClassLoader 的執行個體才會釋放。 我在代碼裡加入了protected void finalize()來檢測執行個體何時釋放。     在reload/com/ailk/dynamic中放一個要動態重載的Java程式,這裡我們把測試類別放這個目錄。 代理類 package com.ailk;

public interface DemoInterface {
public void print( String args);
}測試類別package com.ailk.dynamic;
import com.ailk.DemoInterface;
public class Demo implements DemoInterface {
@Override
public void print( String args){
}
}

主程式package com.ailk;

import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.List;

import com.ailk.dynamic.Demo;

public class DemoMain {
static public void main( String args[] ) throws Exception {

String progClass ="com.ailk.dynamic.Demo";

// 建立CompilingClassLoader
Class c = Class.forName(progClass, true, new CompilingClassLoader());
DemoInterface i=(DemoInterface)c.newInstance();

//cl1和cl2是兩個不同的ClassLoader
ClassLoader cl1=c.getClassLoader();
ClassLoader cl2=Demo.class.getClassLoader();
ClassLoader cl3=DemoInterface.class.getClassLoader();

int ii=0;
List<DemoInterface> objList=new ArrayList();
while(true){
ii++;
CompilingClassLoader ccl = new CompilingClassLoader();
// 通過CCL載入主函數類。
Class clas = ccl.loadClass( progClass,true);
// 利用反射調用它的函數和傳遞參數。
// 產生一個代表主函數的參數類型的類對象。
Class mainArgType[] = { String.class };
// 在類中找函數。
Method method = clas.getMethod( "print", mainArgType );
Object[] argsArray={"Demo"};
//調用方法。
method.invoke( clas.newInstance(), argsArray );

ccl=null;//這裡講主動釋放cc1
DemoInterface instance=(DemoInterface)clas.newInstance();
objList.add(instance);
instance.print("demo");
if (ii>20)
{
ii=0;
objList.clear();
}
Thread.sleep(500);
//強制gc,只有objList清空後 CompilingClassLoader的執行個體才會釋放。
//因為只有由CompilingClassLoader載入的class的執行個體全部釋放後,CompilingClassLoader才能被釋放
System.gc();

}
}
}下面的這個類就是我們自己的一個CompilingClassLoader 。其功能是從當前類的路徑中的目錄的reload中載入class檔案,如果其Java檔案有更新則重新編譯然後再載入,注意,在reload的目錄中只放如需要重新載入的類,其他的不要放到這個目錄中,特別是代理介面。如果代理介面也放到這個目錄中的相應的目錄下的話,CompilingClassLoader 就會將其載入,那麼 我們在main程式的DemoInterface instance=(DemoInterface)clas.newInstance()行就會發生java.lang.ClassCastException 錯誤。

package com.ailk;import java.io.*;/* CompilingClassLoader動態編譯Java源檔案。它檢查.class檔案是否存在,.class檔案是 否比源檔案陳舊。 */public class CompilingClassLoader extends ClassLoader {protected void finalize()    {System.out.println("finalize this:"+this);try {super.finalize();} catch (Throwable e) {// TODO Auto-generated catch blocke.printStackTrace();}     }// 指定一個檔案名稱,從磁碟讀取整個檔案內容,返回位元組數組。private byte[] getBytes(String filename) throws IOException {// 獲得檔案大小。File file = new File(filename);long len = file.length();// 建立一個數組剛好可以存放檔案的內容。byte raw[] = new byte[(int) len];// 開啟檔案FileInputStream fin = new FileInputStream(file);// 讀取所有內容,如果沒法讀取,表示發生了一個錯誤。int r = fin.read(raw);if (r != len)throw new IOException("Can't read all, " + r + " != " + len);// 別忘了關閉檔案。fin.close();// 返回這個數組。return raw;}// 產生一個進程來編譯指定的Java源檔案,制定檔案參數.如果編譯成功返回true,否者,// 返回false。private boolean compile(String javaFile) throws IOException {// 顯示當前進度System.out.println("CCL: Compiling " + javaFile + "...");// 啟動編譯器Process p = Runtime.getRuntime().exec("javac -classpath " + CompilingClassLoader.class.getResource("/").getPath()+ " -Xlint:unchecked " + javaFile);// 等待編譯結束try {p.waitFor();} catch (InterruptedException ie) {System.out.println(ie);}// 檢查返回碼,看編譯是否出錯。int ret = p.exitValue();// 返回編譯是否成功。return ret == 0;}// 類載入器的核心代碼 -載入類在需要的時候自動編譯源檔案。public Class loadClass(String name, boolean resolve)throws ClassNotFoundException {// if (!name.startsWith("com.ailk.dynamic")){// return getParent().loadClass(name);// }// 我們的目的是獲得一個類對象。Class clas = null;// 首先,檢查是否已經出理過這個類。clas = findLoadedClass(name);if (clas != null)return clas;//if (clas == null) {//try {//if (getParent() != null) {//clas = super.findClass(name);//} else {//clas = findSystemClass(name);//}//} catch (ClassNotFoundException e) {//// If still not found, then invoke findClass in order//// to find the class.////clas = findClass(name);//}//}// System.out.println( "findLoadedClass: "+clas );// 通過類名獲得路徑名 比如:java.lang.Object => java/lang/ObjectString fileStub = name.replace('.', '/');// 構建指向源檔案和類檔案的對象。String javaFilename = CompilingClassLoader.class.getResource("/").getPath() + "reload/"+ fileStub + ".java";//System.out.println(javaFilename);String classFilename = CompilingClassLoader.class.getResource("/").getPath() + "reload/"+ fileStub + ".class";//System.out.println(classFilename);File javaFile = new File(javaFilename);File classFile = new File(classFilename);// System.out.println( "j "+javaFile.lastModified()+" c "// +classFile.lastModified() );// 首先,判斷是否需要編譯。如果源檔案存在而類檔案不存在,或者都存在,但是源檔案// 較新,說明需要編譯。boolean javaExists = javaFile.exists();boolean classExists = classFile.exists();if (javaFile.exists()&& (!classFile.exists() || javaFile.lastModified() > classFile.lastModified())) {try {// 編譯,如果編譯失敗,我們必須聲明失敗原因(僅僅使用陳舊的類是不夠的)。if (!compile(javaFilename) || !classFile.exists()) {throw new ClassNotFoundException("Compile failed: "+ javaFilename);}} catch (IOException ie) {// 可能編譯時間出現IO錯誤。throw new ClassNotFoundException(ie.toString());}}// 確保已經正確編譯或者不需要編譯,我們開始載入原始位元組。try {// 讀取位元組。byte raw[] = getBytes(classFilename);// 轉化為類對象clas = defineClass(name, raw, 0, raw.length);System.out.println("load class:"+classFilename+" classloader is:"+this);} catch (IOException ie) {// 這裡並不表示失敗,可能我們處理的類在本地類庫中,如java.lang.Object。}// System.out.println( "defineClass: "+clas );// 可能在類庫中,以預設的方式載入。if (clas == null) {clas = findSystemClass(name);//System.out.println("use define class:"+name);}// System.out.println( "findSystemClass: "+clas );// 如果參數resolve為true,根據需要解釋類。if (resolve && clas != null)resolveClass(clas);// 如果還沒有獲得類,說明出錯了。if (clas == null)throw new ClassNotFoundException(name);// 否則,返回這個類對象。return clas;}}




聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.