我正在使用旧的IBM博客文章中的this code来了解如何在运行时编译和使用Java类。代码大部分工作得很好(顺便说一句,编写得很好),但不幸的是,对于我来说,它在我的一个用例中不起作用,其中被编译的类引用另一个只能由classLoader提供的类提供给CharSequenceCompiler
(来自博客文章),而不是应用程序classLoader。
更具体地说,我传入CharSequenceCompiler
的ClassLoader是一个OSGi classLoader。
拥有此classLoader的bundle可以找到并返回一个类,比如Foo
。
class Foo { public static String FOO = "F"; }
我知道如果你classLoader.findClass("Foo");
这样做是有效的,因为当我从调试器调用它时它会工作。
现在,从我在运行时编译的类,说Dynamic
,我需要使用Foo
...所以我将Foo
包的ClassLoader传递给CharSequenceCompiler
,然后要求它编译Dynamic
:
class Dynamic { public static String D = Foo.FOO; }
这会导致以下错误:
error: cannot find symbol
Foo.FOO;
^
symbol: variable Foo
如果Foo
与CharSequenceCompiler
在同一个项目中,那么它可以工作......所以从正确的类加载器中加载类显然是一个问题。
我已经调试了几天(或晚上,tbh)这段代码,并且无法找出为什么我提供给编译器的classLoader甚至不会被问及这个类......
要求FileManager
list()
每个包中的资源,但即使我使用调试器手动将FileObject
添加到返回的列表中,它仍然无效
由于调试器无法在内部渗透javac使用的本机类,因此我无法继续进行...有没有人对编译器有内在的知识,可以解释发生了什么?
答案 0 :(得分:2)
经过长时间的战斗,我已经想到了这一点。
我发现基于java.tools API的内存中java编译器的几乎所有实现的问题是,即使它们允许你传入ClassLoader
来加载你编译的类,classLoader使用仅来加载新类,但不能获取可以在正在编译的Java代码中使用的类。
出于这个原因,我的用例(如问题中所述)不适用于IBM博客文章(或OpenHFT Java-Runtime-Compiler等其他项目)中显示的代码。
如果您希望由编译器提供的ClassLoader
加载的类(在应用程序ClassLoader
中不可见)可以被编译的类使用,则需要两件事。< / p>
首先,ClassLoader类必须是可枚举的。然后JavaFileManager
可以使用它来正确实现list()
方法。对于每个包,此方法必须返回ClassLoader
可以加载的类。
其次,您需要能够从JavaFileObject
资源构建ClassLoader
,因为这是您必须返回的对象的类型。为此,您需要向ClassLoader
询问类的字节码流(使用getResourceAsStream("Class.class")
),然后创建一个JavaFileObjectImpl
。基本上,这是伪代码:
fileObject = new JavaFileObjectImpl( pathMinusDotClass, JavaFileObject.Kind.CLASS );
fileObject.openOutputStream()
.write( classLoader.getInputStream( path ) );
现在,编译器将知道它可以从提供的classLoader加载哪些类,一切正常。
我在我的OSGiaaS project上的一个真正的编译器中实现了这个,它尚未发布,但我打算尽快完成(2016年7月写)...它在shell中包含一个可以运行的Java命令任意Java代码,这就是我需要让它工作的原因。
答案 1 :(得分:0)
使用真正的java编译器有什么好处。是不是字节码生成选项?
例如:cglib或http://php.net/manual/en/functions.variable-functions.php