【发布时间】:2020-01-02 02:35:00
【问题描述】:
所以首先我知道你可以在不同的构建系统上使用各种影子插件重新定位你编译的 jar 的所有引用。我知道它是如何工作的,并且已经在使用它。但是我遇到了一个问题,我不能在编译时这样做。
我将简化我的情况,以便更容易理解(但我会在底部解释全貌,以防你好奇)。
我正在为两个不同(但相似)的系统(一个 jar forall)编写一个插件。这些平台负责启动底层软件和加载/启动所有插件(所以我无法控制应用程序,包括启动参数)。
平台A 为我提供了一个库(我们称之为com.example.lib)。平台B 也是如此。 但是它决定将其重新定位到org.b.shadow.com.example.lib。
现在在我的插件的核心代码(两个平台上使用的代码)中,我使用了这个库。现在虽然我可以检测到我在哪个平台上,但我目前不知道如何在运行时重写我的代码中对库的所有引用,以便它在平台B 上工作。
根据我的发现,我似乎需要使用自定义 ClassLoader 来实现这一点。这里的问题是我不知道我可以让运行时使用我的自定义ClassLoader。或者真正从哪里开始。
一件重要的事情是,这些重定位可能只会影响我的包中的类中的引用(例如me.brainstone.project)。
我使用的另一个依赖项(并且有阴影)使用 ASM 和 ASM Commons,所以如果可以用它们来做,那就太棒了!
总结一下。我想在运行时仅在我的类中选择性地重新定位引用(对其他类)。
编辑:
在我的整个(原始)帖子中,我只讨论过一个库,但我想指出,我将为多个库执行此操作。并且在那里做需要我付出大量努力的事情(为每个库(类或部分)编写包装器将被视为一项重大努力)允许我使用库并不是我想要的。相反,我想要一个解决方案,该解决方案需要最少的插件才能将新库添加到组合中。
现在这里对我的设置进行更详细的说明。
拳头我想先说一下我知道我可以为不同的平台创建两个不同的罐子。我已经在这样做了。但是由于令人惊讶的是,许多人似乎无法弄清楚这一点,而且我已经厌倦了一遍又一遍地解释它(那些人不会阅读文档来挽救他们的生命)我只想提供一个两个都用一个罐子,即使这意味着我需要花费大量时间来让它工作(我更喜欢这个而不是不断地解释它)。
现在我的实际设置如下所示:在平台A 上提供了库,但在平台B 上却没有。我知道其他插件经常通过遮蔽它来使用该库(许多没有重新定位导致各种问题)。因此,为了防止任何冲突,我下载了库,使用 jar-relocator 重新定位该 jar 中的类,然后使用反射将其注入到类路径中。在这种情况下,如果重新定位,我目前无法使用该库。这就是为什么我想在运行时更改代码中的引用。它也解释了为什么我不想更改其他类的引用,因为我不想意外破坏那些其他插件。我还认为,如果我能以某种方式使用我自己的ClassLoader,我就不需要将 jars 注入主要的ClassLoader,因为这样我就可以告诉ClassLoader 使用额外的 jars 而不必诉诸反射.
但正如我所说,据我了解,问题与简化版本相同。
【问题讨论】:
-
别这样。创建接口和 2 个单独的实现,并在运行时选择匹配的一个。听起来像是另一个过于复杂的 Minecraft 插件,无缘无故。
-
@GotoFinal 大量重复的代码,并从外部库包装我需要使用的所有内容,这正是我想要的。更不用说建造它了......
-
如果你会做类似的事情,你将失去调试此类代码的能力,更糟糕的是......是的,复制一些代码比创建类似的东西更好。并且通过良好的抽象,您将不需要复制任何东西或只是非常小的东西。构建将简单明了。但是我以后可以在这里写一个答案……我什么时候下班回来
-
由于项目的性质,我已经仅限于非常基本的调试。我已经学会了如何使用它,所以我真的不介意不能做我以前做不到的事情。另一件事是我有多个库可以使用。猜测大约 10 到 15。在那些我使用的不仅仅是一个类/接口。因此,对于我将使用的每一个界面都必须这样做,这听起来像是一件可怕的苦差事。老实说,我宁愿有一个解决方案,它一次需要大量工作,但它的扩展性非常好。
-
最后我正在使用 gradle。因此,在展示如何使用 maven 构建它的同时(如果你要这样做的话)很好。这个地方适合所有人。但这对我没有帮助。
标签: java reflection java-8 runtimemodification