加载运行时编译源时格式错误 Java 包 class 名称
Malformed Java package class name while loading runtime-compile source
我有一个 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);
}
}
当编译和加载 class 时,生成的 class 的包名称连接到 class 名称中:
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
我哪里错了?
aClass.getName()
的结果是正确的。如果你想要程序 return "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)
和 return 您期望的对象。
example.test
包未定义,需要通过在编译方法中调用 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();
}
我有一个 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);
}
}
当编译和加载 class 时,生成的 class 的包名称连接到 class 名称中:
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
我哪里错了?
aClass.getName()
的结果是正确的。如果你想要程序 return "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)
和 return 您期望的对象。
example.test
包未定义,需要通过在编译方法中调用 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();
}