(JDK 11)通过依赖于库的反射来运行JAR存档

时间:2018-11-20 17:27:49

标签: java javafx kotlin

注意:我对Java 9中引入的Module系统的经验很少。

我有一个Java进程,应该通过反射来加载和执行它来运行另一个Jar文件。 请注意,这两个Jar文件都依赖于JavaFX框架,该框架已从版本11中的JDK分离出来,因此可能会第二次加载。

以下是适用于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

编辑

在xtratic的帮助下,解决方案实际上很简单:

我只是将child URLClassloader添加到CoreManager#bootstrap反射调用中,即CoreManager(已加载jar)的bootstrap方法,然后将该URLClassloader传递给所有FXML加载器。

1 个答案:

答案 0 :(得分:4)

当您使用新的String value时,似乎正在调用方法 ,但是会抛出URLClassLoader,并由javafx.fxml.LoadException和{ {1}}。

即使您使用自己的PrivilegedActionException加载类,看起来FXML解析器仍试图使用内置的类加载器来加载FX bean,而后者并不了解FXML中的那些类。 / p>

您需要使FXMLLoader使用正确的类加载器,该类加载器包含fxml引用的所有类。阅读InvocationTargetException的源代码,以了解其如何处理类加载。如果可能,您可能需要修改库以执行fxml加载。查看URLClassloader或可能设置FXMLLoader,以便FXMLLoader.setClassLoader(urlClassLoader)使用调用的类加载器。


SecurityManager

FXMLLoader

我猜测Java11可能已经更改了它的类加载器。

请参阅this link(下面粘贴的内容)

  

投射到URL类加载器
  Java 9和模块系统改进了平台的类加载策略,该策略以新类型实现,而在Java 11中,应用程序类加载器就是该类型。这意味着它不再是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; } URLClassLoader序列将不再执行。这是另一个典型的示例,其中Java 11在严格意义上是向后兼容的(因为从未指定过URLCassLoader),但仍然可能导致移植挑战。

     

症状
  这是非常明显的。您会收到一个ClassCastException抱怨新的AppClassLoader不是URLClassLoader:

(URLClassLoader) getClass().getClassLoader()
  

修复
  类加载器可能已强制转换为特定于URLClassLoader的访问方法。如果是这样,您可能不得不面对一些严重的变化。
  ...
  如果您已经使用(URLClassLoader) ClassLoader.getSystemClassLoader()通过附加到类路径来动态加载用户提供的代码(例如,作为插件基础结构的一部分),那么您必须找到一种新的方式来做到这一点,因为它不可能使用Java 11完成。您应该考虑为此创建一个新的类加载器。这样做还有一个好处,因为新类没有加载到应用程序类加载器中,所以您可以摆脱它们。您还应该仔细阅读各层-它们为您提供了一个全新的抽象,用于加载全新的模块图。