Java實現相似eval()函數或exec()函數的功能

一篇參考博客: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提供的動態編譯和動態加載的機制。數據庫

一、動態編譯。
咱們能夠調用Process執行javac。但這種方式坦白來講很差。由於javac的命令參數寫法和操做系統有關,也就是windows和linux的寫法有少許不一樣。後來發現jdk提供一個動態編譯的類。
JavaCompiler javac;
javac = ToolProvider.getSystemJavaCompiler();
int compilationResult = javac.run(null,null,null, "-g","-verbose",javaFile);
這樣就能夠動態進行編譯。前兩個參數是輸入參數、輸出參數,我以爲沒有什麼用,第三個參數是編譯輸出信息,默認輸出到System.out.err裏面。從第四個參數開始,就是javac的參數,能夠用數組,也能夠直接逗號分割。
二、動態加載。
動態加載實際就是調用ClassLoader。固然須要反射機制調用其中的一個內部方法,使之變成外部可調用的方法。
File file = new File("/Users/yangming/Work/DevWorkSpace/ssac/gx_hx/test/");
URLClassLoader classloader = (URLClassLoader) ClassLoader.getSystemClassLoader();
Method add = URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{URL.class});
add.setAccessible(true); //表示Method在使用時應該取消Java語言的訪問權限檢查
add.invoke(classloader, new Object[]{file.toURI().toURL()});
Class c = classloader.loadClass("Test");
Object o = c.newInstance();
Method m = c.getDeclaredMethod("getString");
m.invoke(o, null);
這樣就完成了類的動態加載。

 

下面我經過一個簡單的例子加以說明,寫一個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;  
    }  
}

 

 

 

通過測試,最終的運行結果符合預期,以下所示:函數

相關文章
相關標籤/搜索