2011-04-28 92 views
6

我想用Java編譯器API來編譯一些java類。該類從jar文件中導入一些可以通過上下文ClassLoader加載的包,我們稱之爲X,它不是系統類加載器。當我運行編譯時,編譯器抱怨沒有識別導入。我試圖指定fileManager來傳遞類加載器,但它沒有幫助。Java編譯器API類加載器

當編譯方法被調用時,它首先打印「CLASS LOADED」,所以上下文ClassLoader可以找到依賴類。然而,編譯本身失敗(我收到「編譯失敗」消息),並在編譯期間,我得到這樣的錯誤:

/path/to/my/Source.java:3:包my.dependency不存在 導入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; 
     } 
} 

回答

0

This question has the answer。您必須使用getTask()方法通過選項列表設置類路徑(如接受的答案中所述)。

+1

非常感謝,我會檢查出來。但是,沒有辦法設置現有的classLoader嗎? – 2011-04-28 13:47:57

+0

@Pavel S. - 我懷疑它是否合理,爲編譯器工具提供一個與實際應用程序類路徑分開的類路徑。想象一下,你已經加載了一個類的一個版本,但想用另一個版本來編譯這些類。這*需要*類分離。 – 2011-04-28 13:51:38

0

對於來自不同的jar文件,你可以使用反射API嘗試加載類是..refer以下鏈接http://download.oracle.com/javase/tutorial/reflect/index.html簡單的方法..

+0

感謝您的回答。但那不完全是我需要的。我需要編譯* .java源文件並讓編譯器知道如何加載依賴包。 – 2011-04-28 13:42:37

3

重點在於,當類加載器加載類時,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; 
    } 

哪裏pkgObjects是從包名稱的地圖JavaFileObject。你填寫這張地圖的方式取決於你的類加載器是如何工作的。