如果它们来自我父母的其他子模块,我应该依赖 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 添加一个<dependency>
?
缺点在我们的 pom.xml 中看起来像重复,优点是:如果有人开发“投资”会丢弃番石榴,那么它不会阻止我们的抵押子模块成功构建。
如果是,那么我们指定的<version>
应该是什么? (在父 pom 中没有 + <dependencyManagement>
?)
如果是,那么我们是否应该在某个模块中使用<provided>
范围?
注意:请记住,我是在特定情况下询问模块何时具有共同的父 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?
我看不到在任何情况下都需要遵守最低限度的最佳做法,否则您需要这样做。
如果是,那么我们指定的
<version>
应该是什么? (在父 pom 中没有 +<dependencyManagement>
?)
是的,在父模块中使用<dependencyManagement>
并在子模块中使用<dependency>
是最好的:您将确保所有模块都使用相同版本的依赖项。由于您的模块作为一个整体是一个应用程序,因此它可能会更好,因为它可以避免各种问题,例如在类路径上存在相同依赖项的不同版本而造成破坏。
即使由于某种原因,您使用相同父模块的模块需要不同版本的依赖项,仍然可以使用 <version>
覆盖此特定模块的版本。
如果是,那么我们应该在某个模块中使用作用域吗?
可能不会,具有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 中的传递依赖吗?的主要内容,如果未能解决你的问题,请参考以下文章