2016-03-26 6 views
0

のロード中に、次のように私はJavaランタイムコンパイラを持っている:不正なJavaのパッケージのクラス名ランタイムコンパイルソース

public class Compiler { 

    private final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 
    private final Map<String, String> source = new HashMap<String, String>(); 
    private final MemoryFileManager manager = new MemoryFileManager(this.compiler); 

    public void add(String classname, String fileContent) { 
     add(Collections.singletonMap(classname, fileContent)); 
    } 

    public void add(Map<String, String> map) { 
     source.putAll(map); 
    } 

    public void compile() { 
     List<Source> list = new ArrayList<Source>(); 
     for (Map.Entry<String, String> entry : source.entrySet()) { 
      list.add(new Source(entry.getKey(), JavaFileObject.Kind.SOURCE, entry.getValue())); 
     } 
     this.compiler.getTask(null, this.manager, null, null, null, list).call(); 
    } 

    public byte[] getByteCode(String name) { 
     return this.manager.map.get(name).toByteArray(); 
    } 
} 

および添付の例簡単なクラスローダ:

public class SimpleClassLoader extends ClassLoader { 

    private String classname; 
    private byte[] byteCode; 

    public SimpleClassLoader(String classname, byte[] byteCode) { 
     super(SimpleClassLoader.class.getClassLoader()); 
     this.classname = classname; 
     this.byteCode = byteCode; 
    } 

    @Override 
    protected Class<?> findClass(String name) throws ClassNotFoundException { 
     if(name.equals(classname)){ 
      return defineClass(name, byteCode, 0, byteCode.length); 
     } 
     return super.findClass(name); 
    } 
} 

クラスをコンパイルしてロードする場合

Compiler compiler = new Compiler(); 
    String className = "example.test.TestClass"; 
    String source = "package example.test; public class TestClass{}"; 

    compiler.add(className, source); 
    compiler.compile(); 

    byte[] byteCode = compiler.getByteCode(className); 

    Class<?> aClass = Class.forName(className, true, new SimpleClassLoader(className, byteCode)); 
    System.out.println("Package: " + aClass.getPackage()); // Should be "example.text" 
    System.out.println("Name: " + aClass.getSimpleName()); // Should be "TestClass" 

outpu:、結果のクラスのパッケージ名はクラス名に連結されますこれは予期しないことです。

Package: null 
Name: TestClass 

ここで私は間違っていますか?

答えて

0

example.testパッケージが未定義だったとコンパイルメソッド内definePackage()を呼び出すことによってinitalizedすることが必要だった。

public void compile() { 
    List<Source> list = new ArrayList<Source>(); 
    for (Map.Entry<PackageClass, String> entry : source.entrySet()) { 
     list.add(new Source(entry.getKey().getCanonicalName(), JavaFileObject.Kind.SOURCE, entry.getValue())); 
     if(getPackage(entry.getKey().getPackage()) == null) { 
      definePackage(entry.getKey().getPackage(), null, null, null, null, null, null, null); 
     } 
    } 
    this.compiler.getTask(null, this.manager, null, null, null, list).call(); 
} 
0

aClass.getName()の結果は正しいです。プログラムが "TestClass"を返すようにするには、aClass.getSimpleName()を使用する必要があります。

java.lang.Class.getPackage()のソースコードを確認します。あなたのケースではclass.getClassLoader().getPackage("example.test")を呼び出すPackage.getPackage(this)を呼び出します。 SimpleClassLoaderにはオーバーライドメソッドprotected Class findClass(String name)があるため、メソッドprotected Package getPackage(String name)もオーバーライドして、指定したオブジェクトを返す必要があります。

+1

ああ、)(私もdefinePackage経由でクラスを定義するパッケージを定義する必要がありますように見えます –