java compiler类_Java_Java Compiler 应用实例
轉自:http://hejiangtao.iteye.com/blog/1399122
一直在用JDK1.5, 一直搞不清楚JDK1.6有啥特性, 就翻了翻, 發現這個Compiler API(JSR 199)動態編譯Java源文件功能很有意思. Compiler API如果和反射功能一起使用, 就可以實現java源代碼的動態編譯并執行這些代碼,有點動態語言的特征. 利用這些API普通用戶也可以方便的開發自己的編譯器,動態生成代碼,編譯并運行. 本文就通過一個動態編譯并運行源文件的例子簡單說明下Compile API的基本功能, 有興趣的可以深入研究下. 本實例的完成工程代碼可以從這里?下載: http://dl.iteye.com/topics/download/0807c557-4f0d-3aba-956f-9fe5c9b83962
實例中實現的功能描述:
1. 使用JavaCompiler對象的run方法編譯java源代碼,并在源代碼所在目錄生成對應的class文件
2. 使用JavaCompiler對象的getTask方法編譯java源代碼,并將對應的class文件生成到指定目錄, 并執行所生成類中指定的"printClassName"方法
環境準備:
首先回顧一下JDK, JRE,JVM的概念和關系:
JRE是java的運行環境, 說白了有JRE才能運行java類; 同時java類是運行于虛擬機(JVM)上的, 其實虛擬機是JRE的一部分, 具體來講,在windows上就是JRE下面的一個JVM.dll文件; JDK就是java開發工具箱, 具有編譯java類的功能和運行java類的功能(自身包含了一個JRE).
知道了JDK,JRE,JVM的關系,我們就應該明白,如果要在eclipse里面使用java的編譯功能必須在eclipse里面使用JDK作為Library,否則在eclipse中獲取不了JavaCompiler的對象. 設置如下圖:
懶得找JDK1.6,我就直接下載了個1.7裝了下,然后開發工具使用MyEclipse (當然用的是免費版的 -:)).
在看我們的實例分析及源碼:
首先看下run方法編譯java源文件, run方法比較簡單,但不能指定輸出路徑,監控錯誤信息, 調用后就在源碼所在目錄生成class文件,run方法的聲明如下:
int?run(InputStream?in,
OutputStream?out,
OutputStream?err,
String...?arguments)使用給定?I/O?通道和參數運行工具。按照慣例,工具如果運行成功,則返回?0;如果出現錯誤,則返回非?0?值。任何生成的診斷都將以某種未指定的格式寫入?out?或?err。
參數:
in?-?“標準”輸入;如果為?null,則使用?System.in
out?-?“標準”輸出;如果為?null,則使用?System.out
err?-?“標準”錯誤;如果為?null,則使用?System.err
arguments?-?要傳遞給工具的參數
返回:
如果成功,則返回?0;否則返回非?0?值
拋出:
NullPointerException?-?如果參數數組包含任何?null?元素。
實例源碼,注釋比較詳細,不再解釋,Compiler.java中代碼片段:
/**
*?Author:?Jiangtao?He;?Email:?ross.jiangtao.he@gmail.com
*?@param?sFullFileName:?the?java?source?file?name?with?full?path
*?@return?bRet:?true-compile?successfully,?false?-?compile?unsuccessfully
*?Description:?Compile?java?source?file?to?java?class?with?run?method
*/
public???boolean??compileFile(String?sFullFileName)
{
boolean??bRet?=??false?;
//?get?compiler
JavaCompiler?oJavaCompiler?=?ToolProvider.getSystemJavaCompiler();
//?compile?the?java?source?code?by?run?method
int??iCompileRet?=?oJavaCompiler.run(?null?,??null?,??null?,?sFullFileName);
//?set?compile?result
if??(?0??==?iCompileRet)
{
bRet?=?true?;
}
return??bRet;
}
再看下我們的getTask方法編譯java源代碼, 這個方法其實是構造了一個JavaCompiler.CompilationTask對象, 然后在調用這個對象的call方法, 在構造對象的過程中, 可以指定class的生成路徑,監控錯誤信息,調用過程如下:
1) 生成JavaCompiler對象,用于構造CompilationTask對象,并編譯java源代碼
2) 構造DiagnosticCollector對象,用于存儲診斷信息
3) 構造oStandardJavaFileManager對象,用于設置類的生成路徑, 為了方便使用java反射方法,我直接將本實例中的輸出路徑設置為工程bin目錄.實際應用中應根據場景生成道不同的目錄--比如可以根據配置或者包名來做.
4) 生成源文件迭代器Iterable對象, 用于存儲java源代碼文件完整的路徑
5) 根據上面生成的對象, 調用JavaCompiler對象的getTask構造CompilationTask對象, 并調用其call方法,編譯源代碼
再看下getTask方法的聲明:
JavaCompiler.CompilationTask?getTask(Writer?out,
JavaFileManager?fileManager,
DiagnosticListener?super?JavaFileObject>?diagnosticListener,
Iterable?options,
Iterable?classes,
Iterable?extends?JavaFileObject>?compilationUnits) 使用給定組件和參數創建編譯任務的?future。該編譯可能沒有完成,正如?CompilationTask?接口中所述。
如果提供了文件管理器,則它必須能夠處理?StandardLocation?中定義的所有位置。
參數:
out?-?用于來自編譯器的其他輸出的?Writer;如果為?null,則使用?System.err
fileManager?-?文件管理器;如果為?null,則使用編譯器的標準文件管理器
diagnosticListener?-?診斷偵聽器;如果為?null,則使用編譯器的默認方法報告診斷信息
options?-?編譯器選項;null?表示沒有選項
classes?-?類名稱(用于注釋處理),null?表示沒有類名稱
compilationUnits?-?要編譯的編譯單元;null?表示沒有編譯單元
返回:
表示編譯的對象
拋出:
RuntimeException?-?如果在用戶提供的組件中發生不可恢復的錯誤。cause?為用戶代碼中的錯誤。
IllegalArgumentException?-?如果給定的任一編譯單元具有不同于?source?的類型
源碼清單如下,Compiler.java中代碼片段:
/**
*?Author:?Jiangtao?He;?Email:?ross.jiangtao.he@gmail.com
*?@param?sFullFileName:?the?java?source?file?name?with?full?path
*?@param?sOutputPath:?the?output?path?of?java?class?file
*?@return?bRet:?true-compile?successfully,?false?-?compile?unsuccessfully
*?Description:?Compile?java?source?file?to?java?class?with?getTask
*?????method,?it?can?specify?the?class?output?path?and?catch?diagnostic
*?????information
*?@throws?IOException
*/
public???boolean??compileFile(String?sFullFileName,?String?sOutputPath)??throws??IOException
{
boolean??bRet?=??false?;
//?get?compiler
JavaCompiler?oJavaCompiler?=?ToolProvider.getSystemJavaCompiler();
//?define?the?diagnostic?object,?which?will?be?used?to?save?the
//?diagnostic?information
DiagnosticCollector?oDiagnosticCollector?=?new??DiagnosticCollector();
//?get?StandardJavaFileManager?object,?and?set?the?diagnostic?for?the
//?object
StandardJavaFileManager?oStandardJavaFileManager?=?oJavaCompiler
.getStandardFileManager(oDiagnosticCollector,?null?,??null?);
//?set?class?output?location
Location?oLocation?=?StandardLocation.CLASS_OUTPUT;
try
{
oStandardJavaFileManager.setLocation(oLocation,?Arrays
.asList(new??File[]?{??new??File(sOutputPath)?}));
//?get?JavaFileObject?object,?it?will?specify?the?java?source?file.
Iterable?extends??JavaFileObject>?oItJavaFileObject?=?oStandardJavaFileManager
.getJavaFileObjectsFromFiles(Arrays.asList(new??File(
sFullFileName)));
//?compile?the?java?source?code?by?using?CompilationTask's?call
//?method
bRet?=?oJavaCompiler.getTask(null?,?oStandardJavaFileManager,
oDiagnosticCollector,?null?,??null?,?oItJavaFileObject).call();
//print?the?Diagnostic's?information
for??(Diagnostic?oDiagnostic?:?oDiagnosticCollector
.getDiagnostics())
{
System.out.println("Error?on?line:?"
+?oDiagnostic.getLineNumber()?+?";?URI:?"
+?oDiagnostic.getSource().toString());
}
}
catch??(IOException?e)
{
//exception?process
System.out.println("IO?Exception:?"??+?e);
throw??e;
}
finally
{
//close?file?manager
if??(?null??!=?oStandardJavaFileManager)
{
oStandardJavaFileManager.close();
}
}
return??bRet;
}
編譯的方法就這兩個簡單吧, 下面我們測試下這兩個方法:
首先, 聲明下我們的compiler類的對象,初始化下編譯的類和輸出類的路徑,MyMain.java中代碼片段:
//?get?compiler?object
Compiler?oCompiler?=?new?Compiler();
//?the?java?source?file?name?with?full?path
String?sFullFileName?=?"E:\\myspace\\CompilerSample\\Sample.java";
//?define?the?output?path?of?java?class,?since?this?demo?is?ran?into
//?eclipse,?so?set?it?as?bin
String?sOutputPath?=?"bin/";
測試run方法:
//?Compile?java?source?file?to?java?class?with?run?method
boolean??bRet?=?oCompiler.compileFile(sFullFileName);
//?print?result
if??(bRet)
{
System.out.println("Compile?the?source?code?\""??+?sFullFileName
+?"\"?successfully"?);
}
else
{
System.out.println("Compile?the?source?code?\""??+?sFullFileName
+?"\"?unsuccessfully"?);
}
run方法測試,控制臺信息:
Compile?the?source?code?"E:\myspace\CompilerSample\Sample.java"?successfully
生成的類文件抓圖:
測試getTask方法,并利用java反射運行所生成類中的"printClassName"方法:
//?Compile?java?source?file,?and?output?the?class?file?into?specified
//?path
bRet?=?oCompiler.compileFile(sFullFileName,?sOutputPath);
//?print?result
if??(bRet)
{
System.out.println("Compile?the?source?code?\""??+?sFullFileName
+?"\"?successfully"?);
//?if?compile?success,?then?execute?the?printClassName?method?of?the
//?compiled?class
System.out
.println("Execute?the?printClassName?method?of?the?compiled?class:?"?);
System.out.print("??"?);
//?load?the?class
Class?oClass?=?Class.forName("Sample"?);
//?new?an?object?of?sample?class
Object?oObject?=?oClass.newInstance();
//?get?object?of?printClassName?method
Method?oMethod?=?oClass.getMethod("printClassName"?);
oMethod.invoke(oObject);
}
else
{
System.out.println("Compile?the?source?code?\""??+?sFullFileName
+?"\"?unsuccessfully"?);
}
}
運行測試方法后,控制臺打印信息:
Compile?the?source?code?"E:\myspace\CompilerSample\Sample.java"?successfully
Execute?the?printClassName?method?of?the?compiled?class:
Print?the?class?name:?Sample
生成的類文件抓圖:
至此, 通過java Compiler API動態編譯并運行源文件的例子就完了.
總結
以上是生活随笔為你收集整理的java compiler类_Java_Java Compiler 应用实例的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 计算机基础——5.2 图像与图形
- 下一篇: multiboot之ICAP
