Maven中的依赖管理和依赖之间的区别

dependencyManagementdependencies有什么区别?我在Apache Maven网站上看到了文档。似乎在dependencyManagement下定义的依赖项可以在其子模块中使用,而无需指定版本。

例如:

父项目(Pro-par)在dependencyManagement下定义了一个依赖项:

<dependencyManagement><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>3.8</version></dependency></dependencies></dependencyManagement>

然后在Pro-par的孩子中,我可以使用jUnit:

<dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId></dependency></dependencies>

但是,我想知道是否有必要在父pom中定义jUnit?为什么不直接在需要的模块中定义它?

386076 次浏览

就像你说的;dependencyManagement用于将所有依赖项信息提取到一个公共POM文件中,简化子POM文件中的引用。

当您有多个属性不想在多个子项目下重新键入时,它会变得很有用。

最后,dependencyManagement可用于定义跨多个项目使用的工件的标准版本。

依赖管理允许合并和集中管理依赖版本,而无需添加由所有子级继承的依赖项。当您有一组项目(即多个)继承公共父级时,这尤其有用。

dependencyManagement的另一个非常重要的用例是对传递依赖项中使用的工件版本的控制。如果没有示例,这很难解释。幸运的是,这在留档中进行了说明。

在Eclipse中,dependencyManagement中还有一个特性。当使用dependencies而不使用它时,会在pom文件中注意到未找到的依赖项。如果使用dependencyManagement,则未解决的依赖项在pom文件中未被注意到,错误仅出现在java文件中。(导入等…)

如果依赖项是在顶层pom的依赖项管理元素中定义的,则子项目不必显式列出依赖项的版本。如果子项目确实定义了版本,它将覆盖顶层列出的版本POM的依赖管理部分。也就是说,依赖管理版本仅当孩子不直接声明版本时使用。

Maven站点上的留档很糟糕。依赖管理所做的只是将你的依赖定义(版本、排除项等)向上移动到父pom,然后在子pom中你只需要放置group pId和artifactId。就是这样(除了父pom链接之类的,但这也不是很复杂——依赖管理在父级赢得了依赖——但如果对此有疑问或导入,Maven留档会好一点)。

在阅读了Maven网站上所有的'a'、'b'、'c'垃圾并且感到困惑之后,我重写了他们的例子。因此,如果你有2个项目(项目1和项目2)共享一个共同的依赖项(betaShared),你可以将该依赖项向上移动到父pom。当你在做的时候,你也可以向上移动任何其他依赖项(alpha和charlie),但前提是这对你的项目有意义。因此,对于前面句子中概述的情况,这是在父pom中使用依赖管理的解决方案:

<!-- ParentProj pom --><project><dependencyManagement><dependencies><dependency> <!-- not much benefit defining alpha here, as we only use in 1 child, so optional --><groupId>alpha</groupId><artifactId>alpha</artifactId><version>1.0</version><exclusions><exclusion><groupId>zebra</groupId><artifactId>zebra</artifactId></exclusion></exclusions></dependency><dependency><groupId>charlie</groupId> <!-- not much benefit defining charlie here, so optional --><artifactId>charlie</artifactId><version>1.0</version><type>war</type><scope>runtime</scope></dependency><dependency> <!-- defining betaShared here makes a lot of sense --><groupId>betaShared</groupId><artifactId>betaShared</artifactId><version>1.0</version><type>bar</type><scope>runtime</scope></dependency></dependencies></dependencyManagement></project>
<!-- Child Proj1 pom --><project><dependencies><dependency><groupId>alpha</groupId><artifactId>alpha</artifactId>  <!-- jar type IS DEFAULT, so no need to specify in child projects --></dependency><dependency><groupId>betaShared</groupId><artifactId>betaShared</artifactId><type>bar</type> <!-- This is not a jar dependency, so we must specify type. --></dependency></dependencies></project>
<!-- Child Proj2 --><project><dependencies><dependency><groupId>charlie</groupId><artifactId>charlie</artifactId><type>war</type> <!-- This is not a jar dependency, so we must specify type. --></dependency><dependency><groupId>betaShared</groupId><artifactId>betaShared</artifactId><type>bar</type> <!-- This is not a jar dependency, so we must specify type. --></dependency></dependencies></project>

