在使用插件(例如Minecraft服务器,IDE等)的java应用程序中,很多时候需要使用一些库并将其隐藏在.jar中。
当另一个插件也使用该库并且还包含在其中的.jar但不同版本时,问题就出现了,两个插件都能正常工作直到它们相遇然后,在同一个类加载器下,类加载器将只加载一个版本的库,另一个插件将使用与其设计版本不同的版本运行,如果库是库,则导致NoSuchMethodExceptions和/或不同的运行时行为不向后兼容。
注意:当2个依赖项具有相同的暂时依赖性但在不同版本中时,此问题不关于如何使用maven进行编译。在这种情况下问题是不同的,因为你不能解决甚至在编译时检测依赖问题,因为不同版本的库不会在同一个.jar中,也不在同一个项目中,只在同一个类加载器下在给定的时间由2个插件可能由不同的作者用不同的目的。
避免此类冲突的一般策略是什么?
如果我正在创建自己的库而且它不能向后兼容,我该怎么做才能避免这个问题?
答案 0 :(得分:0)
这是一个典型的java问题,并且正在进行一些重大的Java技术重新设计以改变它,尽管这些都是暂时的(Java 9或Java 10)。
目前,您可以做的最好的事情是对项目进行明确和定期的依赖性分析。
首先,请运行:mvn dependency:tree
您将能够看到同一依赖项的多个版本。不幸的是,在使用外部库时,您可能需要遵循它们使用的相同依赖版本,或者查找外部依赖项的更新版本。
我在依赖性分析中花了很多时间,而且大部分时间都是手动完成的(使用java的bug之一......)
如果您制作自己的软件,我们显式API版本化,并在您进行重大更改时对版本进行重大更改。
例如,使用“版本1.1”,“版本1.2”等。让人们知道版本1.1.1
和1.1.4
向后兼容1.1
,但只要您引入API更改(发生这种情况),请转到版本1.2
并迫使人们更新。
答案 1 :(得分:0)
不幸的是,这是我的应用程序中遇到的问题,尤其是当您无法管理或维护任何第三方外部系统时。要最小化这种情况,您应该创建父poms并将外部依赖项放在那里。还要定期检查是否存在任何不匹配或冲突,以及是否可以在开发阶段删除。当您的应用程序需要特定的依赖项时,有时会达到死锁状态,但架构的其余部分构建在不同的架构上。使用自定义构建包和大量更改推出新更改,有时您无法提供帮助。在这种情况下,预防措施胜于治疗。
答案 2 :(得分:0)
@ vikingsteve的回答解释了如何设计API以避免问题。不幸的是,有时候实际的考虑会妨碍你。例如,当您的代码依赖于两个大型库时(反过来)依赖于第三个库的不同版本。这些版本是不兼容的。
如果您处于这种情况,并且无法通过重写库来解决问题,请求库开发人员/供应商修复它等等,还有几个选择:
您可以对软件进行重组,使具有冲突依赖关系的库位于不同的应用程序中;例如将它们分成“客户端”和“服务器”,或通过消息总线进行通信的“对等”,或通过管道进行通信的父和子进程。
您可以重新构建代码库,以便将两个库加载到主类加载器的两个单独的子类加载器中。这将允许两个子类加载器加载两个版本的公共依赖项....并在JVM中共存。
显然,这些方法都不是一个神奇的子弹。两者都需要对您的应用程序进行一些修改。但他们可以解决你的“依赖地狱”问题。
答案 3 :(得分:0)
我的解决方案是使用所需的外部依赖关系创建另一个独立运行的jar应用程序,并在那里执行任务。