2MUCH

Maven之三:坐标、依赖范围、依赖冲突

2022-09-18


Maven之三:坐标、依赖范围、依赖冲突

学习网站:http://mvnbook.com/index.html

Maven构件

构件:在Maven中,任何项目输出都可成为构件。

构件标识(唯一标识,也称为唯一坐标)

除了各种依赖jar包,我们自己开发的项目,也是要通过坐标进行唯一标识的,这样才能才其它项目中进行依赖引用。

坐标组成:

标识和jar包名的对应关系(顺序拼接):

<dependency>
	<groupId>net.sf.json-lib</groupId>   
	<artifactId>json-lib</artifactId>   
	<version>2.2.2</version>  
	<classifier>jdk15</classifier>    
</dependency>
<!-- 对应的jar包名:
json-lib-2.2.2-jdk15.jar 
 -->

classifier标识使用场景

<dependency>
	<groupId>net.sf.json-lib</groupId>   
	<artifactId>json-lib</artifactId>   
	<version>2.2.2</version>  
	<classifier>jdk15</classifier>    
</dependency>

<dependency>  
	<groupId>net.sf.json-lib</groupId>   
	<artifactId>json-lib</artifactId>   
	<version>2.2.2</version>  
	<classifier>jdk13</classifier>    
</dependency>

以上配置信息实际上对应的 jar 包是 json-lib-2.2.2-jdk15.jar 和 json-lib-2.2.2-jdk13.jar。

<dependency>
	<groupId>net.sf.json-lib</groupId>   
	<artifactId>json-lib</artifactId>   
	<version>2.2.2</version>  
	<classifier>jdk15-javadoc</classifier>    
</dependency> 

以上配置信息对应的是 json-lib-2.2.2-jdk15-javadoc.jar。

注意<classifier>的位置:

<dependency>
	<groupId>net.sf.json-lib</groupId>   
	<artifactId>json-lib</artifactId>   
	<classifier>jdk15-javadoc</classifier>  
	<version>2.2.2</version>   
</dependency> 

对应的是 json-lib-jdk15-javadoc-2.2.2.jar,可能会出现找不到jar包的情况。

构件特性

Maven依赖范围

为何需要依赖范围:在不同的执行阶段,需要的依赖可能不同。限制依赖的使用范围(包括编译、测试、运行等操作),使其在其他阶段无法使用。

编译依赖范围(默认),使用此依赖范围对于编译、测试、运行三种都有效,即在编译、测试和运行的时候都要使用该依赖 Jar 包。

测试依赖范围,从字面意思就可以知道此依赖范围只能用于测试,而在编译和运行项目时无法使用此类依赖,典型的是JUnit,它只用于编译测试代码和运行测试代码的时候才需要。

此依赖范围,对于编译和测试有效,而对运行时无效。比如 servlet-api.jar 在 Tomcat 中已经提供了,我们只需要的是编译期提供而已。

运行时依赖范围,对于测试和运行有效,但是在编译主代码时无效,典型的就是JDBC驱动实现。

系统依赖范围,使用 system 范围的依赖时必须通过 systemPath 元素显示地指定依赖文件的路径,不依赖 Maven 仓库解析,所以可能会造成建构的不可移植。

如:

<dependencies>
	<dependency>  
		<groupId>javax.sql</groupId>  
		<artifactId>jdbc-stdext</artifactId>  
		<version>2.0</version>  
		<scope>system</scope>  
		<systemPath>${java.home}/lib/rt.jar</systemPath>  
	</dependency>  
<dependencies>

Maven依赖冲突

直接依赖冲突

<dependencies>

	<dependency>
		<groupId>org.mybatis</groupId>
		<artifactId>mybatis</artifactId>
		<version>3.3.0</version>
	</dependency>

	<dependency>
		<groupId>org.mybatis</groupId>
		<artifactId>mybatis</artifactId>
		<version>3.5.0</version>
	</dependency>

</dependencies>

根据上列的依赖顺序,项目将使用3.5.0版本的 MyBatis Jar。

传递依赖冲突

例如,项目 A 有这样的依赖关系:X->Y->Z(1.0)、X->G->Z(2.0),Z 是 X 的传递性依赖,但是两条依赖路径上有两个版本的 Z,那么哪个 Z 会被 Maven 解析使用呢?两个版本都被解析显然是不对的,因为那会造成依赖重复,因此必须选择一个。

Maven 依赖优化

实际上 Maven 是比较智能的,它能够自动解析直接依赖和传递性依赖,根据预定义规则判断依赖范围的合理性,也可以对部分依赖进行适当调整来保证构件版本唯一。

即使这样,还会有些情况使 Maven 发生误判,因此手工进行依赖优化还是相当有必要的。我们可以使用 maven-dependency-plugin 提供的三个目标来实现依赖分析:

$ mvn dependency:list
$ mvn dependency:tree
$ mvn dependency:analyze

如若需更精细的分析结果,可以在命令后使用诸如以下参数:

-Dverbose

-Dincludes=<groupId>:<artifactId>

Maven依赖冲突调解规则

四大原则:

A --> B --> X(1.1)         // dist(A->X) = 2

A --> C --> D --> X(1.0)   // dist(A->X) = 3

Maven可以按照第一原则自动调解依赖,结果是使用X(1.1)作为依赖。

A --> B --> X(1.1)   // dist(A->X) = 2

A --> C --> X(1.0)   // dist(A->X) = 2

<!-- A pom.xml -->
<dependencies>
    ...
    dependency B
    ...
    dependency C
</dependencies>

若冲突依赖的路径长度相同,那么第一原则就无法起作用了。当路径长度相同,则需要根据A直接依赖包在pom.xml文件中的先后顺序来判定使用那条依赖路径,如果次级模块相同则向下级模块推,直至可以判断先后位置为止。假设依赖B位置在依赖C之前,则最终会选择X(1.1)依赖。

解决依赖冲突

冲突解决方式简单粗暴,直接在 pom.xml 文件中排除冲突依赖即可(使用 <exclusions></exclusions>显式排除)。

<dependency>
  <groupId>org.glassfish.jersey.containers</groupId>
  <artifactId>jersey-container-grizzly2-http</artifactId>
  <!-- 剔除依赖 -->
  <exclusions>
    <exclusion>
      <groupId>org.glassfish.hk2.external</groupId>
      <artifactId>jakarta.inject</artifactId>
    </exclusion>
    ...
  </exclusions>
</dependency>