Компилировать Java-класс во время выполнения с зависимостями от вложенного JAR - PullRequest
0 голосов
/ 26 декабря 2018

В приложении с весенней загрузкой во время выполнения я делаю следующее:

  1. Создание класса Java
  2. Компиляция
  3. Доступ к некоторым статическим полямскомпилированный класс с использованием отражения.

Я основал свой код на этом посте и получил проблему компиляции моего сгенерированного класса во время выполнения.При запуске в IDE компиляция работает просто замечательно, но при запуске из jar-файла с пружинной загрузкой происходит сбой, говоря, что символы отсутствуют или какой-то пакет не существует.Класс, который я компилирую, имеет зависимости от других классов, которые находятся в jar под \BOOT-INF\lib\, и кажется, что компилятор не может загрузить эти классы, используя существующий classLoader.

Я следовал в этом посте, который предполагает решение этой конкретной проблемы, но я получил UnsupportedOperationException из метода

default Iterable<Set<Location>> listLocationsForModules(Location location) throws IOException {
    throw new UnsupportedOperationException();
}

интерфейса JavaFileManager.

Я столкнулся с другим возможным решением с учетом тут но мне не совсем точно с полной реализацией.

Это похоже на хорошо известную проблему при компиляции класса во время выполнения, есть ли какое-то ясное решение для этого?

Обновление: в настоящее время я использую Java 10.0.2.

1 Ответ

0 голосов
/ 31 декабря 2018

Хотя вы явно не упомянули об этом, я думаю, что вы работаете с версией Java с модулями (JDK 9+), но руководства, которым вы следовали, относятся к более ранним версиям, начиная с Java 6.Вот почему вы получаете ошибку о неподдерживаемом listLocationsForModules, потому что разработчики JDK модифицировали FileManager с помощью методов по умолчанию, которые выдают UnsupportedOperationException.

Если вы на самом деле не хотите использовать версиюJava больше 8, я бы придерживался JDK8, это будет намного проще!

Я продолжу, если вы действительно хотите использовать Java 9 и выше (однако протестировал мой код в Java 11):

Для работы с модулями достаточно, чтобы ваш файловый менеджер делегировал стандартный файловый менеджер:

@Override
public Location getLocationForModule(Location location, String moduleName) throws IOException {
    return standardFileManager.getLocationForModule(location, moduleName);
}

@Override
public Location getLocationForModule(Location location, JavaFileObject fo) throws IOException {
    return standardFileManager.getLocationForModule(location, fo);
}

@Override
public Iterable<Set<Location>> listLocationsForModules(Location location) throws IOException {
    return standardFileManager.listLocationsForModules(location);
}

@Override
public String inferModuleName(Location location) throws IOException {
    return standardFileManager.inferModuleName(location);
}

Я также счел необходимым изменить код Atamur для явной проверки базового Java-модуля (чтобы мы моглиразрешите java.lang в Java 9+!) и делегируйте стандартному файловому менеджеру, как это было бы для пути к классу платформы в предыдущих версиях:

@Override
public Iterable<JavaFileObject> list(Location location, String packageName, Set<JavaFileObject.Kind> kinds, boolean recurse) throws IOException {
    boolean baseModule = location.getName().equals("SYSTEM_MODULES[java.base]");
    if (baseModule || location == StandardLocation.PLATFORM_CLASS_PATH) { // **MODIFICATION CHECK FOR BASE MODULE**
        return standardFileManager.list(location, packageName, kinds, recurse);
    } else if (location == StandardLocation.CLASS_PATH && kinds.contains(JavaFileObject.Kind.CLASS)) {
        if (packageName.startsWith("java") || packageName.startsWith("com.sun")) {
            return standardFileManager.list(location, packageName, kinds, recurse);
        } else { // app specific classes are here
            return finder.find(packageName);
        }
    }
    return Collections.emptyList();

}

Обновления

Некоторые другие моменты:

Извлечение встроенных пружинных классов загрузки:

Получите jarUri, выполнив поиск последнего индекса '!»в каждом packageFolderURL, как в комментарии Taeyun Kim , а не в первом, как в исходном примере.

 private List<JavaFileObject> processJar(URL packageFolderURL) {
  List<JavaFileObject> result = new ArrayList<JavaFileObject>();
  try {
    // Replace:
    // String jarUri = packageFolderURL.toExternalForm().split("!")[0];
    // With:
    String externalForm = packageFolderURL.toExternalForm();
    String jarUri = externalForm.substring(0, externalForm.lastIndexOf('!'));


    JarURLConnection jarConn = (JarURLConnection) packageFolderURL.openConnection();
    String rootEntryName = jarConn.getEntryName();
    int rootEnd = rootEntryName.length()+1;
    // ...

Это позволяет пакету PackageInternalsFinder возвращать CustomJavaFileObject с полными URI для классов ввстроенные пружинные банки (ниже BOOT-INF/lib), которые затем разрешаются с помощью обработчика URI банок с пружинной загрузкой , который регистрируется аналогично объяснению в этом ответе .Обработка URI должна происходить автоматически через загрузочную пружину.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...