如何让JavaCompiler使用提供的classLoader来查找类?

时间:2016-07-19 20:25:53

标签: java javac java-compiler-api

我正在使用旧的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

如果FooCharSequenceCompiler在同一个项目中,那么它可以工作......所以从正确的类加载器中加载类显然是一个问题。

我已经调试了几天(或晚上,tbh)这段代码,并且无法找出为什么我提供给编译器的classLoader甚至不会被问及这个类......

要求FileManager list()每个包中的资源,但即使我使用调试器手动将FileObject添加到返回的列表中,它仍然无效

由于调试器无法在内部渗透javac使用的本机类,因此我无法继续进行...有没有人对编译器有内在的知识,可以解释发生了什么?

2 个答案:

答案 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编译器有什么好处。是不是字节码生成选项?

例如:cglibhttp://php.net/manual/en/functions.variable-functions.php