gpt4 book ai didi

java - URLClassLoader 在同一文件夹中找到 X 但找不到 Y

转载 作者:行者123 更新时间:2023-11-30 06:49:30 24 4
gpt4 key购买 nike

总体思路:我正在为 java 编写一个加载器,它允许动态重新加载类以允许更改实现,而无需重新启动整个程序以保持主应用程序运行并最大限度地减少停机时间。每个外部代码段都按“模块”分组,每个模块都有一个带有“onEnable、postEnable、onDisable”入口/导出点的主类,并且可以包含任意数量的类。要加载模块,需要指定包含入口点的类,然后加载。我将在下面将它们称为“模块”和“附加类”,“模块”是通过实现“公共(public)接口(interface)模块”包含上述功能的类,“附加类”是指模块将使用的所有内容运行时但本身并不是一个模块(例如,我们有一个名为“Car Implements Module”的模块,该模块需要一个“Engine”类才能运行 ->“Car”是模块,“Engine”是一个附加类” )

我最初加载模块的代码(名称是包含完整类名(包括路径)的字符串,稍后给出示例):

Class<?> clazz = mainLoader.loadClass(name);
Module module = (Module) clazz.newInstance();
addLoadedModule(module);
enableLoadedModule(module);

下面是我如何在模块已经存在时重新加载模块,以便我可以覆盖实现。 “m”是应该重新加载的模块的当前实现的实例。

boolean differs = false;
Class<?> newClass = null;
try (URLClassLoader cl = new URLClassLoader(urls, mainLoader.getParent()))
{
// Try to load the class and check if it differs from the already known one
newClass = cl.loadClass(m.getClass().getName());
differs = m.getClass() != newClass;
}
catch (IOException | ClassNotFoundException e)
{
// Class couldn't be found, abort.
e.printStackTrace();
return;
}
if (!differs)
{
// New class == old class -> no need to reload it
return;
}
Module module = null;
try
{
// Try to instantiate the class
module = (Module) newClass.newInstance();
}
catch (InstantiationException | IllegalAccessException e)
{
// Can't instantiate, abort
e.printStackTrace();
return;
}
// Check versions, only reload if the new implementation's version differs from the current one. Version is a custom annotation, don't worry about that; the version check works fine
Version oldVersion = m.getClass().getAnnotation(Version.class);
Version newVersion = module.getClass().getAnnotation(Version.class);
if (oldVersion.equals(newVersion))
{
return;
}
// And if everything went well, disable and remove the old module from the list, then add and enable the new module.
disableModule(m);
modules.remove(m);
modules.put(module, false);
enableLoadedModule(module);

这是mainLoader,urls是一个URL[],指向包含要加载的外部类的位置:

mainLoader = new URLClassLoader(urls, this.getClass().getClassLoader());

当我尝试重新加载需要多个类的实现时,问题就出现了:

A 类模块需要 B 类才能运行。这就是当我尝试动态加载然后重新加载类 A 时发生的情况:

加载 A -> “当然,但我需要 B。” -> 自动加载 B -> “好了,A 现在工作正常了。”重新加载 A -> “当然,但我需要 B。” -> 由于找不到 B 而崩溃

两个类都位于完全相同的文件夹中,结构如下:

  • A 类实现模块:com/foo/bar/A.class
  • B 类:com/foo/bar/B.class
  • 网址:[“com/foo/bar/”]

我使用 load("com.foo.bar.A") 调用该函数,该函数在第一次尝试加载它时有效,但在尝试如上所述重新加载它时失败。

当尝试加载“单类模块”时它工作正常,当模块依赖于额外的外部类时就会出现问题。我尝试使用不同的类加载器在重新加载过程中用作 URLClassLoader 的父级,这些类加载器是 sysloader、Module.class.getClassLoader()、mainLoader(使用该类加载器,它永远不会找到新的类定义,因为它已经知道它,因此甚至不会再次尝试从驱动器加载它)和 mainLoader.getParent()、旧模块的类加载器以及模块类加载器的父级。

我可能只是在监督一些明显的事情,但我不明白为什么它第一次会设法加载“额外”类,但当我重新加载基类时失败......

如果您需要任何调试输出或确切的错误,请告诉我,我用解释什么作用的注释替换了调试输出,因此我得到了有关何时发生的情况的相当详细的日志,但我似乎没有必要,因为它很好地完成了整个“检查然后加载”过程,但在尝试启用模块时它崩溃了。模块的“onEnable”方法需要额外的类 B,这就是它失败的地方。正如我所说,如果您需要实现类 A 和 B、模块、任何其他代码或调试输出,请告诉我,我将根据要求添加它们。

最佳答案

您可以尝试以下几种方法:

  1. 创建 UrlClassLoader 的扩展,以便您可以跟踪它何时加载类以及使用什么类加载器加载该类。
  2. 您的另一个问题是确保这些类在“默认”类路径上都不可用,因为这将导致使用该版本。您不会覆盖默认的类加载行为,即首先检查该类的父级。
  3. 您可能面临的另一个问题与虚拟机缓存类的方式有关 - 我不完全确定它是如何工作的 - 但根据我的经验,似乎一旦加载了一个类,它就会将其放入共享存储空间,这样就不会再次加载该类。在加载该共享空间类的类加载器无法访问之前,该共享空间类不会被卸载。

关于java - URLClassLoader 在同一文件夹中找到 X 但找不到 Y,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43089597/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com