2011-11-03 14 views
24

he encontrado muchas referencias que explican cómo compilar programación una clase Java utilizando la clase JavaCompiler:¿Es posible compilar programáticamente código fuente java solo en memoria?

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

Sin embargo, me gustaría saber si hay una biblioteca de código abierto que me permitió compilar el código fuente generado mediante programación (por lo tanto, sin un archivo src) y generar un código de bytes en una secuencia de salida (sin generar un archivo de clase en el sistema de archivos).

Por ejemplo, Busco a ser capaz de escribir algo como esto:

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

Gracias por cualquier ayuda.

+0

Consulte el [SSCCE ** Text Based ** Compiler] (http://pscode.org/stbc/) para obtener una demostración. a lo que se refieren James y Brian. El STBC usa 'JavaCompiler' /' SimpleJavaFileObject'. –

Respuesta

38

Para comenzar, mire el JavaCompiler API. Básicamente:

  1. Cree la clase Java en una cadena.
  2. Ponga la cadena en la clase que se extiende SimpleJavaFileObject.
  3. Compilar utilizando una instancia JavaCompiler.

Finalmente, llame a los métodos la nueva clase.


Aquí es un example que trabaja con 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 Muchas gracias por los consejos. El último enlace muestra casi lo que estoy buscando. La única diferencia es que aparentemente requiere saber el nombre de la clase que se compilará por adelantado, y lo único que tengo es su código fuente completo. – Sergio

+0

¿Puedes buscar en la clase la palabra 'clase pública' y la siguiente palabra será quizás el nombre de la clase. –

+0

Sé que puedo escanear manualmente el código fuente, solo me preguntaba si se podría hacer algo más elegante. Gracias ! – Sergio

0

Nos dimos una charla sobre este caso de uso en JavaOne 2016 (la pregunta es un poco viejo, pero parece que hay cierto interés todavía)

Hay un repository con ejemplos de generación de código práctica usando javac en memoria.

Mire específicamente SimpleJavaCompiler para ver un ejemplo de cómo hacer esto en la memoria que trata con la seguridad de la secuencia (lo usamos en el contexto de un servidor) para una sola clase. Se podría adaptar fácilmente para un escenario de clase múltiple.

También hay clases para tratar la carga de clases y la generación de código (determinación del alcance de las variables, generación de nombres únicos, sombreado de nombres, etc.).

Cuestiones relacionadas