【问题标题】:Malformed Java package class name while loading runtime-compile source加载运行时编译源时 Java 包类名称格式错误
【发布时间】:2016-03-26 14:54:27
【问题描述】:

我有一个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"

意外的输出如下:

Package: null
Name: TestClass

我哪里错了?

【问题讨论】:

    标签: java classloader jsr199


    【解决方案1】:

    aClass.getName() 的结果是正确的。如果你想让程序返回“TestClass”,你应该使用aClass.getSimpleName()

    我查看java.lang.Class.getPackage()的源代码。它将调用Package.getPackage(this),在您的情况下调用class.getClassLoader().getPackage("example.test")。因为 SimpleClassLoader 具有覆盖方法 protected Class findClass(String name) ,您还应该覆盖方法 protected Package getPackage(String name) 并返回您期望的对象。

    【讨论】:

    • 啊,看来我在通过definePackage()定义类的时候也需要定义包
    【解决方案2】:

    example.test 包未定义,需要通过在 compile 方法中调用 definePackage() 来初始化:

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

    【讨论】:

      猜你喜欢
      • 2010-12-11
      • 2019-07-08
      • 2016-03-05
      • 1970-01-01
      • 2021-02-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-04
      相关资源
      最近更新 更多