2016-08-09 37 views
1

我已经使用this answer作为在运行时编译类的参考,并且它可以正常工作。 所以现在我需要扩展实际上找不到的编译类。无法扩展在运行时编译的类

我已经试过这样:

import java.io.File; 
import java.net.URI; 
import java.net.URL; 
import java.net.URLClassLoader; 
import java.util.Arrays; 

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

public class CompileSourceInMemory { 

    public static Class<?> compile(String className, String sourceCode, URLClassLoader classLoader) throws Exception { 
     JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 
     DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>(); 

     JavaFileObject file = new JavaSourceFromString(className, sourceCode); 

     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) { 
      return Class.forName(className.replace(".", "/"), true, classLoader); 
     } else { 
      throw new Exception("Didn't work!"); 
     } 
    } 

    public static void main(String args[]) throws Exception { 
     URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] { new File("").toURI().toURL() }); 
     StringBuilder sourceCode = new StringBuilder(); 
     // sourceCode.append("package br.bla;"); 
     sourceCode.append("public class HelloWorld {"); 
     sourceCode.append(" public static void main(String args[]) {"); 
     sourceCode.append(" System.out.append(\"This is in another java file\");"); 
     sourceCode.append(" }"); 
     sourceCode.append("}"); 
     Class<?> helloWorld = compile("HelloWorld", sourceCode.toString(), classLoader); 

     sourceCode = new StringBuilder(); 
     // sourceCode.append("package br.bla;"); 
     sourceCode.append("public class ExtendedHelloWorld extends HelloWorld {"); 
     sourceCode.append(" public int i = 2;"); 
     sourceCode.append("}"); 

     Class<?> extendedHelloWorld = compile("ExtendedHelloWorld", sourceCode.toString(), classLoader); 
     Object object = extendedHelloWorld.newInstance(); 

     return; 
    } 
} 

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; 
    } 
} 

输出功率为:

Success: true 
compiler.err.cant.resolve 
ERROR 
40 
40 
50 
JavaSourceFromString[string:///ExtendedHelloWorld.java] 
cannot find symbol 
    symbol: class HelloWorld 
Success: false 

有谁知道如何编译我的ExtendedHelloWorld类?

回答

2

编译HelloWorld成功,因为它只需要它自己的源代码。当您尝试编译ExtendedHelloWorld时,它会失败,因为它需要自己的源代码和HelloWorld的源代码。这可以通过将每个类存储在HashMap<String, String>中来实现,其中key是类名,并且value是类的源代码。


我建议您对代码进行一些更改。

我会压缩你的compile方法并将其分解为两种不同的编译方法。当你想编译一个没有扩展从内存编译的类的类时,第一个会被使用。当你想编译一个扩展从内存编译的类的类时,第二个会被使用。

/* 
* Method to compile a class which doesn't extend a class that's been compiled from memory. 
*/ 
public static Class<?> compile(String className, String sourceCode, URLClassLoader classLoader) throws Exception { 
    return compileHelper(className, classLoader, Arrays.asList(new JavaSourceFromString(className, sourceCode))); 
} 

/* 
* Method to compile a class which extends a class that's been compiled from 
* memory. 
* 
* This method takes in the class name, a Set of Map.Entry<String, String>, 
* which contains class names and their sources, and a class loader. This 
* method iterates over the entries in the Set, creates JavaFileObjects from 
* the class names and their sources and adds each JavaFileObject to an 
* ArrayList which will be used in the 'compileHelper' method. 
*/ 
public static Class<?> compile(String className, Set<Map.Entry<String, String>> nameAndSource, URLClassLoader classLoader) throws Exception { 
    List<JavaFileObject> compilationUnits = new ArrayList<>(); 

    for(Entry<String, String> entry : nameAndSource) { 
     compilationUnits.add(new JavaSourceFromString(entry.getKey(), entry.getValue())); 
    } 

    return compileHelper(className, classLoader, compilationUnits); 
} 

上述方法然后调用辅助方法,它实际上编译类。此方法与您的compile方法非常相似,但诊断的输出已移至单独的方法printDiagnostics(diagnostics)

/* 
* Helper method that actually does the compiling. 
*/ 
private static Class<?> compileHelper(String className, URLClassLoader classLoader, Iterable<? extends JavaFileObject> compilationUnits) throws Exception { 
    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 
    DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>(); 
    CompilationTask task = null; 
    boolean success = false; 

    // debug compilation units section 
    System.out.println("Compiling " + className); 

    System.out.println(" - compilationUnits "); 
    for(JavaFileObject o : compilationUnits) { 
     System.out.println(" + " + o.toString()); 
    } 
    // end debug 

    task = compiler.getTask(null, null, diagnostics, null, null, compilationUnits); 
    success = task.call(); 

    if (success) { 
     System.out.println("Successful compilation of " + className); 
     return Class.forName(className.replace(".", "/"), true, classLoader); 
    } else { 
     System.out.println("Failed while compiling " + className); 
     printDiagnostics(diagnostics); 
     throw new Exception("It didn't work!"); 
    } 
} 

为了使用上述方法,你需要使用HashMap<String, String>存储每个要编译的类的类名和源代码。然后,当你准备好编译做出compile电话在entrySet()经过距HashMap,例如:compile(className, nameAndSource.entrySet(), classLoader)

例如:

public static void main(String args[]) throws Exception { 
    Map<String, String> nameAndSource = new HashMap<>(); 
    URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] { new File("").toURI().toURL() }); 
    String className; 
    StringBuilder sourceCode; 

    // HelloWorld class 
    className = "HelloWorld"; 
    sourceCode = new StringBuilder();   
    sourceCode.append("public class HelloWorld {"); 
    sourceCode.append(" public static void main(String args[]) {"); 
    sourceCode.append(" System.out.append(\"This is in another java file\");"); 
    sourceCode.append(" }"); 
    sourceCode.append("}"); 

    // pass the class name and source code to 'compile'   
    Class<?> helloWorld = compile(className, sourceCode.toString(), classLoader); 

    // add HelloWorld class name and source code to HashMap 
    nameAndSource.put(className, sourceCode.toString()); 

    // ExtendedHelloWorldClass 
    className = "ExtendedHelloWorld"; 
    sourceCode = new StringBuilder(); 
    sourceCode.append("public class ExtendedHelloWorld extends HelloWorld {"); 
    sourceCode.append(" public int num = 2;"); 
    sourceCode.append("}"); 

    // add ExtendedHelloWorld class name and source code to HashMap 
    nameAndSource.put(className, sourceCode.toString()); 

    // here's where we pass in the nameAndSource entrySet() 
    Class<?> extendedHelloWorld = compile(className, nameAndSource.entrySet(), classLoader); 

    return; 
} 

