默认情况下无法实现,且jigsaw不能帮忙。但是,还有希望。
在JVM中,默认类加载器设置使用Java类路径。类路径是用来搜索(按顺序)类的位置的列表。因此,如果在类路径上有相同的依赖关系的两个版本,则会加载类路径中先出现的版本。
Java 9中引入的模块系统引入了新的“模块”概念和新的“模块路径”,后者指定要加载的基本模块集合。模块提供了新的封装级别,防止使用实现中非公共部分的外部使用,这可能很有用。然而,模块系统没有版本或版本选择的概念,所以包含相同模块的多个版本的问题仍然存在。模块加载器会检测并抱怨这个问题,而不是您从类路径加载中获得的沉默选择。但它并没有“修复”它。
当你使用 Maven(或基于 Maven 的解决方案,如 Leiningen 或 Boot)时,你是在请求 Maven 检查传递依赖的树,并选择一个版本来使用。在 Maven 中,最终的选择基于宽度优先遍历传递树的第一个遇到版本。tools.deps 使用其自己的选择算法,优先选择依赖项的最新版本(同时仍然构建逻辑上合理的依赖树)。如果你遵循 Rich 在 Spec-ulation 主旨演讲 中的建议,只在你的版本中进行增量更改,而不是破坏性更改,那么这将大大减少意外版本选择导致的破坏性冲突。
如果你确实想同时加载库的多个版本,Java 类加载器架构允许你这样做,但这需要一个自定义类加载器和一些架构上的规范。在这种情况下,你创建一个接口(或协议),它是由你的基本类加载器加载的,然后使用后委托类加载器,只在该类加载器中加载类的实现。后委托允许每个独立的“插件”加载任何它需要的,但可以通过基本层接口与应用程序通信。正如我所说,这可以工作,但需要大量的设置和规范,并且有一些工作方式的限制,所以通常只在你预期产品将具有高度依赖冲突而有强烈“插件”类型架构贡献时才值得这样做。OSGi 是解决此问题的另一种方法。在 Clojure 中实现这些都不特别有趣。