我对这个问题迟到了,但我认为它值得比公认的答案更清晰的回答(这是正确的,但没有强调实际的重要部分,你需要自己推断)。

在父POM中,<dependencies><dependencyManagement>之间的主要区别是:

  • <dependencies>部分中指定的工件将始终作为子模块的依赖项包含在内。

  • <dependencyManagement>部分中指定的工件,只有在它们也在子模块的<dependencies>部分中指定的情况下,才会包含在子模块中。你问这个问题有什么好处?因为你在父模块中指定了版本和/或范围,而在子POM中指定依赖项时可以将它们省略。这可以帮助你为子模块的依赖项使用统一版本,而无需在每个子模块中指定版本。

有一些答案概述了使用maven的<depedencies><dependencyManagement>标签之间的差异。

然而,以下以简明的方式阐述了几点:

  1. <dependencyManagement>允许合并跨不同模块使用的所有依赖项(在子pom级别使用)——清晰度中心依赖版本管理
  2. <dependencyManagement>允许根据需要轻松升级/降级依赖关系,在其他情况下,这需要在每个子pom级别进行练习-一致性
  3. <dependencies>标签中提供的依赖项总是被导入,而父pom中<dependencyManagement>提供的依赖项只有在子pom的<dependencies>标签中有相应的条目时才会被导入。

在我看来,还有一件事没有得到足够的强调,那就是不想要的继承

下面是一个增量示例:

我在我的parent pom中声明:

<dependencies><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>19.0</version></dependency></dependencies>

砰!我在我的Child AChild BChild C模块中有它:

  • 隐含性由儿童Pom继承
  • 一个地方来管理
  • 不需要在儿童Pom中重新声明任何东西
  • 我仍然可以redelcare和覆盖Child B中的version 18.0,如果我愿意的话。

但是,如果我最终不需要番石榴在Child C,在未来的Child DChild E模块?

他们仍然会继承它,这是不受欢迎的!这就像Java上帝对象代码的味道,你从一个类中继承了一些有用的位,以及一吨不需要的东西。

这就是<dependencyManagement>发挥作用的地方。当你将它添加到你的父pom时,你所有的子模块别再看了。因此,你是强迫进入每个确实需要它的单独模块并再次声明它(Child AChild B,尽管没有版本)。

而且,显然,你不会为Child C这样做,因此你的模块仍然是精简的。

对不起,我参加晚会迟到了。

让我试着用mvn dependency:tree命令解释一下区别

考虑下面的例子

家长POM-我的项目

<modules><module>app</module><module>data</module></modules>
<dependencies><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>19.0</version></dependency></dependencies>
<dependencyManagement><dependencies><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.9</version></dependency></dependencies></dependencyManagement>

儿童POM-数据模块

<dependencies><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId></dependency></dependencies>

子POM-app模块(没有额外的依赖项,因此将依赖项留空)

 <dependencies></dependencies>

在运行mvn dependency:tree命令时,我们得到以下结果

Scanning for projects...------------------------------------------------------------------------Reactor Build Order:
MyProjectappdata
------------------------------------------------------------------------Building MyProject 1.0-SNAPSHOT------------------------------------------------------------------------
--- maven-dependency-plugin:2.8:tree (default-cli) @ MyProject ---com.iamvickyav:MyProject:pom:1.0-SNAPSHOT\- com.google.guava:guava:jar:19.0:compile
------------------------------------------------------------------------Building app 1.0-SNAPSHOT------------------------------------------------------------------------
--- maven-dependency-plugin:2.8:tree (default-cli) @ app ---com.iamvickyav:app:jar:1.0-SNAPSHOT\- com.google.guava:guava:jar:19.0:compile
------------------------------------------------------------------------Building data 1.0-SNAPSHOT------------------------------------------------------------------------
--- maven-dependency-plugin:2.8:tree (default-cli) @ data ---com.iamvickyav:data:jar:1.0-SNAPSHOT+- org.apache.commons:commons-lang3:jar:3.9:compile\- com.google.guava:guava:jar:19.0:compile

