dependencyManagement
和 dependencies
有什么区别?我在 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?为什么不在需要的模块中直接定义呢?
我对这个问题的回答很晚,但我认为值得一个比接受的更清晰的回答(这是正确的,但不强调实际重要的部分,你需要自己推断)。
在父 POM 中,<dependencies>
和 <dependencyManagement>
之间的主要区别是:
Dependency Management 允许合并和集中管理依赖项版本,而无需添加所有子项继承的依赖项。当您有一组项目(即多个项目)继承了一个共同的父项时,这尤其有用。
dependencyManagement
的另一个极其重要的用例是控制传递依赖项中使用的工件版本。如果没有例子,这很难解释。幸运的是,文档中对此进行了说明。
<dependencyManagement>
中。在 <dependencyManagement>
中包含依赖项可集中管理每个依赖项的版本、范围和排除项(如果您决定使用它)以及何时使用它。 Maven 的guide to dependency management 涉及所有细节。
dependencyManagement
还控制传递依赖项)仅在显式设置依赖项时才为真:stackoverflow.com/questions/28312975/…
dependencies
部分。我们这样做是为了让所有子项目默认都有一些 apache-commons,而不是一直声明它们。
Maven 网站上的 documentation 太可怕了。 dependencyManagement 所做的只是将您的依赖项定义(版本、排除项等)向上移动到父 pom,然后在子 pom 中您只需放置 groupId 和 artifactId。就是这样(除了父 pom 链接等,但这也不是很复杂——dependencyManagement 胜过父级别的依赖——但如果对此或导入有疑问,Maven 文档会更好一些)。
在阅读了 Maven 站点上所有的“a”、“b”、“c”垃圾并感到困惑之后,我重新编写了他们的示例。因此,如果您有 2 个项目(proj1 和 proj2)共享一个公共依赖项(betaShared),您可以将该依赖项移至父 pom。当您使用它时,您还可以向上移动任何其他依赖项(alpha 和 charlie),但前提是它对您的项目有意义。因此,对于前面句子中概述的情况,这是在父 pom 中使用 dependencyManagement 的解决方案:
<!-- 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>
在我看来,还有一件事没有得到足够的强调,那就是不需要的继承。
这是一个增量示例:
我在我的 parent
pom 中声明:
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>19.0</version>
</dependency>
</dependencies>
繁荣!我的 Child A
、Child B
和 Child C
模块中有它:
子 pom 继承的隐含性
一个管理的地方
无需在 child poms 中重新声明任何内容
如果我愿意,我仍然可以在 Child B 中重新维护和覆盖到 18.0 版本。
但是,如果我最终在 Child C
中不需要番石榴,在未来的 Child D
和 Child E
模块中也不需要呢?
他们仍然会继承它,这是不希望的!这就像 Java God 对象代码的味道,你从一个类中继承了一些有用的位,以及大量不需要的东西。
这就是 <dependencyManagement>
发挥作用的地方。当您将此添加到您的父 pom 中时,您的所有子模块停止看到它。因此,您被迫进入每个确实需要它的单独模块并再次声明它(Child A
和 Child B
,但没有版本)。
而且,显然,您不会为 Child C
这样做,因此您的模块仍然很精简。
<dependencyManagement>
那么默认情况下依赖项不会在子 pom 中继承吗?因为在文档中:maven.apache.org/guides/introduction/… 在解释 <dependencyManagement>
的第二次使用时,它看起来默认会被继承。在一行中,他们说:“当在项目 B 上运行 maven 时,将使用工件 a、b、c 和 d 的 1.0 版,而不管它们的 pom 中指定的版本是什么”,即使“b”没有用于项目B
就像你说的; dependencyManagement
用于将所有依赖信息拉入一个公共 POM 文件中,简化子 POM 文件中的引用。
当您不想在多个子项目下重新键入多个属性时,它会变得很有用。
最后,dependencyManagement
可用于定义工件的标准版本以跨多个项目使用。
对不起,我迟到了。
让我尝试使用 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 - 应用程序模块(没有额外的依赖项,因此将依赖项留空)
<dependencies>
</dependencies>
在运行 mvn dependency:tree
命令时,我们得到以下结果
Scanning for projects...
------------------------------------------------------------------------
Reactor Build Order:
MyProject
app
data
------------------------------------------------------------------------
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 commons 仅在数据模块中被列为依赖项(甚至在父模块中也没有)
有一些答案概述了 <depedencies>
和 <dependencyManagement>
标记与 maven 之间的区别。
但是,下面以简洁的方式阐述了几点:
如果依赖项是在顶级 pom 的 dependencyManagement 元素中定义的,则子项目不必显式列出依赖项的版本。如果子项目确实定义了一个版本,它将覆盖顶级 POM 的 dependencyManagement 部分中列出的版本。也就是说,dependencyManagement 版本只在子不直接声明版本时使用。
用我自己的话来说,您的 parent-project
可以帮助您提供 2 种依赖项:
隐式依赖项:父项目的
显式依赖项:允许您选择要在子项目中应用的依赖项。因此,您使用
在我看来,<dependencyManagement>
(如果我错了,请纠正我)只是通过帮助您集中依赖项的版本而有用。它就像一种辅助功能。作为最佳实践,您的 <dependencyManagement>
必须位于其他项目将继承的父项目中。一个典型的例子是通过声明 Spring 父项目来创建 Spring 项目的方式。
两者之间的区别最好体现在 Maven 网站文档中可用的 dependencyManagement 元素的必要和充分定义中:
依赖管理
“从这个继承的项目的默认依赖信息。本节中的依赖不会立即解决。相反,当从这个派生的 POM 声明由匹配的 groupId 和 artifactId 描述的依赖时,本节中的版本和其他值如果尚未指定,则用于该依赖项。” [https://maven.apache.org/ref/3.6.1/maven-model/maven.html]
它应该与其他页面上提供的更多信息一起阅读:
“.. 将依赖项引用与依赖项管理部分匹配的最小信息集实际上是 {groupId, artifactId, type, classifier}。在许多情况下,这些依赖项将引用没有分类器的 jar 工件。这允许我们将标识集简写为 {groupId, artifactId},因为类型字段的默认值是 jar,而默认分类器是 null。” [https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html]
因此,依赖元素的所有子元素(范围、排除项等)——除了 groupId、artifactId、type、classifier,而不仅仅是 version——在此时可用于锁定/默认(并因此继承自从那里开始)您在dependencyElement 中指定依赖项。如果您将 type 和 classifier 子元素的依赖项(请参阅第一个引用的网页以检查所有子元素)分别指定为 not jar 和 not null,则需要 {groupId, artifactId, classifier, type}在源自dependencyManagement 元素的继承中的任何点引用(解决)该依赖项。否则,如果您不打算覆盖分类器和类型的默认值(分别为 jar 和 null),则 {groupId, artifactId} 就足够了。所以 default 在该定义中是一个很好的关键字;任何子元素(当然除了 groupId、artifactId、classifier 和 type 之外)在您引用依赖项时显式分配的值会覆盖 dependencyManagement 元素中的默认值。
因此,dependencyManagement 之外的任何依赖元素,无论是作为对某个dependencyManagement 元素的引用还是作为独立元素,都会立即被解析(即安装到本地存储库并可用于类路径)。
在 Eclipse 中,dependencyManagement
中还有一项功能。如果在没有它的情况下使用 dependencies
,则会在 pom 文件中注意到未找到的依赖项。如果使用 dependencyManagement
,未解决的依赖关系在 pom 文件中不会被注意到,错误只会出现在 java 文件中。 (进口等...)
我不建议使用 dependencyManagement
。
使用它的唯一好处是您可以在父 pom 中定义版本,而无需在子 pom 中再次定义它。但是如果你有一组项目(尤其是微服务项目)。使用 dependencyManagement
没有任何好处。
不同的项目可能需要不同的依赖。为什么要从同一个父 pom 继承它。尽可能简单。如果一个项目需要一个依赖,那么将其添加到 pom 文件中。不要混淆开发人员。
如果你有一个 parent-pom,那么在我看来,使用 <dependencyManagement>
只是为了控制版本(可能还有范围)是浪费空间并让初级开发人员感到困惑。
无论如何,您可能会在某种父 pom 文件中拥有版本的属性。为什么不在子 pom 中使用这个属性呢?这样,您仍然可以一次为所有子项目更新属性中的版本(在父 pom 中)。这与没有 <dependencyManagement>
的 <dependencyManagement>
具有相同的效果。
在我看来,<dependencyManagement>
应该用于“真正的”依赖项管理,例如排除项等。
<dependencyManagement>
的一个用例是用于解决库版本冲突。
例子:
项目 A 有库 x:1.0.1
项目 A 有 B 库
库有库 x:1.0.0
有了这个集合,您将在项目 A 同时 x:1.0.1
和 x:1.0.0
时发生冲突。要解决此问题,您可以将特定版本的依赖项放入 <dependencyManagement>
标记
here 解释为易于理解。依赖管理和依赖管理之间的结论区别依赖项是声明和实际添加
Artifacts specified in the <dependencies> section will ALWAYS be included as a dependency of the child module(s)
中添加它们也包含在父级中。似乎不可能为孩子设置依赖关系,但不能为父母设置依赖关系。