Maven依赖详解

Maven依赖总结

一、依赖配置

  1. mave依赖通过groupId、artifactId和version来确定依赖的唯一性;
  2. type:大多数情况下不用声明,默认为jar;
  3. scope:指明依赖范围;
  4. optional:标记依赖是否可选;
  5. exclusions:排除传递性依赖;
<project>
    ...
    <dependencies>
        ...
        <dependency>
            <groupId>...</groupId>
            <artifactId>...</artifactId>
            <version>...</version>
            <type>...</type>
            <scope>...</scope>
            <optional>...</optional>
            <exclusions>
                <exclusion>
                ...
                </exclusion>
                ...
            </exclusions>
        </dependency>
        ...
    </dependencies>
    ...
<project>

二、依赖范围

  首先需要知道maven有三套classpath,编译classpath、测试classpath和运行classpath。

  1. 编译classpath:指编译主代码会使用到的classpath,主代码即默认的src/main/java目录下的代码;
  2. 测试classpath:指编译和运行测试代码会用到的classpath,测试代码即默认的src/test/java目录下的代码;
  3. 运行classpath:运行主代码和打包时会用到的classpath,因为打包也是作为运行主代码所用,所以用的同一套classpath;

  使用不同的依赖范围scope,可将依赖放入不同的classpath,即会在不同的生命周期或阶段生效。

  1. compile:编译依赖范围。默认为此依赖范围,使用此依赖范围的maven依赖,对于编译、测试和运行三种classpath都有效;
  2. test:测试依赖范围。只对测试classpath有效,编译、运行主代码和打包时无效,如JUnit;
  3. provided:已提供依赖范围。对编译classpath和测试classpath有效,运行主代码和打包时无效,如Servlet-api;
  4. runtime:运行时依赖范围。对运行classpath和测试classpath有效,编译主代码时无效,如mysql-connect-java;
  5. system:系统依赖范围。和provided依赖范围一致,使用system范围的依赖需通过systemPath显式指定依赖文件的路径。默认打包后不存在,需增加额外配置
  6. import:导入依赖范围。和三种classpath没有关系。主要用来继承非父工程的依赖版本,springboot中比较常见。
<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.2.2.RELEASE</version>
</parent>

<!-- 由于一个项目只能有一个父工程,所以可通过此方式继承其他工程的依赖版本-->
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-dependencies</artifactId>
      <version>${spring-cloud.version}</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

  依赖范围与classpath的关系

Maven依赖详解

 

三、传递性依赖

  如果A依赖于B,B依赖于C,我们说A对B是第一直接依赖,B对C是第二直接依赖,A对C是传递性依赖。传递性依赖和第一直接依赖范围、第二直接依赖范围有关。

  最左边一列表示第一直接依赖范围,最上面一行表示第二直接依赖范围,则传递性依赖情况如下:

Maven依赖详解

  为方便记忆,总结如下:

  1. 当第二直接依赖的范围是complie时,传递性依赖的范围与第一直接依赖的范围一致;
  2. 当第二直接依赖的范围是test时,依赖不会得以传递;
  3. 当第二直接依赖的范围provided时,只传递第一直接依赖范围也为provided的依赖,且传递性依赖的范围同样为provided;
  4. 当第二直接依赖的范围时runtime时,依赖性传递的范围与第一直接依赖的范围一致,但compile例外,此时传递性依赖的范围为runtime。

四、依赖调解

  maven具有依赖调解机制,当碰到如这样的依赖关系:A -> B -> C -> X(1.0)、A -> D -> X(2.0),X是A的传递性依赖,maven会根据某些原则自主决定X的依赖版本,最终X的依赖版本只会有一个。

  1. 第一原则,路径最近者优先:在以上例子中,X(2.0)会被解析使用;
  2. 第二原则,第一声明者优先:当路径一样时,如:A -> B ->Y(1.0)、A -> C -> Y(2.0),如果B的依赖声明在C之前,则Y(1.0)会被解析使用。

  实际开发中,建议显式指明或排除相同依赖的版本,不应该依赖于maven的依赖调解机制。

五、可选依赖

  通过optional标签指定,默认为false,即满足依赖传递的条件时,此依赖会被传递,显式指定为true时,此依赖不会被传递。

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

六、排除依赖

  由于某种原因,不想引入某个传递性依赖时,或发生依赖冲突时(即同一个groupId和artifactId存在不同的version时,不应依赖自动调解),可通过exclusions排除一个或多个传递性依赖。

  声明exclusion时只需要groupId和artifactId,因为maven解析后的依赖中,groupId和artifactId不会重复。

Maven依赖详解 

七、归类依赖

  当需要依赖同一个工程的不同模块时,这些依赖的版本往往是相同的,此时可通过maven属性将版本号归类,达到避免重复、防止版本冲突、方便版本升级的目的。

  如很多对Spring Framework的依赖,可统一管理版本:

<properties>
    <spring.version>4.1.3.RELEASE</spring.version>
</properties>
<dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
    </dependency>
 
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>${spring.version}</version>
    </dependency>
 
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>${spring.version}</version>
    </dependency>
 
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.version}</version>
    </dependency>
 
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>${spring.version}</version>
    </dependency>
</dependencies>

八、优化依赖

  经过maven的依赖传递机制和依赖调解,以及显式声明和排除依赖后,最终得到的那些依赖叫已解析依赖。可通过以下命令查看和优化已解析依赖:

mvn dependency:list
#查看已解析依赖

Maven依赖详解

 

mvn dependency:tree
#查看依赖树,了解依赖传递信息
#注意依赖树中显示的依赖范围表示对当前工程的依赖范围

Maven依赖详解

 

mvn dependency:analyze
#分析依赖树
#Used undeclared dependencies表示使用未显式声明的依赖
#Unused declared dependencies表示未使用的显式声明的依赖
#此命令只会分析编译主代码和测试代码需要用到的依赖,不分析运行测试代码和主代码的依赖,也就是说无法分析runtime依赖范围的依赖

Maven依赖详解

 

 

  参考:《Maven实战》   

Maven依赖详解

上一篇:操作符优先级


下一篇:Blazor+Dapr+K8s微服务之开发环境调试