默认情况下无法实现,而且 jigsaw 也无能为力。但是,仍有一线希望。
JVM 中的默认类加载器设置使用 Java 类路径。类路径是为查找类的位置(按顺序)的地点列表。因此,如果类路径上存在同一依赖项的两个版本,则会加载类路径上第一个出现的版本。
Java 9 中引入的模块系统引入了“模块”这一新概念,以及指定要加载的模块基本集合的新“模块路径”。模块提供了新的封装级别,防止外部使用实现的非公开部分,这可能很有用。然而,模块系统没有版本或版本选择的概念,因此包含同一模块多个版本的问题仍然存在。模块加载器将检测并抱怨这个问题,而不是像类路径加载那样得到一个无声的选择。但它并没有做什么“修正”。
当你使用Maven(或基于Maven的解决系统如Leiningen或Boot)时,你就要求Maven检查传递依赖的树,并选择一个版本来使用。在Maven中,选择基本上是基于对传递树的广度优先遍历中首先遇到的版本。tools.deps使用自己的选择算法,优先选择依赖项的最新版本(同时仍然构建逻辑上合理的依赖项树)。如果你遵循Rich在《Spec-ulation首脑演讲》(https://github.com/matthiasn/talk-transcripts/blob/master/Hickey_Rich/Spec_ulation.md)中的建议,只对你版本中进行增加性修改而不是破坏性修改,那么这将大大减少由意外版本选择引起的破坏性冲突。
如果您确实想在同一时间加载库的多个版本,Java类加载器架构允许您这样做,但这需要自定义类加载器和一些架构纪律。在这种情况下,您创建一个接口(或协议),该接口是从您的基石类加载器加载的,然后使用后委托类加载器,并只在那个类加载器中加载类的工作实现。后委托允许每个独立的“插件”加载它所需要的任何内容,然后再通过基石级别的接口与应用程序通信。正如我所说的,这是可行的,但是需要大量的设置和纪律,并且对工作方式有一些限制,因此只有在您构建的一个产品中,强烈期望有“插件”类型架构和高度依赖冲突可能性时才值得一试。OSGi也是解决这个问题的一种方法。在Clojure中实现这两种方法都不太有趣。