在Maven中dependencyManagement和依赖关系之间的差异

时间:2010-04-12 02:43:17

标签: maven pom.xml dependency-management

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

14 个答案:

答案 0 :(得分:577)

我对这个问题的时间已经很晚了,但我认为它比接受的答案更明确(这是正确的,但并不强调你需要的实际重要部分)推断自己)。

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

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

<dependencyManagement> 部分中指定的工件只会在子模块中包含,如果它们也在 <dependencies> 部分中指定子模块本身。你问为什么这么好?因为您在父项中指定了版本和/或作用域,并且在指定子POM中的依赖项时可以将它们保留。这可以帮助您为子模块使用统一版本的依赖项,而无需在每个子模块中指定版本。

答案 1 :(得分:388)

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

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

答案 2 :(得分:44)

Maven网站上的documentation非常糟糕。 dependencyManagement所做的只是将您的依赖关系定义(版本,排除等)移动到父pom,然后在子poms中您只需要放置groupId和artifactId。这就是它(除了父pom链等之外,但这也不是很复杂 - dependencyManagement胜过父级别的依赖 - 但如果有关于那个或导入的问题,那么Maven文档好一点)。

阅读了所有的&#39;&#39;&#39;&#39; c&#39; Maven网站上的垃圾变得混乱,我重新写了他们的例子。因此,如果你有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>

答案 3 :(得分:43)

就像你说的那样; dependencyManagement用于将所有依赖关系信息提取到公共POM文件中,从而简化子POM文件中的引用。

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

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

答案 4 :(得分:27)

在我看来,还有一件事情还不够突出,那就是不需要的继承

这是一个增量示例:

我在我的parent pom:

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

热潮!我在我的Child AChild BChild C模块中使用了它:

  • 儿童poms继承的implicilty
  • 一个管理的地方
  • 无需重新安装儿童poms中的任何内容
  • 如果我愿意,我仍然可以在version 18.0中重新展示并覆盖Child B

但是如果我最终不需要Child C中的guava,以及将来Child DChild E模块中的guava怎么办?

他们仍会继承它,这是不受欢迎的! 这就像Java God Object代码的气味,你从类中继承了一些有用的东西,还有一些不需要的东西。

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

显然,你不会为Child C做这件事,因此你的模块仍然是精益的。

答案 5 :(得分:11)

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

然而,下面简要阐述了几点:

  1. <dependencyManagement>允许合并在不同模块中使用的所有依赖项(在子pom级别使用) - 清晰度中央依赖项版本管理
  2. <dependencyManagement>允许根据需要轻松升级/降级依赖关系,在其他情况下,这需要在每个子pom级别执行 - 一致性
  3. 始终导入
  4. <dependencies>标记中提供的依赖项,而仅当子pom在其<dependencyManagement>标记中包含相应条目时,才会导入父pom中<dependencies>处提供的依赖项。

答案 6 :(得分:9)

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

答案 7 :(得分:4)

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

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

如果在子模块本身的部分中也指定了子模块,则该部分中指定的工件将仅包含在子模块中。你问为什么这么好?因为您在父项中指定了版本和/或作用域,并且在指定子POM中的依赖项时可以将它们保留。这可以帮助您为子模块使用统一版本的依赖项,而无需在每个子模块中指定版本。

答案 8 :(得分:4)

对不起,我参加聚会很晚。

让我尝试使用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番石榴被列为每个模块(包括父模块)中的依赖项,而 apache commons 仅被列为数据模块中的依赖项(甚至不在父模块中)

答案 9 :(得分:2)

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

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

根据我的观点,<dependencyManagement>(如果我错了,请纠正我)仅对帮助您集中依赖项的版本很有用。就像一种辅助功能。

答案 10 :(得分:1)

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

答案 11 :(得分:1)

两者之间的区别最好体现在Maven网站文档中提供的dependencyManagement元素似乎是必需的和充分的定义:

dependencyManagement

“继承自此项目的项目的默认依赖关系信息。本节中的依赖关系不会立即得到解决。相反,当从此项目派生的POM声明由匹配的groupId和artifactId描述的依赖项时,版本和其他值如果尚未指定,则将使用此部分中的依赖项。” [https://maven.apache.org/ref/3.6.1/maven-model/maven.html]

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

” ..用于将依赖项引用与dependencyManagement部分进行匹配的最小信息集实际上是{groupId,artifactId,type,classifier}。在许多情况下,这些依赖项将引用没有分类器的jar工件。由于类型字段的默认值为jar,默认分类器为null,因此这使我们可以简化设置为{groupId,artifactId}的标识。” [https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html]

因此,依赖项的所有子元素(作用域,排除项等)(除了groupId,artifactId,类型,分类器,不仅是版本)都可以在该点进行锁定/默认设置(和因此继承自此),您可以在dependencyElement中指定依赖项。如果您使用type和classifier子元素(请参见第一个引用的网页检查所有子元素)指定的依赖关系分别不是jar和not null,则需要{groupId,artifactId,classifier,type}引用(解析)该依赖关系,该依赖关系在依赖项中的任何时候都源自dependencyManagement元素。另外,如果您不打算覆盖分类器和类型的默认值(分别为jar和null),则{groupId,artifactId}就足够了。因此,default是该定义中的一个好关键字。在您引用依赖项时,任何明确指定值的子元素(当然,除了groupId,artifactId,分类器和类型之外)都将覆盖dependencyManagement元素中的默认值。

因此,无论是作为dependencyManagement元素的引用还是作为独立项的引用,dependencyManagement之外的任何依赖项元素都将立即得到解决(即,安装到本地存储库中并且可用于类路径)。

答案 12 :(得分:0)

如果您还是有一个pom,那么我认为仅使用<dependencyManagement>来控制版本(可能是作用域)就是浪费空间,并使初级开发人员感到困惑。

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

我认为<dependencyManagement>应该用于对依赖项的“真实”管理,例如排除等。

答案 13 :(得分:0)

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

示例:

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

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