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