这里是完整的源代码是什么我试图描述如上:

public class CompileSourceInMemory { 

    /* 
    * Method to compile a class which extends a class that's been compiled from 
    * memory. 
    * 
    * This method takes in the class name, a Set of Map.Entry<String, String>, 
    * which contains class names and their sources, and a class loader. This 
    * method iterates over the entries in the Set, creates JavaFileObjects from 
    * the class names and their sources and adds each JavaFileObject to an 
    * ArrayList which will be used the private compile method. 
    */ 
    public static Class<?> compile(String className, Set<Map.Entry<String, String>> nameAndSource, URLClassLoader classLoader) throws Exception { 
     List<JavaFileObject> compilationUnits = new ArrayList<>(); 

     for (Entry<String, String> entry : nameAndSource) { 
      compilationUnits.add(newJavaSourceFromString(entry.getKey(), entry.getValue())); 
     } 

     return compileHelper(className, classLoader, compilationUnits); 
    } 

    /* 
    * Method to compile a class which doesn't extend a class that's been 
    * compiled from memory. 
    */ 
    public static Class<?> compile(String className, String sourceCode, URLClassLoader classLoader) throws Exception { 
     return compileHelper(className, classLoader, Arrays.asList(new JavaSourceFromString(className, sourceCode))); 
    } 

    /* 
    * Helper method that actually does the compiling. 
    */ 
    private static Class<?> compileHelper(String className, URLClassLoader classLoader, Iterable<? extends JavaFileObject> compilationUnits) throws Exception { 
     JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 
     DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>(); 
     CompilationTask task = null; 
     boolean success = false; 

     // debug compilation units section 
     System.out.println("Compiling " + className); 

     System.out.println(" - compilationUnits "); 
     for (JavaFileObject o : compilationUnits) { 
      System.out.println(" + " + o.toString()); 
     } 
     // end debug 

     task = compiler.getTask(null, null, diagnostics, null, null, compilationUnits); 
     success = task.call(); 

     if (success) { 
      System.out.println("Successful compilation of " + className); 
      return Class.forName(className.replace(".", "/"), true, classLoader); 
     } else { 
      System.out.println("Failed while compiling " + className); 
      printDiagnostics(diagnostics); 
      throw new Exception("It didn't work!"); 
     } 
    } 

