java動態編譯

隨風而逝,只是飄零發表於2016-05-27

一、使用 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);
    
    }
}

引用至:http://www.cnblogs.com/snake-hand/p/3159694.html

相關文章