Maven 中 dependencies 与 dependencyManagement 的区别

前提

这段时间项目中遇到过了一些 Jar 包冲突的问题,很多是由于我们项目模块很多的时候,用 Maven 管理不当导致的冲突问题,本文就这个问题参考网上的资料,于是总结下 Maven 中 dependencies 与 dependencyManagement 的区别。

假设项目结构如下:

parent 为父模块,抽象出来管理子项目的公共依赖,为了项目的正确运行,必须让所有的子项目使用依赖项的统一版本,必须确保应用的各个项目的依赖项和版本一致,才能保证测试的和发布的是相同的结果。

dependencyManagement

在项目的 parent 层,可以通过 dependencyManagement 元素来管理 jar 包的版本,让子项目中引用一个依赖而不用显示的列出版本号。

parent 中 pom.xml

<properties>
    <version.framework>1.0-SNAPSHOT</version.framework>
    <javaee-api.version>1.0-SNAPSHOT</javaee-api.version>
</properties>
<dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>com.zhisheng</groupId>
        <artifactId>framework-cache</artifactId>
        <version>${version.framework}</version>
      </dependency>
      <dependency>  
        <groupId>javax</groupId>  
        <artifactId>javaee-api</artifactId>  
        <version>${javaee-api.version}</version>  
      </dependency>  
    </dependencies>
</dependencyManagement>

extendion 中的 pom.xml

<parent>  
    <artifactId>parent</artifactId>  
    <groupId>com.zhisheng</groupId>
    <version>0.0.1-SNAPSHOT</version>  
    <relativePath>../parent/pom.xml</relativePath> 
</parent>
<!--依赖关系-->  
<dependencies>  
    <dependency>  
        <groupId>javax</groupId>  
        <artifactId>javaee-api</artifactId>  
    </dependency>  
    <dependency>
        <groupId>com.zhisheng</groupId>
        <artifactId>framework-cache</artifactId>
    </dependency>
    <dependency>  
        <groupId>com.fasterxml.jackson.core</groupId>  
        <artifactId>jackson-annotations</artifactId>  
    </dependency>  
</dependencies>  

这样做的好处:统一管理项目的版本号,确保应用的各个项目的依赖和版本一致,才能保证测试的和发布的是相同的成果,因此,在顶层 pom 中定义共同的依赖关系。同时可以避免在每个使用的子项目中都声明一个版本号,这样想升级或者切换到另一个版本时,只需要在父类容器里更新,不需要任何一个子项目的修改;如果某个子项目需要另外一个版本号时,只需要在 dependencies 中声明一个版本号即可。子类就会使用子类声明的版本号,不继承于父类版本号。

我们知道 Maven 的继承和 Java 的继承一样,是无法实现多重继承的,如果10个、20个甚至更多模块继承自同一个模块,那么按照我们之前的做法,这个父模块的 dependencyManagement 会包含大量的依赖。如果你想把这些依赖分类以更清晰的管理,那就不可能了,import scope 依赖能解决这个问题。你可以把 dependencyManagement 放到单独的专门用来管理依赖的 POM 中,然后在需要使用依赖的模块中通过 import scope 依赖,就可以引入dependencyManagement。例如可以写这样一个用于依赖管理的 POM:

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.zhisheng.sample</groupId>
  <artifactId>sample-dependency-infrastructure</artifactId>
  <packaging>pom</packaging>
  <version>1.0-SNAPSHOT</version>
  <dependencyManagement>
    <dependencies>
        <dependency>
          <groupId>junit</groupId>
          <artifactid>junit</artifactId>
          <version>4.8.2</version>
          <scope>test</scope>
        </dependency>
        <dependency>
          <groupId>log4j</groupId>
          <artifactid>log4j</artifactId>
          <version>1.2.16</version>
        </dependency>
    </dependencies>
  </dependencyManagement>
</project>

然后就可以通过非继承的方式来引入这段依赖管理配置:

<dependencyManagement>
    <dependencies>
        <dependency>
          <groupId>com.zhisheng.sample</groupId>
          <artifactid>sample-dependency-infrastructure</artifactId>
          <version>1.0-SNAPSHOT</version>
          <type>pom</type>
          <scope>import</scope>
        </dependency>
    </dependencies>
  </dependencyManagement>

  <dependency>
    <groupId>junit</groupId>
    <artifactid>junit</artifactId>
  </dependency>
  <dependency>
    <groupId>log4j</groupId>
    <artifactid>log4j</artifactId>
  </dependency>

这样,父模块的 POM 就会非常干净,由专门的 packaging 为 pom 的 POM 来管理依赖,也契合的面向对象设计中的单一职责原则。此外,我们还能够创建多个这样的依赖管理 POM,以更细化的方式管理依赖。这种做法与面向对象设计中使用组合而非继承也有点相似的味道。

dependencies

相对于 dependencyManagement,所有声明在父项目中 dependencies 里的依赖都会被子项目自动引入,并默认被所有的子项目继承。

区别

dependencies 即使在子项目中不写该依赖项,那么子项目仍然会从父项目中继承该依赖项(全部继承)

dependencyManagement 里只是声明依赖,并不实现引入,因此子项目需要显示的声明需要用的依赖。如果不在子项目中声明依赖,是不会从父项目中继承下来的;只有在子项目中写了该依赖项,并且没有指定具体版本,才会从父项目中继承该项,并且 version 和 scope 都读取自父 pom; 另外如果子项目中指定了版本号,那么会使用子项目中指定的jar版本。

消除多模块插件配置重复

与 dependencyManagement 类似的,我们也可以使用 pluginManagement 元素管理插件。一个常见的用法就是我们希望项目所有模块的使用 Maven Compiler Plugin 的时候,都使用 Java 1.8,以及指定 Java 源文件编码为 UTF-8,这时可以在父模块的 POM 中如下配置 pluginManagement:

<build>
  <pluginManagement>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.5.1</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
          <encoding>UTF-8</encoding>
        </configuration>
      </plugin>
    </plugins>
  </pluginManagement>
</build>

这段配置会被应用到所有子模块的 maven-compiler-plugin 中,由于 Maven 内置了 maven-compiler-plugin 与生命周期的绑定,因此子模块就不再需要任何 maven-compiler-plugin 的配置了。

与依赖配置不同的是,通常所有项目对于任意一个依赖的配置都应该是统一的,但插件却不是这样,例如你可以希望模块 A 运行所有单元测试,模块 B 要跳过一些测试,这时就需要配置 maven-surefire-plugin 来实现,那样两个模块的插件配置就不一致了。这也就是说,简单的把插件配置提取到父 POM 的 pluginManagement 中往往不适合所有情况,那我们在使用的时候就需要注意了,只有那些普适的插件配置才应该使用 pluginManagement 提取到父 POM 中。

关于插件 pluginManagement,Maven 并没有提供与 import scope 依赖类似的方式管理,那我们只能借助继承关系,不过好在一般来说插件配置的数量远没有依赖配置那么多,因此这也不是一个问题。

最后

你看到的只是冰山一角,更多请看书籍《Maven 实战》

转载请注明地址:http://www.54tianzhisheng.cn/2017/11/11/Maven-dependencies-dependencyManagement/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhisheng_blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值