如何更改 getSystemJavaCompiler 的类加载器

How can I change classloader of getSystemJavaCompiler

我正在使用 Java 编译器 API 动态编译 Java 源代码。我生成的源文件继承自com.example.BaseClass,这只是一个普通的class,不是动态生成的。生成的 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
                                       ^

我尝试更改编译器的 class 加载程序,以便编译器在 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() returns

java.net.FactoryURLClassLoader@39a5ae48

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

java 标准编译器进行查找的方式有些奇怪,它并不总是正确解析出 运行 class 路径。无论如何,它使用 JavaFileManager.list 调用来完成该解决方案。

它会在尝试查找您的基地的过程中至少调用它 4 次 class。覆盖 ForwardingJavaFileManager and pass that into getTask 并让它查找资源并 return 它。

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