一篇參考博客:http://www.cnblogs.com/fangwenyu/archive/2011/10/12/2209051.htmlhtml
在Python中有一個exec()函數,一樣在JavaScript中有一個eval()函數,這兩個函數有一個類似的特色,那就是能夠在裏面傳入一段Python代碼或者JavaScript代碼,發現居然能夠運行該代碼。java
可是遺憾的是,Java中並不存在這樣的函數,因而突發奇想,咱們可不能夠在Java中實現一個相似的函數,用來執行Java代碼呢?linux
咱們知道,Python和JavaScript屬於腳本語言,也就是非編譯型語言,它們並不存在先編譯後執行的過程的。而Java、C++這種編譯型的語言,通常都是先編譯,後執行。對於Java來講,編譯生成.class文件,而後JVM運行.class文件。而咱們若是想要將Java代碼傳入方法中,而後運行,那麼就不能採用傳統的編譯+運行了,採用Java提供的動態編譯和動態加載的機制。數據庫
下面我經過一個簡單的例子加以說明,寫一個eval()方法,能夠執行System.out.println("Hello, " + str);這行代碼:windows
Eval.java:數組
package com.darrenchan; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.tools.JavaCompiler; import javax.tools.JavaFileObject; import javax.tools.SimpleJavaFileObject; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; public class Eval { /* * 從java6版本開始,已經支持動態編譯了,你能夠在運行期直接編譯.java文件,執行.class文件,而且可以得到相關的輸入輸出, * 甚至還能監聽相關的事件。 * java的動態編譯提供了多種渠道,好比,能夠動態編譯一個字符串,也能夠是文本文件,也能夠是編譯過的字節碼文件(.class文件), * 甚至能夠是存放在數據庫中的明文代碼或字節碼,只要是符合java規範的就均可以在運行期動態加載,其實現方式就是實現JavaFileObject * 接口,重寫getCharContent、openInputStream、openOutputStream,或者實現JDK * 已經提供的兩個SimpleJavaFileObject、ForwardingJavaFileObject。下面我演示一下,如何動態編譯一個字符串。 */ /** * Java動態編譯演示 */ public static void main(String[] args) throws Exception { // Java源代碼 String sourceStr = "public class Hello{public String sayHello(String name){return \"Hello, \"+name;}}"; // 類及文件名 String clsName = "Hello"; // 方法名 String methodName = "sayHello"; /** * 當前編譯器:注意,若是是用的jdk1.6的版本(建議使用jdk1.7,1.7是沒有任何問題的),ToolProvider. * getSystemJavaCompiler()拿到的對象將會爲null, * 緣由是須要加載的Tools.jar不在jdk安裝目錄的jre目錄下,須要手動將lib目錄下的該jar包拷貝到jre下去,詳情請參考: * http://www.cnblogs.com/fangwenyu/archive/2011/10/12/2209051.html */ JavaCompiler cmp = ToolProvider.getSystemJavaCompiler(); // Java標準文件管理器 StandardJavaFileManager fm = cmp.getStandardFileManager(null, null, null); // Java文件對象 JavaFileObject jfo = new StringJavaObject(clsName, sourceStr); // 編譯參數,相似於javac <options> 中的options List<String> optionsList = new ArrayList<String>(); // 編譯文件的存放地方,注意:此處是爲Eclipse工具特設的 optionsList.addAll(Arrays.asList(new String[] { "-d", "./bin" })); // 要編譯的單元 List<JavaFileObject> jfos = Arrays.asList(new JavaFileObject[] { jfo }); // 設置編譯環境 JavaCompiler.CompilationTask task = cmp.getTask(null, fm, null, optionsList, null, jfos); // 編譯成功 if (task.call()) { // 生成對象 Object obj = Class.forName(clsName).newInstance(); Class<? extends Object> cls = obj.getClass(); // 調用sayHello方法 Method m = cls.getMethod(methodName, String.class); // 第一個參數是執行該方法的主調,後面若干個參數是執行該方法時傳入該方法的實參 String str = (String) m.invoke(obj, "陳馳"); System.out.println(str); } } }
StringJavaObject.java:ide
package com.darrenchan; import java.io.IOException; import java.net.URI; import javax.tools.SimpleJavaFileObject; public class StringJavaObject extends SimpleJavaFileObject { /** * 源代碼 */ private String content = ""; /** * 遵循Java規範的類名及文件 */ public StringJavaObject(String javaFileName, String content){ super(_createStringJavaObjectUri(javaFileName), Kind.SOURCE); this.content = content; } /** * 產生一個URL資源路徑 */ private static URI _createStringJavaObjectUri(String javaFileName) { //注意此處未設置包名 return URI.create("String:///" + javaFileName + Kind.SOURCE.extension); } /** * 文本文件代碼 */ @Override public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { return content; } }
通過測試,最終的運行結果符合預期,以下所示:函數