    public static void main(String args[]) throws Exception { 
     Map<String, String> nameAndSource = new HashMap<>(); 
     URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] { new File("").toURI().toURL() }); 
     String className; 
     StringBuilder sourceCode; 

     // HelloWorld Class 
     className = "HelloWorld"; 
     sourceCode = new StringBuilder(); 
     sourceCode.append("public class HelloWorld {"); 
     sourceCode.append(" public static void main(String args[]) {"); 
     sourceCode.append(" System.out.append(\"This is in another java file\");"); 
     sourceCode.append(" }"); 
     sourceCode.append("}"); 

     // pass the class name and source code to 'compile' 
     Class<?> helloWorld = compile(className, sourceCode.toString(), classLoader); 

     // add HelloWorld class name and source code to HashMap 
     nameAndSource.put(className, sourceCode.toString()); 

     // ExtendedHelloWorld Class 
     className = "ExtendedHelloWorld"; 
     sourceCode = new StringBuilder(); 
     sourceCode.append("public class ExtendedHelloWorld extends HelloWorld {"); 
     sourceCode.append(" public int num = 2;"); 
     sourceCode.append("}"); 

     // add ExtendedHelloWorld class name and source code to HashMap 
     nameAndSource.put(className, sourceCode.toString()); 

     // pass the nameAndSource entrySet() to 'compile' 
     Class<?> extendedHelloWorld = compile(className, nameAndSource.entrySet(), classLoader); 

     // ExtendedExtendedHelloWorld Class 
     className = "ExtendedExtendedHelloWorld"; 
     sourceCode = new StringBuilder(); 
     sourceCode.append("public class ExtendedExtendedHelloWorld extends ExtendedHelloWorld {"); 
     sourceCode.append(" public void printNum() { System.out.println(num); }"); 
     sourceCode.append("}"); 

     // add ExtendedExtendedHelloWorld class name and source code to HashMap 
     nameAndSource.put(className, sourceCode.toString()); 

     // pass the nameAndSource entrySet() to 'compile' 
     Class<?> extendedExtendedHelloWorld = compile(className, nameAndSource.entrySet(), classLoader); 
     Object eehw = extendedExtendedHelloWorld.newInstance(); 

     return; 
    } 

    private static void printDiagnostics(DiagnosticCollector<JavaFileObject> diagnostics) { 
     StringBuilder sb = new StringBuilder("-- Diagnostics --\n"); 
     for (Diagnostic<?> d : diagnostics.getDiagnostics()) { 
      sb.append(String 
        .format("d.getCode() - %s%nd.getKind() - %s%nd.getPosition() - %d%nd.getStartPosition() - %d%nd.getEndPosition() - %d%nd.getSource() - %s%nd.getMessage(null) - %s%n", 
          d.getCode(), d.getKind().toString(), 
          d.getPosition(), d.getStartPosition(), 
          d.getEndPosition(), d.getSource().toString(), 
          d.getMessage(null))); 
     } 
     System.out.println(sb.append("--").toString()); 
    } 
} 

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; 
    } 
} 

这里是上面运行代码的输出:

Compiling HelloWorld 
- compilationUnits 
    + JavaSourceFromString[string:///HelloWorld.java] 
Successful compilation of HelloWorld 
Compiling ExtendedHelloWorld 
- compilationUnits 
    + JavaSourceFromString[string:///ExtendedHelloWorld.java] 
    + JavaSourceFromString[string:///HelloWorld.java] 
Successful compilation of ExtendedHelloWorld 
Compiling ExtendedExtendedHelloWorld 
- compilationUnits 
    + JavaSourceFromString[string:///ExtendedHelloWorld.java] 
    + JavaSourceFromString[string:///ExtendedExtendedHelloWorld.java] 
    + JavaSourceFromString[string:///HelloWorld.java] 
Successful compilation of ExtendedExtendedHelloWorld 
+0

它不适用于像br.bla.ExtendedHelloWorld这样的全限定类名。 – hbelmiro