一、使用 JavaCompiler 接口來編譯 java 源程序(最簡單的)
使用 Java API 來編譯 Java 源程式有非常多方法,目前讓我們來看一種最簡單的方法,通過 JavaCompiler 進行編譯。
我們能通過 ToolProvider 類的靜態方法 getSystemJavaCompiler 來得到一個 JavaCompiler 接 口的實例。
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
JavaCompiler 中最核心的方法是 run。通過這個方法能編譯 java 源程序。這個方法有 3 個固 定參數和 1 個可變參數(可變參數是從 Jave SE5 開始提供的一個新的參數類型,用 type... argu 表 示)。前 3 個參數分別用來為 java 編譯器提供參數、得到 Java 編譯器的輸出信息及接收編譯器的 錯誤信息,后面的可變參數能傳入一個或多個 Java 源程式文件。如果 run 編譯成功,返回 0。
int run(InputStream in, OutputStream out, OutputStream err, String... arguments)
如果前 3 個參數傳入的是 null,那么 run 方法將以標准的輸入、輸出代替,即 System.in、 System.out 和 System.err。如果我們要編譯一個 hello.java 文件,並將使用標准輸入輸出,run 的使用方法如下:
int results = tool.run(null, null, null, "Hello.java");
完整代碼如下(用的是 eclipse 工具)

package com.dongtai.demo;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
public class DynamicCompileTest {
public static void main(String[] args) throws IOException {
// 編譯程序
JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
int result = javaCompiler.run(null, null, null, "-d","./temp/","./temp/com/Hello.java");
System.out.println( result == 0 ? "恭喜編譯成功" : "對不起編譯失敗");
// 運行程序
Runtime run = Runtime.getRuntime();
Process process = run.exec("java -cp ./temp temp/com/Hello");
InputStream in = process.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String info = "";
while ((info = reader.readLine()) != null) {
System.out.println(info);
}
}
}
二、使用 StandardJavaFileManager 編譯 Java 源程序
在第一部分我們討論調用 java 編譯器的最容易的方法。這種方法能非常好地工作,但他確不 能更有效地得到我們所需要的信息,如標准的輸入、輸出信息。而在 Java SE6 中最佳的方法是使 用 StandardJavaFileManager 類。這個類能非常好地控制輸入、輸出,並且能通過 DiagnosticListener 得到診斷信息,而 DiagnosticCollector 類就是 listener 的實現。
使用 StandardJavaFileManager 需要兩步。首先建立一個 DiagnosticCollector 實例及通過 JavaCompiler 的 getStandardFileManager()方法得到一個 StandardFileManager 對象。最后通過 CompilationTask 中的 call 方法編譯源程序
每個類的具體方法參數可以查看 jase6 API 文檔。上面有很詳細的解釋
package com.dongtai.demo;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
public class DynamicCompileTest {
public static void main(String[] args) throws IOException{
// 1.創建需要動態編譯的代碼字符串
String nr = "\r\n"; //回車
String source = "package temp.com; " + nr +
" public class Hello{" + nr +
" public static void main (String[] args){" + nr +
" System.out.println(\"HelloWorld! 1\");" + nr +
" }" + nr +
" }";
// 2.將欲動態編譯的代碼寫入文件中 1.創建臨時目錄 2.寫入臨時文件目錄
File dir = new File(System.getProperty("user.dir") + "/temp"); //臨時目錄
// 如果 \temp 不存在 就創建
if (!dir.exists()) {
dir.mkdir();
}
FileWriter writer = new FileWriter(new File(dir,"Hello.java"));
writer.write(source);
writer.flush();
writer.close();
// 3.取得當前系統的編譯器
JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
// 4.獲取一個文件管理器
StandardJavaFileManager javaFileManager = javaCompiler.getStandardFileManager(null, null, null);
// 5.文件管理器根與文件連接起來
Iterable it = javaFileManager.getJavaFileObjects(new File(dir,"Hello.java"));
// 6.創建編譯任務
CompilationTask task = javaCompiler.getTask(null, javaFileManager, null, Arrays.asList("-d", "./temp"), null, it);
// 7.執行編譯
task.call();
javaFileManager.close();
// 8.運行程序
Runtime run = Runtime.getRuntime();
Process process = run.exec("java -cp ./temp temp/com/Hello");
InputStream in = process.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String info = "";
while ((info = reader.readLine()) != null) {
System.out.println(info);
}
}
}
三、從內存中動態編譯 java 程序
JavaCompiler 不僅能編譯硬盤上的 Java 文件,而且還能編譯內存中的 Java 代碼,然后使
用 reflection 來運行他們。我們能編寫一個類,通過這個類能輸入 Java 原始碼。一但建立這個對
象,你能向其中輸入任意的 Java 代碼,然后編譯和運行。

package com.dongtai.demo;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
public class DynamicCompileTest {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException{
/*
* 編譯內存中的java代碼
* */
// 1.將代碼寫入內存中
StringWriter writer = new StringWriter(); // 內存字符串輸出流
PrintWriter out = new PrintWriter(writer);
out.println("package com.dongtai.hello;");
out.println("public class Hello{");
out.println("public static void main(String[] args){");
out.println("System.out.println(\"HelloWorld! 2\");");
out.println("}");
out.println("}");
out.flush();
out.close();
// 2.開始編譯
JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
JavaFileObject fileObject = new JavaStringObject("Hello", writer.toString());
CompilationTask task = javaCompiler.getTask(null, null, null, Arrays.asList("-d","./bin"), null, Arrays.asList(fileObject));
boolean success = task.call();
if (!success) {
System.out.println("編譯失敗");
}else{
System.out.println("編譯成功");
}
URL[] urls = new URL[]{new URL("file:/" + "./bin/")};
URLClassLoader classLoader = new URLClassLoader(urls);
Class classl = classLoader.loadClass("com.dongtai.hello.Hello");
Method method = classl.getDeclaredMethod("main", String[].class);
String[] argsl = {null};
method.invoke(classl.newInstance(), argsl);
}
}
