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

问题描述 投票:1回答:2

我在旧的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使用的本机类,因此我无法继续进行...有没有人有编译器的内部知识可以解释发生了什么?

java javac java-compiler-api
2个回答
2
投票

经过长时间的战斗,我发现了这一点。

我发现基于java.tools API的内存中java编译器几乎所有实现的问题是,即使它们允许你传入ClassLoader来加载你编译的类,classLoader也只用于加载新类,但不是为了获得可以在编译的Java代码中使用的类。

出于这个原因,我的用例(如问题中所解释的)不适用于IBM博客文章(或其他项目,如OpenHFT Java-Runtime-Compiler)中显示的代码。

如果您希望由编译器提供的ClassLoader加载的类(在应用程序ClassLoader中不可见)可以被编译的类使用,则需要两件事。

首先,ClassLoader类必须是可枚举的。然后,JavaFileManager可以使用它来正确实现list()方法。对于每个包,此方法必须返回ClassLoader可以根据需要加载的类。

其次,您需要能够从JavaFileObject资源构建ClassLoaders,因为这是您必须返回的对象的类型。要做到这一点,你需要向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命令,这就是为什么我需要让这个工作。


0
投票

使用真正的java编译器有什么好处。不是字节码生成选项吗?

例如:Byte Buddycglib

© www.soinside.com 2019 - 2024. All rights reserved.