【问题标题】:How to get the JavaCompiler to use a provided classLoader to find a class?如何让 JavaCompiler 使用提供的 classLoader 来查找类?
【发布时间】:2016-07-19 20:25:53
【问题描述】:

我正在使用来自 IBM 旧博文的 this code,该博文介绍了如何在运行时编译和使用 Java 类。代码大部分工作得很好(顺便说一句,写得很好),但不幸的是,它不适用于我的一个用例,其中正在编译的类引用另一个只能由 classLoader 提供的类提供给CharSequenceCompiler(来自博客文章),而不是由应用程序类加载器提供。

更具体地说,我传递给CharSequenceCompiler 的 ClassLoader 是一个 OSGi 类加载器。

拥有这个类加载器的包可以找到并返回一个类,比如Foo

class Foo { public static String FOO = "F"; }

我知道如果你这样做 classLoader.findClass("Foo"); 这会起作用,因为当我从调试器调用它时它会起作用。

现在,从我在运行时编译的类中,比如Dynamic,我需要使用Foo...所以我将Foo bundle 的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


    【解决方案1】:

    经过长时间的战斗,我想通了。

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

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

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

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

    其次,您需要能够从ClassLoader 资源构建JavaFileObjects,因为这是您必须返回的对象的类型。为此,您需要向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代码,这就是为什么我需要让它工作。

    【讨论】:

    • 附带说明,在我的情况下,ClassLoader 类是可枚举的,因为 OSGi 支持 BundleWiring... 在 OSGi 之外,没有安全的方法来获取 classLoader 可以加载的所有类,而无需检查 jar (所以如果你从其他东西加载类,你就卡住了)。
    【解决方案2】:

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

    例如:Byte Buddycglib

    【讨论】:

    • 谢谢,但我想从源代码中获取字节码。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-09-30
    • 1970-01-01
    • 2017-04-08
    • 2011-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多