当前位置: 首页 > 知识库问答 >
问题:

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

赵晟睿
2023-03-14

(是的,这是hacky,可能不是最佳实践,但它是体积最小的解决方案)

我有一个涉及几个jar的项目——一个可运行的启动器、一个服务器、一个服务器的包装器和服务器的插件。

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

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

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

问题在于类装入器希望自动解析每个类并导入插件补丁类文件,而服务器尚未装入。

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

共有1个答案

马弘和
2023-03-14

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

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

你可以选择不解析这个类,将false传递给< code>loadClass。关于解析一个类需要什么的细节可以在这里找到。然后,您可以通过显式调用< code>resolveClass在单独的步骤中进行解析。

但是,如果这不能让你得到你想要的,我不知道这是否是唯一可能的解决方案,而且它肯定很糟糕(而且从一开始就肯定有更好的方法来解决这个问题),但是如果你做这样的事情呢:

我将调用用作修补程序的类,并且必须在服务器“预加载”类/代码之前加载。我将调用具有服务器依赖关系的补丁组件,但其加载必须延迟到服务器加载“后加载”类/代码之后。

对于每个插件:

>

  • 从补丁类中剥离所有后加载内容,并将其移动到其他类,例如插件实现或其他内容。然后,使该实现类的实例成为插件类的成员,将任何必要的成员函数委托给它,但不要立即实例化插件实现,并使成员字段的类型为Object(这通常被描述为不透明的指针/pimpl模式)。从本质上讲,您正在重构以使用pimppl习语,其中您的预加载内容是直接编码的,并且您的后加载内容实际上被委托给隐藏在对象后面的其他类,并且不会立即初始化。您的目标是从插件类本身中删除对服务器类的所有依赖关系,将其更改为加载补丁所需的最低限度,但将所有内容移动到最终隐藏在不透明指针后面的实现类中。

    现在像往常一样加载你所有的插件。它们现在应该加载了,因为所有与服务器相关的后加载内容都已经被删除了,所以服务器类不会被加载。

    现在让你的插件公开某种< code>serverLoaded(),或者< code>initializePlugin()方法或者类似的东西。在服务器类加载之后,遍历并调用每个加载的插件。

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

    因此,基本上,你将所有的后加载内容隐藏在一个不透明的指针后面,从而对类加载器保持隐藏,然后当需要时,你动态地实例化各种< code > PluginImplementation 类,从而使你的插件“完全完整”,但允许延迟加载依赖于服务器的部分。

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

    我相信可能有更好的选择,但这是我能想到的,考虑到你已经拥有的东西,它可能涉及最少的工作量。你必须移动大量的代码;像Eclipse之类的IDE或者至少可以使吐出委托变得容易(只是暂时使成员字段成为插件实现以帮助IDE,然后您可以在生成所有代码后将其更改回Object),但它应该最大限度地减少对现有架构的*基本*更改。

    这是另一个想法,尽管我不确定它是否适合您当前的代码,或者在您的情况下是否可能:

    基本上,这里的目标是通过以下方式使插件不再依赖于服务器本身:

    1. 将服务器声明为接口。
    2. 让您的具体服务器实现实现该接口。
    3. 更改所有插件以在该接口上运行,而不是在服务器类本身的实例上运行。
    4. 确保你的服务器类没有声明插件可以使用的任何内部类:将任何内部类分解到顶层(像Eclipse这样的IDE可以为你做到这一点)。

    现在您必须想办法告诉每个插件它正在使用的服务器实例,但插件会将其存储在具有基本接口类型的成员中。

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

    我认为这个想法比上面的简单得多,也不那么令人讨厌,我只是不知道它是否比上面的更可行。

    请注意,这两个选项并不一定能保证成功,但在实践中它们可能会起作用。

  •  类似资料:
    • 问题内容: 我有以下情况。 我有一个从另一个基类继承并重写一个方法的Java类。基本方法不会引发异常,因此没有声明。 现在我自己的方法应该能够引发异常,但是我要么有选择 吞下异常 添加抛出声明 两者都不令人满意,因为第一个会默默地忽略异常(好的,我可以执行一些日志记录),第二个会因为方法头不同而产生编译器错误。 问题答案: 如果确实需要,可以抛出未经检查的异常而不必声明它们。未检查的异常扩展。扩展

    • 问题内容: 我正在尝试使用预览功能制作一个活页内CSS编辑器,该功能将重新加载样式表并应用它,而无需重新加载页面。最好的方法是什么? 问题答案: 在“编辑”页面上,不要以常规方式(带有标签)包括CSS,而是将其全部写入标签。编辑的属性将自动更新页面,即使没有往返服务器的访问。 使用jQuery的Javascript: 就是这样! 如果您真的想花哨的话,请将功能附加到textarea的keydown

    • 问题内容: 是否可以 不 尝试加载就知道是否已加载Java类?尝试加载该类,但我不希望出现这种副作用。还有另一种方法吗? (我不想覆盖类加载器。我正在寻找一个相对简单的方法。) 问题答案: (感谢Aleksi)此代码: 产生: 请注意,示例类不在软件包中。完整的二进制名称是必需的。 二进制名称的一个示例是

    • 我想知道java中上述类装入器的职责是否可以合并?(不是由用户/程序员,而是由JVM开发人员) 引导加载器从jdk/jre/lib/*加载类,扩展加载器从jdk/jre/lib/ext/*加载,那么将它们结合在一起有什么问题呢? 这样做也可以保存一个级别的委托。不是吗? 我知道它们之间唯一的区别是bootstrap classloader是用本地语言实现的,而extension classload

    • 远程类加载是指加载未在执行位置显示的类。 例如,JavaApplet需要将类从服务器加载到本地并在本地执行。 一些包含URLClassLoader的程序需要从网络加载类,并在本地执行。 RMI怎么样?我找到了一个名为RMIClassLoader的类?是远程类加载吗?