動態編譯JAVA程式
這段說明很有用,動態的編譯是件非常好的想法,我這裡引用收藏一下:)
地址:http://www.itpub.net/showthread.php?s=&postid=8088670#post8088670
[轉貼者注]對於很多應用系統,常常需要動態裝載和執行類和程式碼片斷,這有利於部署的簡易性和系統設計上的靈活性。本文給出了一個比較全面的介紹,值得參考。
在Sun JDK 1.2及後續版本中,包含了一組可在程式執行時刻編譯和執行Java程式碼的API。這些API被包含在tools.jar類庫中。這個功能允許Java程式在執行時動態編譯、執行小的程式碼塊,在有些情況下這個功能會讓Java應用程式的架構更加靈活、開放。
本文假定讀者已經在計算機中安裝並配置好了Sun JDK 1.2或更高的版本,並對javac編譯器命令有所瞭解。
在Java程式中使用編譯器
假定要使用javac命令編譯 /home/mytest目錄下Test.java檔案,並設定class檔案存放在/home/mytest/classes路徑下,輸入下面命令:
javac -d /home/mytest/classes Test.java
達到同樣的目的,也可以使用Sun提供的一個Java編譯器的API來實現。它的使用也很簡單,核心程式碼段如下:
…
String[] args = new String[] {“-d”, “/homemytestclasses”, “Test.java”};
Int status = javac.compile(args);
…
javac編譯工具被安裝在JDK根目錄的/bin目錄下,負責將原始碼編譯成執行於JVM的位元組碼。事實上,我們經常使用/bin目錄下的javac編譯工具來編譯Java原始檔。如果在Java程式中動態編譯任意制定的Java語句,使用這個外部的javac編譯器就顯得不夠靈活了。雖然有時可使用Runtime類來執行一個外部命令,但如果想知道程式碼是否被編譯透過、編譯時發生了什麼錯誤,用Runtime類的exec()方法就很難實現了。
在Sun的JDK 1.2及後續版本中,JDK安裝路徑的/lib路徑下包含了一個tools.jar檔案,這個類庫包含了一個完整的編譯器包。com.sun.tools.javac.Main是編譯器的主類入口,如果已經熟悉了javac編譯器命令列的使用方法,很容易理解這個類的使用方法。方法compile(String[] p)執行編譯動作,引數p是一個String陣列,用來存放javac命令的引數選項,編譯後的狀態返回一個Int值,其對應值參考如下表所示:
表 狀態引數與對應值
EXIT_OK 0
EXIT_ERROR 1
EXIT_CMDERR 2
EXIT_SYSERR 3
EXIT_ABNORMAL 4
在程式執行時編譯和執行Java語句
從上面一段中,我們已經基本瞭解了動態編譯一個Java檔案的方法。那麼,如何執行時動態編譯指定的Java語句呢?這裡需要一個技巧。
假設要動態編譯的Java條語句如下:
System.out.println(“Hello,This runtime code!”);
編譯器不支援編譯單個Java語句,被編譯的物件必須是一個以.java為字尾的、結構合法的類源程式檔案,所以需要對這個語句進行改造,變成一個完整的類,並把這條語句置入main方法中,便於測試。
public class {
public static void main(String[] args) throws Exception {
System.out.println(“Hello,This runtime code!”);
}
}
這樣,欲動態編譯的程式碼已經被程式動態拼裝成了上面那段程式碼,準備工作還沒有結束,不過看起來工作在趨向稍微的複雜化。因為上述程式碼當前還存放在記憶體中,編譯器似乎對一個硬碟檔案更感興趣。我們需要引用java.io.File類(JDK 1.2以上),建立一個臨時的檔案來存放上述程式碼的內容。java.io.File類的靜態方法createTempFile()方法保證所建立的檔名是不重複的,這樣會增大這段程式的靈活性。靈活性取決於真正應用到系統架構中的策略。
System.getProperty(“user.dir”)用來獲得當前路徑,在這裡作為臨時檔案的存放目錄。
File file;
file = File.createTempFile(“JavaRuntime”, “.java”, new File(System.getProperty(“user.dir”)));
String filename = file.getName();
String classname = getClassName(filename);
//將程式碼輸出到檔案
PrintWriter out = new PrintWriter(new FileOutputStream(file));
out.println(“public class” + classname + “ {”};
out.println(“..程式碼..”);
out.println(“}”);
//關閉檔案流
out.flush();
out.close();
我們約定被建立的臨時檔名以“JavaRuntime”為頭綴(可任意命名),字尾名以“.java”結尾。一個待編譯的Java原始檔已被動態生成。下一步要從com.sun.tools.javac包中建立一個Main例項,呼叫javac.compile()方法編譯這個臨時檔案:
Private static com.sun.tools.javac.Main javac = new com.sun.tools.javac.Main();
String[] args = new String[] {“-d”, System.getProperty(“user.dir”),filename };
Int status = javac.compile(args);
假定臨時檔案透過了編譯器文法驗證等驗證,編譯成功(status值等於0,參看前表),在當前程式的執行目錄下就會多了一個Java類檔案。我們將透過執行這個Java 類檔案,來模擬執行欲動態編譯程式碼的結果。
Java提供在執行時刻載入類的特性,可動態識別和呼叫類構造方法、類欄位和類方法。java.lang.reflect.Method實現了Member介面,可以呼叫介面的方法來獲得方法類的名稱、修飾詞等。方法getRuturnType()、getParameterTypes()、getExeptionTypess()等返回被表示方法的構造資訊。Method另一個重要的特性是可以呼叫invoke()執行這個方法(詳細使用方法可以檢視java.lang.reflect包文件)。下面這段程式碼中建立一個java.lang.reflect.Method類方法,呼叫getMethod()方法獲得被拼裝的main方法的對映,這段程式碼如下:
try {
// 訪問這個類
Class cls = Class.forName(classname);
//呼叫main方法
Method main = cls.getMethod(“main”, new Class[] { String[].class });
main.invoke(null, new Object[] { new String[0] });
}catch (SecurityException se) {
debug(“access to the information is denied:” + se.toString());
}catch (NoSuchMethodException nme) {
debug(“a matching method is not found or if then name is or :
” + nme.toString());
}catch (InvocationTargetException ite) {
debug(“Exception in main: ” + ite.getTargetException());
}catch (Exception e){
debug(e.toString());
}
執行結果參如下:
Hello,This runtime code!
示範程式
下面給出了一個簡單的Java程式,這個程式說明了如何利用Sun的javac編譯器完成動態編譯Java語句。執行該程式需要計算機安裝JDK 1.2以上版本,並在classpath中或執行時指定tools.jar檔案位置。
程式結構:
◆ compile() 編譯Java程式碼,返回生成的臨時檔案;
◆ run()執行編譯的class檔案;
◆ debug()輸出除錯資訊;
◆ getClassName()從一個Java原始檔獲得類名;
◆ readLine()從控制檯讀取使用者輸入的Java Code。
Import java.io.File;
…
Public class RuntimeCode{
/**編譯器*/
private static com.sun.tools.javac.Main javac = new com.sun.tools.javac.Main();
/**等待使用者輸入JavaCode,然後編譯、執行*/
public static void main(String[] args) throws Exception{
…
run(compile(code));
}
/**編譯JavaCode,返回臨時檔案物件*/
private synchronized static File compile(String code)
throws IOException,Exception {
File file;
//在使用者當前檔案目錄建立一個臨時程式碼檔案
file = File.createTempFile(“JavaRuntime”, “.java”,
new File(System.getProperty(“user.dir”)));
//當虛擬機器退出時,刪除此臨時java原始檔
file.deleteOnExit();
//獲得檔名和類名字
String filename = file.getName();
String classname = getClassName(filename);
//將程式碼輸出到檔案
PrintWriter out = new PrintWriter(new FileOutputStream(file));
out.println(“/**”);
…
//關閉檔案流
out.flush();
out.close();
//編譯程式碼檔案
String[] args = new String[] {“-d”, System.getProperty(“user.dir”),filename };
//返回編譯的狀態程式碼
int status = javac.compile(args);
//處理編譯狀態
…
}
/**執行剛剛編譯的類檔案*/
private static synchronized void run(File file)
…
//當虛擬機器退出時,刪除此臨時編譯的類檔案
new File(file.getParent(), classname + “.class”).deleteOnExit();
try {
// 訪問這個類
Class cls = Class.forName(classname);
//對映main方法
Method main = cls.getMethod(“main”, new Class[] { String[].class });
//執行main方法
main.invoke(null, new Object[] { new String[0] });
}catch (SecurityException se) {
…
}
}
/**列印除錯資訊*/
private static void debug(String msg) {
System.err.println(msg);
}
/**根據一個java原始檔名獲得類名*/
private static String getClassName(String filename){
return filename.substring(0,filename.length()-5);
}
/**從控制檯獲得使用者輸入的Java程式碼段*/
…
}
編譯執行上述程式碼,在please input java code提示下輸入以下程式碼:
for(int i=0;i<10;i++){System.out.println(“this is:”+i);}
執行結果如下所示:
Please input java code:
for(int i=0;i<10;i++){System.out.println(“this is:”+i);}
wait....
--------------------
this is:0
this is:1
this is:2
this is:3
this is:4
this is:5
this is:6
this is:7
this is:8
this is:9
總結
在大中型企業應用系統平臺中,使用程式碼動態編譯技術結合OO程式設計模型,可在系統不菪機條件下保證系統的可擴充套件性和伸縮性。如果你是一個Java程式設計師,稍加調整以上程式碼,還可以幫助除錯小段的Java程式碼.
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/750220/viewspace-927053/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- java動態編譯Java編譯
- Java動態編譯和熱更新Java編譯
- Java動態編譯優化——提升編譯速度(N倍)Java編譯優化
- JIT-動態編譯與AOT-靜態編譯:java/ java/ JavaScript/Dart亂談編譯JavaScriptDart
- 深入理解Java的動態編譯Java編譯
- apache動態編譯/靜態編譯區別Apache編譯
- 有關Linux的可執行程式——動態編譯、靜態編譯、readelfLinux行程編譯
- 透過編譯器動態產生Java class編譯Java
- 編譯lua動態庫編譯
- Dubbo原始碼之動態編譯原始碼編譯
- nginxphp動態編譯載入模組.NginxPHP編譯
- java程式中編譯另一個java程式Java編譯
- Java程式碼的編譯與反編譯那些事兒Java編譯
- ☕【Java技術指南】「編譯器專題」深入分析探究“靜態編譯器”(JAVA\IDEA\ECJ編譯器)是否可以實現程式碼優化?Java編譯Idea優化
- Java動態程式設計---動態代理Java程式設計
- Java編譯與反編譯Java編譯
- 【譯】11. Java反射——動態代理Java反射
- 如何保護Java程式 防止Java反編譯Java編譯
- 將Java編譯為原生程式碼Java編譯
- java 程式碼編譯檢查工具Java編譯
- 基於.net standard 的動態編譯實現編譯
- C編譯: 動態連線庫 (.so檔案)編譯
- mingw下編譯zlib quazip動態庫編譯
- 詳解.NET中的動態編譯技術編譯
- Linux下如何用GCC編譯動態庫LinuxGC編譯
- .NET 中的動態編譯(生成exe檔案)編譯
- 動手編寫—動態陣列(Java實現)陣列Java
- 為了簡寫這行程式碼,我竟使用靜態和動態編譯技術行程編譯
- Java動態編譯優化——URLClassLoader 記憶體洩漏問題解決Java編譯優化記憶體
- Java動態編譯優化——ZipFileIndex記憶體洩漏問題分析解決Java編譯優化Index記憶體
- Linux下快速靜態編譯Qt以及Qt動態/靜態版本共存Linux編譯QT
- 從fdk_aac編碼器到自動靜態編譯FFmpeg編譯
- linux下靜態庫、動態庫編譯及makefile書寫Linux編譯
- linux成長之路(gcc編譯器、靜態庫、動態庫)LinuxGC編譯
- C/C++ 編譯器和偵錯程式以及靜態庫、動態庫使用匯總(轉)C++編譯
- Natasha 4.0 探索之路系列(三) 基本的動態編譯編譯
- Google Protocol buffer 學習筆記.下篇-動態編譯GoProtocol筆記編譯
- 教你如何動態除錯 iOS App(反編譯App)除錯iOSAPP編譯