2011-11-03 42 views
24

Tôi đã tìm thấy nhiều tài liệu tham khảo giải thích làm thế nào để lập trình biên dịch một lớp Java bằng cách sử dụng lớp JavaCompiler:Có thể lập trình biên dịch mã nguồn java chỉ trong bộ nhớ không?

JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 
int result = compiler.run(null, null, null, "a_file_name"); 

Tuy nhiên, tôi muốn biết nếu có một thư viện mã nguồn mở cho phép tôi biên dịch mã nguồn tạo lập trình (do đó không có tệp src được tham gia) và tạo một số mã byte trong luồng đầu ra (không tạo tệp lớp trong hệ thống tệp).

Ví dụ, tôi đang tìm kiếm để có thể viết một cái gì đó như thế này:

InputStream input = generateSourceCode(); 
OutputStream output = getByteCode(input); 
doCoolStuffWithByteCode(output); 

Cảm ơn sự giúp đỡ nào.

+0

Xem trình biên dịch [SSCCE ** dựa trên văn bản **] (http://pscode.org/stbc/) để có bản trình diễn. về những gì James & Brian đang đề cập đến. STBC sử dụng 'JavaCompiler' /' SimpleJavaFileObject'. –

Trả lời

38

Để bắt đầu, hãy xem JavaCompiler API. Về cơ bản:

  1. Tạo lớp Java trong chuỗi.
  2. Đặt chuỗi vào lớp mở rộng SimpleJavaFileObject.
  3. Biên dịch bằng cách sử dụng phiên bản JavaCompiler.

Cuối cùng, hãy gọi phương thức cho lớp mới.


Dưới đây là một example làm việc với JDK6 +:

import java.io.IOException; 
import java.io.PrintWriter; 
import java.io.StringWriter; 
import java.lang.reflect.InvocationTargetException; 
import java.net.URI; 
import java.util.Arrays; 

import javax.tools.Diagnostic; 
import javax.tools.DiagnosticCollector; 
import javax.tools.JavaCompiler; 
import javax.tools.JavaFileObject; 
import javax.tools.SimpleJavaFileObject; 
import javax.tools.ToolProvider; 
import javax.tools.JavaCompiler.CompilationTask; 
import javax.tools.JavaFileObject.Kind; 

public class CompileSourceInMemory { 
    public static void main(String args[]) throws IOException { 
    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 
    DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>(); 

    StringWriter writer = new StringWriter(); 
    PrintWriter out = new PrintWriter(writer); 
    out.println("public class HelloWorld {"); 
    out.println(" public static void main(String args[]) {"); 
    out.println(" System.out.println(\"This is in another java file\");");  
    out.println(" }"); 
    out.println("}"); 
    out.close(); 
    JavaFileObject file = new JavaSourceFromString("HelloWorld", writer.toString()); 

    Iterable<? extends JavaFileObject> compilationUnits = Arrays.asList(file); 
    CompilationTask task = compiler.getTask(null, null, diagnostics, null, null, compilationUnits); 

    boolean success = task.call(); 
    for (Diagnostic diagnostic : diagnostics.getDiagnostics()) { 
     System.out.println(diagnostic.getCode()); 
     System.out.println(diagnostic.getKind()); 
     System.out.println(diagnostic.getPosition()); 
     System.out.println(diagnostic.getStartPosition()); 
     System.out.println(diagnostic.getEndPosition()); 
     System.out.println(diagnostic.getSource()); 
     System.out.println(diagnostic.getMessage(null)); 

    } 
    System.out.println("Success: " + success); 

    if (success) { 
     try { 
     Class.forName("HelloWorld").getDeclaredMethod("main", new Class[] { String[].class }) 
      .invoke(null, new Object[] { null }); 
     } catch (ClassNotFoundException e) { 
     System.err.println("Class not found: " + e); 
     } catch (NoSuchMethodException e) { 
     System.err.println("No such method: " + e); 
     } catch (IllegalAccessException e) { 
     System.err.println("Illegal access: " + e); 
     } catch (InvocationTargetException e) { 
     System.err.println("Invocation target: " + e); 
     } 
    } 
    } 
} 

class JavaSourceFromString extends SimpleJavaFileObject { 
    final String code; 

    JavaSourceFromString(String name, String code) { 
    super(URI.create("string:///" + name.replace('.','/') + Kind.SOURCE.extension),Kind.SOURCE); 
    this.code = code; 
    } 

    @Override 
    public CharSequence getCharContent(boolean ignoreEncodingErrors) { 
    return code; 
    } 
} 
+0

+1 Cảm ơn rất nhiều về các con trỏ. Liên kết cuối cùng hiển thị hầu như những gì tôi đang tìm kiếm. Sự khác biệt duy nhất là rõ ràng nó đòi hỏi phải biết tên của lớp được biên dịch trước, và điều duy nhất tôi có là mã nguồn đầy đủ của nó. – Sergio

+0

Bạn có thể chỉ cần tìm lớp học cho từ 'lớp công khai' và từ tiếp theo sẽ là tên lớp có lẽ. –

+0

Tôi biết tôi có thể quét mã nguồn theo cách thủ công, chỉ cần tự hỏi liệu có điều gì đó thanh lịch hơn có thể được thực hiện hay không. Cảm ơn ! – Sergio

0

Chúng tôi đã có cuộc trao đổi về trường hợp sử dụng này trong JavaOne 2016 (câu hỏi là loại cũ, nhưng có vẻ là một số lợi ích vẫn).

Có một số repository với các ví dụ về tạo mã thực tế bằng cách sử dụng javac trong bộ nhớ.

Cụ thể xem SimpleJavaCompiler để biết ví dụ về cách thực hiện điều này trong bộ nhớ đề cập đến an toàn luồng (chúng tôi sử dụng nó trong ngữ cảnh của máy chủ) cho một lớp duy nhất. Nó có thể dễ dàng được điều chỉnh cho một kịch bản nhiều lớp.

Ngoài ra còn có các lớp học để xử lý việc tải lớp và tạo mã (phạm vi biến, tạo tên duy nhất, đặt tên bóng, v.v.).

Các vấn đề liên quan