(JDK 11) Запустите архив JAR через отражение, которое зависит от библиотек - PullRequest
0 голосов
/ 20 ноября 2018

Примечание : У меня очень мало опыта работы с системой Module, представленной в Java 9.

У меня есть Java-процесс, который должен запустить другой файл Jar, загрузив и выполнив его с помощью отражения.Обратите внимание, что оба Jar-файла зависят от среды JavaFX, которая была отсоединена от JDK в Версии 11 и в результате может быть загружена во второй раз.

Здесь Исходная версия, которая работала на JDK версии 8:

val ideFile = File(binFolder, "Sk-IDE.jar")
val classLoader = ClassLoader.getSystemClassLoader() as URLClassLoader
val method = URLClassLoader::class.java.getDeclaredMethod("addURL", URL::class.java)
method.isAccessible = true
method.invoke(classLoader, ideFile.toURI().toURL())
val coreManager = Class.forName("com.skide.CoreManager")
val instance = coreManager.newInstance()
coreManager.getDeclaredMethod("bootstrap", Array<String>::class.java).invoke(instance, State.args)

(Рефлексивный вызов addURL был необходим, потому что в противном случае загрузчик Jar не смог бы загрузить свои fxml-файлы)

Этот подход вызывает это исключение в JDK 11:

java.lang.ClassCastException: class jdk.internal.loader.ClassLoaders$AppClassLoader cannot be cast to class java.net.URLClassLoader (jdk.internal.loader.ClassLoaders$AppClassLoader and java.net.URLClassLoader are in module java.base of loader 'bootstrap')

Подход к созданию нового URLClassLoader:

val child = URLClassLoader(arrayOf(URL(ideFile.toURI().toURL().toString())), Installer::class.java.classLoader)
val coreManager = Class.forName("com.skide.CoreManager", true, child)
val instance = coreManager.newInstance()
Platform.runLater {
    coreManager.getDeclaredMethod("bootstrap", Array<String>::class.java).invoke(instance, State.args)
}

Создает исключение, связанное с JavaFX:

Exception in thread "JavaFX Application Thread" [20.11.2018 20:13:41 | ERROR] java.security.PrivilegedActionException: java.lang.reflect.InvocationTargetException
[20.11.2018 20:13:41 | ERROR]   at java.base/java.security.AccessController.doPrivileged(Native Method)
[20.11.2018 20:13:41 | ERROR]   at com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:427)
[20.11.2018 20:13:41 | ERROR]   at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
[20.11.2018 20:13:41 | ERROR]   at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
[20.11.2018 20:13:41 | ERROR]   at com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
[20.11.2018 20:13:41 | ERROR]   at java.base/java.lang.Thread.run(Thread.java:834)
[20.11.2018 20:13:41 | ERROR] Caused by: java.lang.reflect.InvocationTargetException
[20.11.2018 20:13:41 | ERROR]   at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[20.11.2018 20:13:41 | ERROR]   at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
[20.11.2018 20:13:41 | ERROR]   at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
[20.11.2018 20:13:41 | ERROR]   at java.base/java.lang.reflect.Method.invoke(Method.java:566)
[20.11.2018 20:13:41 | ERROR]   at com.skide.installer.Installer$start$2$1$1.run(Installer.kt:249)
[20.11.2018 20:13:41 | ERROR]   at com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:428)
[20.11.2018 20:13:41 | ERROR]   ... 6 more
[20.11.2018 20:13:41 | ERROR] Caused by: javafx.fxml.LoadException: 
unknown path:9

[20.11.2018 20:13:41 | ERROR]   at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2625)
[20.11.2018 20:13:41 | ERROR]   at javafx.fxml.FXMLLoader.access$700(FXMLLoader.java:105)
[20.11.2018 20:13:41 | ERROR]   at javafx.fxml.FXMLLoader$ValueElement.processAttribute(FXMLLoader.java:930)
[20.11.2018 20:13:41 | ERROR]   at javafx.fxml.FXMLLoader$InstanceDeclarationElement.processAttribute(FXMLLoader.java:980)
[20.11.2018 20:13:41 | ERROR]   at javafx.fxml.FXMLLoader$Element.processStartElement(FXMLLoader.java:227)
[20.11.2018 20:13:41 | ERROR]   at javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:752)
[20.11.2018 20:13:41 | ERROR]   at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2722)
[20.11.2018 20:13:41 | ERROR]   at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2552)
[20.11.2018 20:13:41 | ERROR]   at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2450)
[20.11.2018 20:13:41 | ERROR]   at com.skide.CoreManager$bootstrap$1.invoke(CoreManager.kt:47)
[20.11.2018 20:13:41 | ERROR]   at com.skide.CoreManager$bootstrap$1.invoke(CoreManager.kt:24)
[20.11.2018 20:13:41 | ERROR]   at com.skide.CoreManager.bootstrap(CoreManager.kt:136)
[20.11.2018 20:13:41 | ERROR]   ... 12 more
[20.11.2018 20:13:41 | ERROR] Caused by: java.lang.ClassNotFoundException: com.skide.gui.controllers.SplashGuiController
[20.11.2018 20:13:41 | ERROR]   at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:583)
[20.11.2018 20:13:41 | ERROR]   at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
[20.11.2018 20:13:41 | ERROR]   at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
[20.11.2018 20:13:41 | ERROR]   at javafx.fxml.FXMLLoader$ValueElement.processAttribute(FXMLLoader.java:928)
[20.11.2018 20:13:41 | ERROR]   ... 21 more

