是否可以在不加载引用类/导入的情况下加载类?

时间:2016-09-19 00:13:42

标签: java dependencies classloader circular-dependency dynamic-class-loaders

(是的,这是hacky,可能不是最好的做法,但它是最不起眼的解决方案)

我有一个涉及多个jar的项目 - 一个可运行的启动器,一个服务器,一个服务器的包装器,以及服务器的插件。

启动程序通过启动新的未连接进程,子进程或仅通过实例化它来运行包装器,具体取决于配置。对于这个问题,这应该是不重要的。

包装器使用URLClassLoader加载服务器jar并启动它,工作正常。

在启动服务器之前,包装器会查找包含服务器中通常使用的某些类路径/文件的插件,并加载它们,修补该类的正常版本。

问题是类加载器想要自动解析每个类并导入插件补丁类文件,而服务器尚未加载。

我需要阻止类加载器解析导入,或者在服务器之后加载类并用它们替换已经加载的类。据我所知,没有不稳定性和字节码操作,第二种选择是不可能的?

1 个答案:

答案 0 :(得分:1)

首先,回答你的标题问题:

  

是否可以在不加载引用类/导入的情况下加载类?

您可以通过将false传递给loadClass来选择解决该类。可以找到有关解决类所需内容的详细信息here。然后,您可以通过明确调用resolveClass在单独的步骤中执行解决方案。

然而,如果那不能得到你想要的东西,我不知道这是否是唯一可行的解​​决方案,而且它肯定很糟糕(而且肯定有更好的方法可以接近这从一开始),但如果你这样做会怎么样:

我要打电话给你作为补丁的类,必须在服务器之前加载"预加载"课程/代码。我将调用具有服务器依赖性的补丁组件,但其加载必须延迟,直到服务器加载"后加载"课程/代码。

对于每个插件:

  1. 从补丁类中删除所有后加载的东西并将其移动到另一个类,例如PluginImplementation或其他什么。然后使该实现类的实例成为您的插件类的成员,将任何必要的成员函数委托给它,但不要立即实例化PluginImplementation,并使成员字段成为类型Object(通常将其描述为an opaque pointer / the pimpl pattern。基本上你是在重构使用pimpl习语,你的预加载内容是直接编码的,你的后载内容实际上被委托给隐藏在Object后面的另一个类,而不是立即初始化。 您的目标是从插件类本身中删除服务器类的所有依赖项,将其更改为最小,该修补程序只需要加载,但是将所有的肉移动到最终隐藏在不透明指针后面的实现类。

  2. 现在正常加载所有插件。它们应该立即加载,并且由于已经从它们中删除了所有具有服务器依赖性的后加载内容,因此无法加载服务器类。

  3. 现在让你的插件暴露某种serverLoaded()initializePlugin()方法或类似的东西。 加载服务器类之后,通过并在每个加载的插件上调用它们。

  4. 最后,在上一步的初始化方法中,让您的插件使用Class.forName().newInstance() 实例化后加载类

  5. 所以,基本上,你将所有后加载的东西隐藏在一个不透明的指针后面,从而将它隐藏在类加载器中,然后在它的时间,你动态地实例化各种PluginImplementation类,从而使你的插件完全完整"但允许延迟加载与服务器相关的部分。

    缺点是这增加了一些限制,需要很多照顾。在服务器加载并调用初始化函数之前,您需要确保没有调用任何PluginImplementation - 委托方法,因为实现类还没有被实例化。

    我确信可能有更好的选择,但这是我能想到的,可能涉及最少的工作量给定你已经拥有的。你必须移动很多代码;像Eclipse这样的IDE或其他什么方法可以使代表更容易吐出代表(只是暂时使成员字段成为PluginImplementation以帮助IDE,然后在生成所有内容之后将其更改回Object代码),但它应该最小化对现有架构的*基础*更改。

    这是另一个想法,虽然我不确定它与您当前的代码有多好,或者在您的情况下甚至是可能的:

    基本上,这里的目标是使插件不再依赖于服务器本身,执行以下操作:

    1. 将您的服务器声明为界面
    2. 使您的具体服务器实现实现该接口。
    3. 更改所有插件以在该接口上操作,而不是在服务器类本身的实例上操作。
    4. 确保您的服务器类没有声明插件可能使用的任何内部类:将任何内部类分解到顶层(IDE就像Eclipse一样可以为您执行此操作)。
    5. 现在,您必须找出一种方法来告诉每个插件它正在使用的服务器的实例,但插件会将其存储在具有基本接口类型的成员中。 / p>

      这样,加载插件不会加载服务器本身,只加载基本接口。

      我认为这个想法比上述更简单,更简单,我只是不知道它是否比上述更可行。

      请注意,这两个选项都不一定guarantee success,但实际上它们可能会有效。