ChatGPT解决这个技术问题 Extra ChatGPT

Maven中dependencyManagement和dependencies的区别

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?为什么不在需要的模块中直接定义呢?


L
Lii

我对这个问题的回答很晚,但我认为值得一个比接受的更清晰的回答(这是正确的,但不强调实际重要的部分,你需要自己推断)。

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

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

部分中指定的工件只有在子模块本身的 部分中也指定时才会包含在子模块中。你问为什么好?因为您在父 POM 中指定版本和/或范围,并且您可以在指定子 POM 中的依赖项时将它们排除在外。这可以帮助您对子模块的依赖项使用统一版本,而无需在每个子模块中指定版本。


我也迟到了,但这里有一个问题。对于dependancyManagement 部分,为什么不在父项中设置属性(版本),然后在子项中仅在依赖项部分中使用该版本?为什么它在父母和孩子的pom中都设置了?
我会在 Artifacts specified in the <dependencies> section will ALWAYS be included as a dependency of the child module(s) 中添加它们也包含在父级中。似乎不可能为孩子设置依赖关系,但不能为父母设置依赖关系。
@DimitarVukman 因为答案不完整:它不仅仅是版本。您可能已经为依赖项定义了版本、范围甚至排除项。而且,即使只是对于版本,在子 pom 中声明 GA(组,工件)而不是 GAV(并且 V 的值是您在父级中自定义定义的值)会更清晰和更易于维护
当您有 10、15 或数十个项目时,父 pom 中的 dependencyManagement 的价值变得清晰,并且您需要确保它们都使用相同版本的各种共享库,而不必声明所有这些项目都使用所有那些依赖
为什么不将依赖项也放入父 pom.xml 中?万一所有子项目都不需要这些依赖项,这不是一个好主意吗?
P
Pascal Thivent

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

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


那么,即使它们在父项目的 pom 中的 部分声明,也需要在子项目 pom 中声明依赖关系?是否可以对依赖项进行某种继承?
是的,您仍然需要在子 POM 中定义它们以表明您正在使用它们。它们实际上并未包含在子项目中,只是因为它们位于父 POM 的 <dependencyManagement> 中。在 <dependencyManagement> 中包含依赖项可集中管理每个依赖项的版本、范围和排除项(如果您决定使用它)以及何时使用它。 Maven 的guide to dependency management 涉及所有细节。
第二段(dependencyManagement 还控制传递依赖项)仅在显式设置依赖项时才为真:stackoverflow.com/questions/28312975/…
@johnny-b-goode 你仍然可以做的是在你的父 pom.xml 中创建一个新的 dependencies 部分。我们这样做是为了让所有子项目默认都有一些 apache-commons,而不是一直声明它们。
需要注意的是,dependencyManagement 还控制范围,而不仅仅是传递依赖项的版本。
J
Janez Kuhar

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>

有点离题的问题:依赖类型“bar”是什么意思?我在 Maven 文档的示例 pom 中看到但找不到定义。我认为这是“战争”或“罐子”的错字,但我在其他例子中看到了它,比如你的例子。
NobodyMan - 所以它只是另一种存档类型的占位符。就像使用'foo'。或者,如果有人使用扩展名“bar”制作了自定义类型,则可以使用它。并且有很多不为人知的档案类型。像 sar,它是 jboss 服务归档。
您的示例非常清楚,并重申了我从文档中自己摸索的内容。你提交给 Maven 项目了吗?在研究了您的示例后,我正准备简化一个具有两者并且只需要依赖声明的 POM,因为与之关联的项目没有子项目。
好吧,我正要删除 DependencyManagement 节点,直到我想到离开它可以让我为任何间接找到依赖关系树的子 POM 建立最低版本。例如,在追查 javax.cache.cache-apI 时,我发现了一个明显更新的版本 1.0.0(相对于 0.3.0),它也可以在整个过程中使用。
这个解释很到位。
A
Andrejs

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

这是一个增量示例:

我在我的 parent pom 中声明:

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

繁荣!我的 Child AChild BChild C 模块中有它:

子 pom 继承的隐含性

一个管理的地方

无需在 child poms 中重新声明任何内容

如果我愿意,我仍然可以在 Child B 中重新维护和覆盖到 18.0 版本。

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

他们仍然会继承它,这是不希望的!这就像 Java God 对象代码的味道,你从一个类中继承了一些有用的位,以及大量不需要的东西。

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

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


