如果它们来自我父母的其他子模块,我应该依赖 Maven 中的传递依赖吗?

Posted

技术标签:

【中文标题】如果它们来自我父母的其他子模块,我应该依赖 Maven 中的传递依赖吗?【英文标题】:Should I rely on transitive dependencies in Maven if they come from other sub-module of my parent? 【发布时间】:2018-04-25 22:23:23 【问题描述】:

假设我们正在处理 mortgage 子模块,并且我们在模块代码中直接使用 Google Guava 类,但 guava 的依赖关系在其他子模块中定义同一个父级,我们只能通过对“投资”模块的传递依赖来访问 Guava 类:

banking-system (parent pom.xml)
|
|-- investment (pom.xml defines <dependency>guava</dependency>)
|
|-- mortgage (pom.xml defiens <dependency>investment</dependency>)

我们是否还应该在 mortgage pom.xml 中为 Guava 添加一个&lt;dependency&gt;

缺点在我们的 pom.xml 中看起来像重复,优点是:如果有人开发“投资”会丢弃番石榴,那么它不会阻止我们的抵押子模块成功构建。

如果是,那么我们指定的&lt;version&gt; 应该是什么? (在父 pom 中没有 + &lt;dependencyManagement&gt;?)

如果是,那么我们是否应该在某个模块中使用&lt;provided&gt; 范围?

注意:请记住,我是在特定情况下询问模块何时具有共同的父 pom(例如,作为一个整体的应用程序)。

也许这个结构不是最好的例子,想象一下:

banking-app
    banking-core (dep.on: guava, commons, spring)
    investment (dep.on: banking-core)
    mortgage (dep.on: banking-core)

还应该Investment在使用@Component时显式声明Spring,如果使用Guava的LoadedCache则声明Guava?

【问题讨论】:

我会复制并让 parent 中的 dependencyManagement 负责版本。 Maven : Should I keep or remove declared dependencies that are also transitives dependencies?的可能重复 【参考方案1】:

我们在模块代码中直接使用 Google Guava 类,但是 番石榴的依赖关系在相同的其他子模块中定义 父级,我们只能通过传递访问 Guava 类 对“投资”模块的依赖 [...] 我们还应该在抵押 pom.xml 中放入 Guava 吗?

是的,你应该在你的模块中声明 Google Guava 依赖,而不是期望它可以作为传递依赖。即使它适用于当前版本,但在以后的直接依赖版本中可能不再如此。

如果你的代码依赖于一个模块,你的代码应该只直接依赖于这个模块的类,而不是这个模块的传递依赖。正如您所提到的,不能保证 investment 模块将来会继续依赖 Guava。您需要在父级的 pom.xml 或模块本身中指定此依赖项,以确保它在不依赖传递依赖项的情况下可用。这不是重复,你怎么能告诉 Maven 你的模块依赖于 Guava?

我看不到在任何情况下都需要遵守最低限度的最佳做法,否则您需要这样做。

如果是,那么我们指定的&lt;version&gt; 应该是什么? (在父 pom 中没有 + &lt;dependencyManagement&gt;?)

是的,在父模块中使用&lt;dependencyManagement&gt; 并在子模块中使用&lt;dependency&gt; 是最好的:您将确保所有模块都使用相同版本的依赖项。由于您的模块作为一个整体是一个应用程序,因此它可能会更好,因为它可以避免各种问题,例如在类路径上存在相同依赖项的不同版本而造成破坏。

即使由于某种原因,您使用相同父模块的模块需要不同版本的依赖项,仍然可以使用 &lt;version&gt; 覆盖此特定模块的版本。

如果是,那么我们应该在某个模块中使用作用域吗?

可能不会,具有compile 范围的依赖项是大多数打包方法的最佳选择。

但是您可能会遇到需要或更喜欢这样做的情况,例如,如果所述模块需要使用特定于运行时环境的版本,或者您的部署或打包模型的设计方式这需要它。鉴于您所暴露的情况,两者都是可能的,尽管大多数时候它不应该是必要的。

【讨论】:

【参考方案2】:

    ,声明部门。不是复制!!!编译依赖是可传递的,这不是 maven 开发人员想要的,它是由 java 语言强制的。因为像类继承这样的特性会强制这种行为。您已经提到的“专业人士”是重要的事实。 见the (*) note in the transitive-scope-table

    是的,始终在您的 reactor 父级中使用 dependencyManagement 声明所需的第三方 lib-versions。在运行时从不同的 lib 版本中发现错误是一件痛苦的事。避免在大型反应器的子模块中声明第三方库的版本,始终在父模块中使用 depMngs。

    ,我只会对运行时提供的依赖项使用“提供”,在您的示例中是 tomcat/jboss/wildfly/.. 用于诸如 servlet-api/cdi-api/ 之类的东西。但不适用于第三方库。 尽可能晚地声明“提供”范围(即您的部署模块战争/耳朵),而不是在您的业务模块中。这使得编写测试更容易。 例如:

    投资(取决于番石榴范围:=提供) 抵押(取决于投资,但自己不需要番石榴)

--> 抵押类路径不包含番石榴。 如果您为抵押贷款编写单元测试,其中涉及投资的类将不起作用 -> 您需要至少声明具有 scope=test/runtime 的 guava 才能运行这些测试...

【讨论】:

【参考方案3】:

当一个模块使用第 3 方库时,该模块也应该在其 pom.xml 中显式地依赖该库。想象一下,如果另一个项目应该使用“抵押”模块,并且已经不依赖 Guava,它将失败,例如当单元测试遇到涉及 Guava 的代码路径时。显式依赖还涵盖了您重构“投资”模块以使其不再使用 Guava 的场景。您的“投资”模块应该对其依赖项中的此类更改不可知。

明确列出您的直接依赖项总是正确的。关于版本,最好将其保留在父 pom 的 dependencyManagement 部分中,以便所有子项目都继承该(相同)版本。

【讨论】:

但是请记住mortgage 模块明确需要investment 模块,所以当有人依赖mortgage "ony" 时,它无论如何都会用番石榴拉investment 显式依赖仍然是模块中使用的所有代码的方法。我用另一个例子更新了我的答案。

以上是关于如果它们来自我父母的其他子模块,我应该依赖 Maven 中的传递依赖吗?的主要内容,如果未能解决你的问题,请参考以下文章

深入了解保留周期

SpringBoot子模块相互依赖打包

如何在 git repo 中链接依赖项?

敏捷软件开发 – DIP 依赖倒置原则

Git子模块与依赖管理?

当父母在python中崩溃时杀死子进程