google guava在每个模块(包括父模块)中被列为依赖项,而apache公共资源仅在数据模块中被列为依赖项(甚至在父模块中也不是)

两者之间的区别最好体现在Maven网站文档中可用的依赖管理元素的必要和充分的定义中:

依赖管理

"从这个项目继承的项目的默认依赖信息。本节中的依赖项不会立即解析。相反,当从此派生的POM声明由匹配的group pId和artifactId描述的依赖项时,如果尚未指定本节中的版本和其他值,则将用于该依赖项。"[https://maven.apache.org/ref/3.6.1/maven-model/maven.html]

它应该与其他页面上的更多信息一起阅读:

"…用于将依赖项引用与依赖项管理部分匹配的最小信息集实际上是{group pId, artifactId, type,分类器}。在许多情况下,这些依赖项将引用没有分类器的jar工件。这允许我们将标识集简写为{group pId, artifactId},因为类型字段的默认值是jar,而默认分类器为null。"[https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html]

因此,一个依赖元素的所有子元素(范围、排除项等,)——除了group pId、artifactId、type、分类器,而不仅仅是版本——在你在依赖元素中指定依赖时(因此从那里继承)都可以锁定/默认。如果你将类型和分类器子元素的依赖项(请参阅第一个引用的网页以检查所有子元素)分别指定为not jar和not null,你需要{group pId、artifactId、分类器、type}来引用(解析)源自依赖管理元素的继承中的任何一点的依赖项。另外,如果您不打算覆盖分类器和类型(分别为jar和null)的默认值,{group pId, artifactId}就足够了。因此,default在该定义中是一个很好的关键字;任何子元素(除了group pId、artifactId、分类器和类型,当然)在您引用依赖项时显式分配值覆盖依赖项管理元素中的默认值。

因此,依赖管理之外的任何依赖元素,无论是作为对某些依赖管理元素的引用还是作为独立元素,都会立即解析(即安装到本地存储库并可用于类路径)。

用我自己的话来说,你的parent-project可以帮助你提供两种依赖关系:

  • 隐式依赖:您的parent-project<dependencies>部分中定义的所有依赖项都被所有child-projects继承
  • 显式依赖:允许您选择要在child-projects中应用的依赖项。因此,您使用<dependencyManagement>部分来声明您将在不同的child-projects中使用的所有依赖项。最重要的是,在本节中,您定义了<version>,这样您就不必在child-project中再次声明它。

在我看来,<dependencyManagement>(如果我错了请纠正我)只是帮助您集中依赖项的版本。它就像一种辅助功能。作为最佳实践,您的<dependencyManagement>必须位于其他项目将继承的父项目中。一个典型的例子是通过声明Spring父项目来创建Spring项目的方式。

无论如何,如果你有一个父pom,那么在我看来,使用<dependencyManagement>只是为了控制版本(可能还有范围)是浪费空间,并且会让初级开发人员感到困惑。

无论如何,您可能会在某种父pom文件中拥有版本的属性。为什么不在子pom中使用此属性呢?这样您仍然可以一次更新所有子项目的属性(在父pom中)中的版本。这与<dependencyManagement>的效果相同,只是没有<dependencyManagement>

在我看来,<dependencyManagement>应该用于对依赖项的“真正”管理,例如排除等。

<dependencyManagement>的一个用例是解决库版本冲突。

示例:

  • 项目A有库x: 1.0.1
  • 项目A有B库
  • B库有库x: 1.0.0

有了这个集合,你会得到项目Ax:1.0.1x:1.0.0的冲突。要解决这个问题,您可以将具有特定版本的依赖项放入<dependencyManagement>标签

我不推荐使用dependencyManagement

使用它的唯一好处是你可以在父pom中定义版本,不需要在子pom中再次定义。但是如果你有一组项目(尤其是微服务项目)。使用dependencyManagement没有任何好处。

不同的项目可能需要不同的依赖。为什么要从同一个父pom继承它。尽量保持简单。如果一个项目需要依赖,那么将其添加到pom文件中。不要混淆开发人员。

它被这里解释为易于理解。依赖管理和依赖之间的结论区别是声明和实际添加