Maven 依赖管理

作者: data4 | 来源:发表于2016-12-23 12:15 被阅读2142次

    管理包依赖是 Maven 核心功能之一,下面通过如何引入 jar 包;如何解析 jar 包依赖;包冲突是如何产生;如何解决包冲突;依赖管理解决什么问题;什么是依赖范围;使用包依赖的最佳实践等 6 个问题来介绍。

    如何引入 jar 包

    在代码开发时,如果需要使用第三方 jar 包提供的类库,那么需要在 pom.xml 加入该 jar 包依赖。
    例如:使用 zookeeper client

    <dependencies>
      <!-- https://mvnrepository.com/artifact/org.apache.hadoop/zookeeper -->
      <dependency>
          <groupId>org.apache.hadoop</groupId>
          <artifactId>zookeeper</artifactId>
          <version>3.3.1</version>
      </dependency>
    </dependencies>
    

    Maven 如何解析 jar 包依赖——传递依赖

    如上所述,在 pom.xml 中引入 zookeeper jar 包依赖,当 Maven 解析该依赖时,需要引入的 jar 包不仅仅只有 zookeeper,还会有 zookeeper 内部依赖的 jar 包,还会有 zookeeper 内部依赖的 jar 包依赖的 jar 包......,依赖关系不断传递,直至没有依赖。
    例如:上述 pom.xml 引入 zookeeper 依赖,实际引入的 jar 包有:


    zookeeper jar 依赖项

    包冲突如何产生?

    举个🌰:假设 A->B->C->D1, E->F->D2,D1,D2 分别为 D 的不同版本。
    如果 pom.xml 文件中引入了 A 和 E 之后,按照 Maven 传递依赖原则,工程内需要引入的实际 Jar 包将会有:A B C D1 和 E F D2,因此 D1,D2 将会产生包冲突。

    如何解决包冲突

    Maven 解析 pom.xml 文件时,同一个 jar 包只会保留一个,这样有效的避免因引入两个 jar 包导致的工程运行不稳定性。

    Maven 默认处理策略

    • 最短路径优先
      Maven 面对 D1 和 D2 时,会默认选择最短路径的那个 jar 包,即 D2。E->F->D2 比 A->B->C->D1 路径短 1。
    • 最先声明优先
      如果路径一样的话,举个🌰: A->B->C1, E->F->C2 ,两个依赖路径长度都是 2,那么就选择最先声明。

    移除依赖

    如果我们不想通过 A->B->->D1 引入 D1 的话,那么我们在声明引入 A 的时候将 D1 排除掉,这样也避免了包冲突。
    举个🌰:将 zookeeper 的 jline 依赖排除

    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>zookeeper</artifactId>
        <version>3.3.1</version>
        <exclusions>
            <exclusion>
                <groupId>jline</groupId>
                <artifactId>jline</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    

    检测包冲突工具

    mvn dependency:help
    mvn dependency:analyze
    mvn dependency:tree
    mvn dependency:tree -Dverbose
    

    详细参考:
    mvn dependency
    mvn dependency:tree

    依赖管理解决什么问题

    当同一个工程内有多个模块时,并且要求多个模块使用某个 jar 包的相同版本,为了方便统一版本号,升级版本号,需要提取出一个父亲模块来管理子模块共同依赖的 jar 包版本。
    举个🌰:有两个模块 projectA, projectB,它们的依赖分别如下所示:
    projectA:

    <project>
      ...
      <dependencies>
        <dependency>
          <groupId>group-a</groupId>
          <artifactId>artifact-a</artifactId>
          <version>1.0</version>
          <exclusions>
            <exclusion>
              <groupId>group-c</groupId>
              <artifactId>excluded-artifact</artifactId>
            </exclusion>
          </exclusions>
        </dependency>
        <dependency>
          <groupId>group-a</groupId>
          <artifactId>artifact-b</artifactId>
          <version>1.0</version>
          <type>bar</type>
          <scope>runtime</scope>
        </dependency>
      </dependencies>
    </project>
    

    projectB:

    <project>
      ...
      <dependencies>
        <dependency>
          <groupId>group-c</groupId>
          <artifactId>artifact-b</artifactId>
          <version>1.0</version>
          <type>war</type>
          <scope>runtime</scope>
        </dependency>
        <dependency>
          <groupId>group-a</groupId>
          <artifactId>artifact-b</artifactId>
          <version>1.0</version>
          <type>bar</type>
          <scope>runtime</scope>
        </dependency>
      </dependencies>
    </project>
    

    projectA 和 projectB 共同依赖了 group-a/artifact-b/1.0,提取公共依赖,生成 parent, parent 依赖如下:

    <project>
      ...
      <dependencyManagement>
        <dependencies>
          <dependency>
            <groupId>group-a</groupId>
            <artifactId>artifact-b</artifactId>
            <version>1.0</version>
            <type>bar</type>
            <scope>runtime</scope>
          </dependency>
        </dependencies>
      </dependencyManagement>
    </project>
    

    则 projectA 和 projectB 均不需要指定 group-a/artifact-b 的 version 信息,未来升级 version 信息时,只需要在 parent 内部指定。

    projectA:

    <project>
      ...
      <dependencies>
        <dependency>
          <groupId>group-a</groupId>
          <artifactId>artifact-a</artifactId>
          <version>1.0</version>
          <exclusions>
            <exclusion>
              <groupId>group-c</groupId>
              <artifactId>excluded-artifact</artifactId>
            </exclusion>
          </exclusions>
        </dependency>
        <dependency>
          <groupId>group-a</groupId>
          <artifactId>artifact-b</artifactId>
        </dependency>
      </dependencies>
    </project>
    

    projectB:

    <project>
      ...
      <dependencies>
        <dependency>
          <groupId>group-c</groupId>
          <artifactId>artifact-b</artifactId>
          <version>1.0</version>
          <type>war</type>
          <scope>runtime</scope>
        </dependency>
        <dependency>
          <groupId>group-a</groupId>
          <artifactId>artifact-b</artifactId>
        </dependency>
      </dependencies>
    </project>
    

    依赖范围

    如果不显示执行 <scope> 属性时,默认 <scope>compile</scope>。
    scope 有哪些属性:compile, provided, runtime, test, system 等。
    详细参考:依赖范围

    最佳实践

    (1)项目中源代码使用的 jar 包一定在 pom.xml 中显示引用。
    (2)经常 check 一下包冲突,检查是否需要处理。
    (3)当使用多个模块时,parent 一定要使用包管理模块来规范 Jar 包版本,而不是包依赖模块直接引入依赖。 dependencyManagement vs dependencies

    参考

    参考

    maven 入门指南
    maven 生命周期
    Maven 默认插件以及功能
    maven 依赖管理
    maven-shade-plugin 入门指南
    maven-assembly-plugin 入门指南
    maven 依赖机制

    欢迎大家访问本人网站 程序员工具箱 致力于贡献便捷的工具,帮助程序员写出更优秀的代码,同时也欢迎大家一起来贡献。

    相关文章

      网友评论

      本文标题:Maven 依赖管理

      本文链接:https://www.haomeiwen.com/subject/aydkvttx.html