Каков наилучший способ решить эту проблему?

С уважением, Liz3

EDIT

С помощью xtratic решение было на самом деле простым:

Я просто добавил child URLClassloader кCoreManager # загрузочный рефлексивный вызов, метод начальной загрузки CoreManager (загруженный файл jar), который передает этот URLClassloader всем загрузчикам FXML.

1 Ответ

0 голосов
/ 20 ноября 2018

Когда вы используете новый URLClassLoader, кажется, что метод вызывается , но выбрасывает javafx.fxml.LoadException, который оборачивается PrivilegedActionException и InvocationTargetException.

Даже если вы загружаете свой класс своим собственным URLClassloader, похоже, что синтаксический анализатор FXML пытается загрузить бины FX встроенным загрузчиком классов, который не знает об этих классах в FXML.

Вам нужно будет заставить FXMLLoader использовать правильный загрузчик классов, который содержит все классы, на которые ссылается fxml .Прочитайте источник FXMLLoader, чтобы получить представление о том, как он обрабатывает загрузку классов.Возможно, вам придется изменить библиотеку, выполнив загрузку fxml, если это возможно.Посмотрите на FXMLLoader.setClassLoader(urlClassLoader) или, возможно, настройте SecurityManager, чтобы FXMLLoader использовал вызывающий загрузчик классов.


FXMLLoader.java

/**
 * Returns the classloader used by this serializer.
 * @since JavaFX 2.1
 */
@CallerSensitive
public ClassLoader getClassLoader() {
    if (classLoader == null) {
        final SecurityManager sm = System.getSecurityManager();
        final Class caller = (sm != null) ?
                Reflection.getCallerClass() :
                null;
        return getDefaultClassLoader(caller);
    }
    return classLoader;
}

private static ClassLoader getDefaultClassLoader(Class caller) {
    if (defaultClassLoader == null) {
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            final ClassLoader callerClassLoader = (caller != null) ?
                    caller.getClassLoader() :
                    null;
            if (needsClassLoaderPermissionCheck(callerClassLoader, FXMLLoader.class.getClassLoader())) {
                sm.checkPermission(GET_CLASSLOADER_PERMISSION);
            }
        }
        return Thread.currentThread().getContextClassLoader();
    }
    return defaultClassLoader;
}

У меня былопредположение, что Java11, возможно, изменил свой загрузчик классов.

См. эту ссылку (содержание вставлено ниже)

Приведение к URL-загрузчику классов
Java 9 и модульная система улучшили стратегию загрузки классов платформы, которая реализована в новом типе, а в Java 11 загрузчик классов приложений относится к этому типу.Это означает, что это больше не URLClassLoader, поэтому случайные (URLClassLoader) getClass().getClassLoader() или (URLClassLoader) ClassLoader.getSystemClassLoader() последовательностей больше не будут выполнять .Это еще один типичный пример, когда Java 11 обладает обратной совместимостью в строгом смысле (поскольку он никогда не указывался как URLCassLoader), но, тем не менее, может вызвать проблемы с миграцией.

Симптомы
Этоодно очень очевидно.Вы получите ClassCastException с жалобой на то, что новый AppClassLoader не является URLClassLoader:

Exception in thread "main" java.lang.ClassCastException:
java.base/jdk.internal.loader.ClassLoaders$AppClassLoader
cannot be cast to java.base/java.net.URLClassLoader
    at monitor.Main.logClassPathContent(Main.java:46)
    at monitor.Main.main(Main.java:28)

Исправления
Загрузчик класса, вероятно, был приведен для доступа к конкретным методамв URLClassLoader.Если это так, возможно, вам придется столкнуться с серьезными изменениями.
...
Если вы использовали URLClassLoader для динамической загрузки предоставленного пользователем кода (например, как часть инфраструктуры плагина), добавив кпуть к классу, тогда вам нужно найти новый способ сделать это, поскольку это невозможно сделать с помощью Java 11. Вместо этого вам следует рассмотреть возможность создания нового загрузчика классов для этого .Это дает дополнительное преимущество, заключающееся в том, что вы сможете избавиться от новых классов, поскольку они не загружаются в загрузчик классов приложений.Вы также должны прочитать о слоях - они дают вам чистую абстракцию для загрузки совершенно нового графика модуля.

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