Java Compiler API с классами, которые зависят друг от друга - PullRequest
1 голос
/ 29 ноября 2010

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

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

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

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

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

РЕДАКТИРОВАТЬ: Я нашел решение по этой ссылке: http://www.ibm.com/developerworks/java/library/j-jcomp/index.html

Ответы [ 4 ]

2 голосов
/ 07 декабря 2011

Да, это вполне возможно, если вы правильно реализуете ForwardingJavaFileManager.Два самых важных метода: inferBinaryName () и list () .Если вы правильно установите эти два параметра, компилятор сможет разрешать ранее скомпилированные классы.

inferBinaryName() должен возвращать класс ' простое имя (например, выведенный двоичный файлимя для 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;
}

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

1 голос
/ 29 ноября 2010

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

0 голосов
/ 29 ноября 2010

Я не думаю, что вы можете избежать компиляции обоих классов. Фактически, если вы не скомпилируете их оба, есть вероятность, что у вас возникнут проблемы с двоичной совместимостью или проблемы с неверными встроенными константами.

По сути, это та же проблема, что и при компиляции одного класса, а не другого из командной строки.

Но, честно говоря, я бы не стал беспокоиться о том, чтобы попытаться оптимизировать подобную компиляцию. (И если вашему приложению требуется , чтобы иметь возможность динамически компилировать один класс, а не другой, оно, вероятно, имеет значительные проблемы с дизайном.)

0 голосов
/ 29 ноября 2010

Как, если вы поддерживаете измененное время файлов и скомпилированный (в памяти) байт-код?

...