【问题标题】:How can I change classloader of getSystemJavaCompiler如何更改 getSystemJavaCompiler 的类加载器
【发布时间】:2018-09-21 08:49:29
【问题描述】:

我正在使用 Java 编译器 API 动态编译 Java 源代码。我生成的源文件继承自com.example.BaseClass,它只是一个普通的类,不是动态生成的。生成的 Java 源代码如下所示:

public class Foo implements com.example.BaseClass
{
    @Override
    public Integer getAnswer(com.example.Context context) throws Exception
    {
        return ...;
    }
}

在 IDE 中运行时一切正常,但打包到 Springboot jar 后,我的 com.example.BaseClass 被移动到 BOOT-INF/classes/com.example.BaseClass。动态编译时,我现在得到:

/Foo.java:1: error: package com.example does not exist 
public class Foo implements com.example.BaseClass
                                       ^

我尝试更改编译器的类加载器,以便编译器在 BOOT-INF/classes 中搜索。

    ClassLoader before = Thread.currentThread().getContextClassLoader();
    Thread.currentThread().setContextClassLoader(new CustomClassloader(before));
    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    Thread.currentThread().setContextClassLoader(before);

但是,调试显示我的 CustomClassloader.loadClass(String name) 方法从未被调用。更多调试表明 compiler.getClass().getClassloader() 返回

java.net.FactoryURLClassLoader@39a5ae48

因此,Compiler 实例不使用 CustomClassloader。如何让编译器使用我的 CustomClassloader?当然也欢迎更好的解决编译问题的解决方案:-)。

【问题讨论】:

  • 在获得 JavaCompiler 实例之前,如果不是交换类加载器,而是 getClass().forName("com.example.BaseClass") 会发生什么
  • 无变化;编译器仍然抱怨包不存在。

标签: java spring-boot dynamic classloader java-compiler-api


【解决方案1】:

关于 java 标准编译器如何进行查找有一些奇怪的地方,它并不总是正确地解析出正在运行的类路径。无论如何,它使用JavaFileManager.list 调用来解决这个问题。

在尝试查找您的基类的过程中,它会调用它至少 4 次。覆盖 ForwardingJavaFileManager 并将其传递给 getTask 并让它查找资源并返回它。

或者,您可以使用Janino in-momeory compiler 库,它在内存文件系统中设置了一个伪造的文件系统(不编译到磁盘),并且仍然使用平台编译器并为您整理所有这些类路径的废话。

【讨论】:

  • @Iscoughlin 感谢您的意见。我创建了一个 JavaFileManager,list() 方法确实找到了 BaseClass。但是,列表方法必须返回 Iterable。我已经使用匿名类实现了 JavaFileObject。当代码即将编译时,我收到以下消息: java.lang.RuntimeException: java.lang.IllegalArgumentException: com.example.MyCompilingClass$1$1 我假设 IllegalArgument 是由 Compiler 类无法找到匿名内部引起的班级。使用 SimpleJavaFileObject 的子类而不是匿名类时出现相同的消息。
  • 您只需要简单的文件对象。 kind 应该是 Kind.CLASS 并且 URI 应该解析到你的 ...com/example/BaseClass.class 资源。
  • 另外,你可以在这里看到一个字节数组文件管理器的完整实现:​​janino-compiler.github.io/janino/apidocs/org/codehaus/commons/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多