2016-08-09 2 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의 소스가 필요하기 때문에 실패합니다. 이것은 각각의 클래스를 에 저장함으로써 달성 될 수 있는데, 여기서 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) 인 별도의 메서드로 이동되었습니다. 당신은 당신이 컴파일 할 각 클래스의 클래스 이름과 소스 코드를 저장하는 HashMap<String, String>를 사용해야합니다 위의 방법을 사용하기 위해

/* 
* 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!"); 
    } 
} 

. 예를 들어 compile(className, nameAndSource.entrySet(), classLoader)

: 당신은 예를 들어, HashMap에서 entrySet() 전달 compile로 전화를 걸 컴파일 할 준비가되면


가 여기

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

관련 문제