2010-11-29 72 views
1

我正在使用Java编译器API编译内存中的类。也就是说,类被编译为字节码(没有存储在磁盘中的类文件),然后通过重新构造字节码进行加载。Java编译器API与相互依赖的类

有时,我需要编译一个依赖另一个类的类,这个类也是内存中编译的类。例如:编译A类,然后编译依赖于A类的B类。

为了解决这个问题,我传递了A类和B类作为编译器API的getTask方法所需的编译单元。

但是,我真的不喜欢这个解决方案,因为它使我重新编译已编译的A类。

有没有办法解决这个问题?

编辑:我发现通过这个环节的解决方案:http://www.ibm.com/developerworks/java/library/j-jcomp/index.html

+0

对不起,如果我很慢,但我不明白你发布的链接如何解决这个问题。我自己遇到了同样的问题......如果我可以分别编译类,我的API就会变得更简洁,易于管理。如果您可以将解决方案作为答案发布,我将不胜感激。 :) – stevevls 2011-12-06 14:08:59

+1

实际上,我刚刚解决了这个问题,所以我已经为后人的缘故发布了一个答案。感谢您的好问题! – stevevls 2011-12-07 13:07:50

回答

1

这导致的,为什么你想先单独编译A类明显的问题。为什么不只是一次编译一切?

+0

我认为你错过了这一点。我发布了一个例子。您的解决方案实际上并非可扩展或模块化。 – halfwarp 2010-11-30 14:31:01

0

如果维护文件和(内存中)编译的字节码的修改时间,该怎么办?

0

我不认为你可以避免编译这两个类。事实上,如果你不编译它们,你最终可能会遇到二进制兼容性问题,或者出现内联常量不正确的问题。

这与本质上是相同的问题,如果您从命令行编译一个类而不是另一个类。

但说实话,我不会担心试图优化这样的编译。 (如果你的应用程序需要能够动态编译一个类,而不是其他的,它有可能有显著的设计问题。)

2

是的,这是完全可能的,只要你正确地贯彻ForwardingJavaFileManager。两种最重要的方法是inferBinaryName()list()。如果你正确设置了这两个,编译器将能够解析你之前编译的类。

inferBinaryName()必须返回类'simple name(例如,com.test.Test的推断二进制名称将只是Test)。下面是我实现的(我的JavaFileObject子类被称为InAppJavaFileObject):

@Override 
public String inferBinaryName(Location location, JavaFileObject javaFileObject) { 

    if(location == StandardLocation.CLASS_PATH && javaFileObject instanceof InAppJavaFileObject) { 
     return StringUtils.substringBeforeLast(javaFileObject.getName(), ".java"); 
    } 

    return super.inferBinaryName(location, javaFileObject); 
} 

注意,我是从年底剥离“的.java”。在构建JavaFileObject时,文件名必须以“.java”结尾,但如果稍后不去除后缀,编译器将无法找到您的类。

list()有点复杂,因为你必须小心地与你的委托文件管理器一起玩。在我的实现,我把地图完全限定类名来我的JavaFileObject子类,我可以遍历:

@Override 
public Iterable<JavaFileObject> list(Location action, String pkg, Set<JavaFileObject.Kind> kind, boolean recurse) throws IOException { 

    Iterable<JavaFileObject> superFiles = super.list(action, pkg, kind, recurse); 

    // see if there's anything in our cache that matches the criteria. 
    if(action == StandardLocation.CLASS_PATH && (kind.contains(JavaFileObject.Kind.CLASS) || kind.contains(JavaFileObject.Kind.SOURCE))) { 

     List<JavaFileObject> ourFiles = new ArrayList<JavaFileObject>(); 
     for(Map.Entry<String,InAppJavaFileObject> entry : files.entrySet()) { 
      String className = entry.getKey(); 
      if(className.startsWith(pkg) && ("".equals(pkg) || pkg.equals(className.substring(0, className.lastIndexOf('.'))))) { 
       ourFiles.add(entry.getValue()); 
      } 
     } 

     if(ourFiles.size() > 0) { 
      for(JavaFileObject javaFileObject : superFiles) { 
       ourFiles.add(javaFileObject); 
      } 

      return ourFiles; 
     } 
    } 

    // nothing found in our hash map that matches the criteria... return 
    // whatever super came up with. 
    return superFiles; 
} 

一旦你拥有了这些方法正确实施,其余的只是工作。请享用!