2010-11-29 9 views
1

Я использую Java Compiler API для компиляции классов в памяти. То есть классы компилируются в байт-код (нет .classes-файлы, хранящиеся на диске), а затем загружаются путем восстановления байт-кода.Java Compiler API с классами, которые зависят друг от друга

Иногда мне нужно скомпилировать класс, который зависит от другого, также скомпилированного класса. Например, компилируйте класс A, затем скомпилируйте класс B, который зависит от класса A.

Для решения этого вопроса я передаю как класс A, так и класс B в качестве единиц компиляции, необходимых методу getTask API-интерфейса компилятора.

Однако мне действительно не нравится это решение, так как он заставляет меня перекомпилировать класс A, который уже был скомпилирован.

Есть ли способ обойти это?

EDIT: Я нашел решение по этой ссылке: 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

Это приводит к очевидному вопросу о том, почему вы хотите скомпилировать класс А отдельно первым. Почему бы просто не скомпилировать все за один раз?

+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; 
} 

После того, как у вас есть эти методы должным образом реализованы, остальные просто работает. Наслаждайтесь!