中提到的依赖项是否会为父 pom 项目下载?
您确定如果我们在父 pom 中使用 <dependencyManagement> 那么默认情况下依赖项不会在子 pom 中继承吗?因为在文档中:maven.apache.org/guides/introduction/… 在解释 <dependencyManagement> 的第二次使用时,它看起来默认会被继承。在一行中,他们说:“当在项目 B 上运行 maven 时,将使用工件 a、b、c 和 d 的 1.0 版,而不管它们的 pom 中指定的版本是什么”,即使“b”没有用于项目B
自己试试
正如有人已经问过但仍未得到答复,为什么不简单地使用父 pom 中的属性,而无需在父级中使用 dependencyManagement 部分?
@Andrey M. Stepanov - 好问题。对于单个依赖项,您可能希望集中管理的不仅是版本,还包括 和其他工件属性。那么使用 会更简洁,而不是每个工件使用 3-4 个属性。
K
Koray Tugay

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

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

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


那么,依赖关系不会继承吗?无论如何都需要在子项目的pom中声明?
是的,您无论如何都需要在子项目中声明它们,但不指定版本。
当您希望在具有父子关系的多个 java 项目中管理版本时,此方案很有用。
I
IamVickyAV

对不起,我迟到了。

让我尝试使用 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 仅在数据模块中被列为依赖项(甚至在父模块中也没有)


C
Clijsters

有一些答案概述了 <depedencies><dependencyManagement> 标记与 maven 之间的区别。

但是,下面以简洁的方式阐述了几点:

允许整合跨不同模块使用的所有依赖项(在子 pom 级别使用) - 清晰,中央依赖项版本管理 允许根据需要轻松升级/降级依赖项,在其他情况下,这需要在每个子 pom 级别 - 标签中提供的一致性依赖项总是被导入,而父 pom 中 提供的依赖项只有在子 pom 在其 标签中有相应条目时才会被导入。


M
Mustafa Güven

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


我认为这种说法可能不正确。在 Maven 的依赖管理示例 (#2) 中,他们说在父 pom 中定义的具有版本的依赖关系将覆盖子 pom 中指定的版本:“当 maven 在项目 B 版本 1.0 的工件 a、b、c 上运行时, 并且无论在他们的 pom 中指定的版本如何,都将使用 d。”
@devdanke 至少,Eclipse m2e 发出警告:Overriding managed version ... for ....
H
Harry Coder

用我自己的话来说,您的 parent-project 可以帮助您提供 2 种依赖项:

隐式依赖项:父项目的 部分中定义的所有依赖项都被所有子项目继承

显式依赖项:允许您选择要在子项目中应用的依赖项。因此,您使用 部分来声明您将在不同子项目中使用的所有依赖项。最重要的是,在本节中,您定义了一个 ,这样您就不必在您的子项目中再次声明它。

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


r
rps

两者之间的区别最好体现在 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 元素的引用还是作为独立元素,都会立即被解析(即安装到本地存储库并可用于类路径)。


G
Gangnus

在 Eclipse 中,dependencyManagement 中还有一项功能。如果在没有它的情况下使用 dependencies,则会在 pom 文件中注意到未找到的依赖项。如果使用 dependencyManagement,未解决的依赖关系在 pom 文件中不会被注意到,错误只会出现在 java 文件中。 (进口等...)


@KorayTugay “未解决”,当然,感谢您的通知。已编辑。
x
xxy

我不建议使用 dependencyManagement

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

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


您错过了可以定义/覆盖传递依赖项的一点,它为您在项目中提供了一个位置,dependencyManagement 是正确的选择...如果您有单个模块设置,那么您的声明很好,但如果您有多个它更容易在你的根 pom 中有一个 dependencyManagement 并在那里定义所有东西......当你需要改变东西时集中位置......还有许多库等给你一个 bom,这使得使用这些框架/库变得更容易(junit 5/弹簧靴/等)...
J
JSamir

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

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

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


M
MagGGG

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

例子:

项目 A 有库 x:1.0.1

项目 A 有 B 库

库有库 x:1.0.0

有了这个集合,您将在项目 A 同时 x:1.0.1x:1.0.0 时发生冲突。要解决此问题,您可以将特定版本的依赖项放入 <dependencyManagement> 标记


P
Phan Xuan Bao

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


正如目前所写的那样,您的答案尚不清楚。请edit添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。您可以找到有关如何写出好答案的更多信息in the help center