Java编译器API ClassLoader

Pav*_* S. 7 java compiler-construction classloader

我正在尝试使用Java Compiler API来编译一些java类.该类从jar文件导入一些包,这些包可以由上下文ClassLoader加载,让我们称他为X,这不是系统类加载器.当我运行编译时,编译器抱怨没有识别导入.我试图指定fileManager来传递类加载器,但它没有帮助.

当调用compile方法时,它首先打印"CLASS LOADED",因此上下文ClassLoader可以找到依赖类.但是,编译本身失败(我收到"Compilation FAILED"消息)并且在编译期间我得到如下错误:

/path/to/my/Source.java:3:包my.dependency不存在import my.dependency.MyClass; ^

我究竟做错了什么?将自定义类加载器传递给compilationTask的正确方法是什么?我不能从ClassLoader中提取URL,因为它不是URLClassLoader.

我的方法在这里:

public void compile(List<File> filesToCompile) {
       JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

       StandardJavaFileManager stdFileManager =
               compiler.getStandardFileManager(null, null, null);
       Iterable<? extends JavaFileObject> fileObjects = stdFileManager
               .getJavaFileObjectsFromFiles(filesToCompile);

       FileManagerImpl fileManager = new FileManagerImpl(stdFileManager);

       CompilationTask task = compiler.getTask(null, fileManager, null, null, null, fileObjects);
       Boolean result = task.call();
       if (result == true) {
           System.out.println("Compilation has succeeded");
       } else {
           System.out.println("Compilation FAILED");
       }
}

private final class FileManagerImpl extends ForwardingJavaFileManager<JavaFileManager> {

      public FileManagerImpl(JavaFileManager fileManager) {
           super(fileManager);
      }

      @Override
      public ClassLoader getClassLoader(JavaFileManager.Location location) {
          ClassLoader def = getContextClassLoader();
          try {
               def.loadClass("my.dependency.MyClass");
               System.out.println("CLASS LOADED");
          } catch (ClassNotFoundException ex) {
               System.out.println("NOT LOADED");
          }
          return def;
      }
}
Run Code Online (Sandbox Code Playgroud)

Nor*_*wap 6

重点是,当类加载器加载类时,javac将调用JavaFileManager#list()以获取包中所有文件的列表.

因此,要使用自定义类加载器,您需要修改(或扩展)它以覆盖JavaFileManager#list().希望您可以重用一些用于类加载的逻辑.

您可能希望使用自己的实现JavaFileObject来表示类对象.然后您需要覆盖JavaFileManager#inferBinaryName()(否则javac版本将崩溃).您的实现JavaFileObject也需要覆盖(至少)JavaFileObject#openInputStream.

以下是一些提示:http://atamur.blogspot.be/2009/10/using-built-in-javacompiler-with-custom.html

此外,不要让你的生活比它应该更难,并延长ForwardingJavaFileManagerSimpleJavaFileObject.

作为参考,这是一个示例实现:

  @Override public Iterable<JavaFileObject> list(Location location,
    String packageName, Set<JavaFileObject.Kind> kinds, boolean recurse)
  throws IOException
  {
    Iterable<JavaFileObject> stdResults =
      fileManager.list(location, packageName, kinds, recurse);

    if (location != StandardLocation.CLASS_PATH
    ||  !kinds.contains(JavaFileObject.Kind.CLASS))
    {
        return stdResults;
    }

    Set<JavaFileObject> additional = pkgObjects.get(packageName);

    if (additional == null || additional.isEmpty()) {
      return stdResults;
    }

    List<JavaFileObject> out = new ArrayList<>();

    for (JavaFileObject obj : additional) {
      out.add(obj);
    }
    for (JavaFileObject obj : stdResults) {
      out.add(obj);
    }

    return out;
  }
Run Code Online (Sandbox Code Playgroud)

pkgObjects从包名到的地图在哪里JavaFileObject.填充此映射的方式取决于类加载器的工作方式.


And*_*s_D 0

这个问题已有答案了。您必须使用该getTask()方法通过选项列表设置类路径(如已接受的答案中详细描述的)。