Maven如何解决包冲突

Maven如何解决包冲突

Maven是一个跨平台的项目管理工具。作为Apache组织的一个颇为成功的开源项目,其主要服务于基于Java平台的项目创建,依赖管理和项目信息管理。

Maven依赖配置

maven的依赖配置主要是如下格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

<project>
...
<dependencies>
<dependency>
<groupId>...</groupId>
<artifactId>...</artifactId>
<version>...</version>
<type>...</type>
<scope>...</scope>
<optional>...</optional>
<exclusions>
<exclusion>
<groupId>...</groupId>
<artifactId>...</artifactId>
</exclusion>
...
</exclusions>
</dependency>
...
</dependencies>
...
</project>

根元素下project下的dependencies可以包含一个或者多个dependency元素,以声明一个或者多个项目依赖。每个依赖可以包含的元素有:

  • groupId,artifactId和version:依赖的基本坐标,对于任何一个依赖来说,基本坐标是最重要的,Maven根据坐标才能找到需要的依赖。
  • type:依赖的类型,对应于项目坐标定义的packaging。大部分情况下,该元素不必声明,其默认值是jar。
  • scope:依赖的范围,下面会进行详解。
  • optional:标记依赖是否可选。
  • exclusions:用来排除传递性依赖

依赖范围

Maven在编译主代码的时候需要使用一套classpath,在编译和执行测试的时候会使用另一套classpath,实际运行项目的时候,又会使用一套classpath。

依赖范围就是用来控制依赖与这三种classpath(编译classpath、测试classpath、运行classpath)的关系,Maven有以下几种依赖范围:

  • compile:编译依赖范围。如果没有指定,就会默认使用该依赖范围。使用此依赖范围的Maven依赖,对于编译、测试、运行三种classpath都有效。典型的例子是spring-core,在编译,测试和运行的时候都需要使用该依赖。
  • provided:已提供依赖范围。使用此依赖范围的Maven依赖,对于编译和测试classpath有效,但在运行时无效。典型的例子是servlet-api,编译和测试项目的时候需要该依赖,但在运行项目的时候,由于容器已经提供,就不需要Maven重复地引入一遍。
  • test:测试依赖范围。使用此依赖范围的Maven依赖,只对于测试classpath有效,在编译主代码或者运行项目的使用时将无法使用此类依赖。典型的例子就是JUnit,它只有在编译测试代码及运行测试的时候才需要。
  • runtime:运行时依赖范围。使用此依赖范围的Maven依赖,对于测试和运行classpath有效,但在编译主代码时无效。典型的例子是JDBC驱动实现,项目主代码的编译只需要JDK提供的JDBC接口,只有在执行测试或者运行项目的时候才需要实现上述接口的具体JDBC驱动。
  • system:系统依赖范围。该依赖范围与provided所表示的依赖范围一致,对于编译和测试classpath有效,但在运行时无效。只是使用system范围依赖时必须通过systemPath元素显式地指定依赖文件的路径。由于此类依赖不是通过Maven仓库解析的,而且往往与本机系统绑定,可能造成构建的不可移植,因此应该谨慎使用,systemPath元素可以引用环境变量。

依赖传递

当我们项目依赖了Jar包A,而Jar包A又依赖了Jar包B和Jar包C,那么maven在引入的时候就会把A依赖的包B和C也一并引入进来。

包冲突产生的原因

假设项目A引用了Jar包B和Jar包C,Jar包B又引用了Jar包D的1.0版本,而Jar包C引用了Jar包D的2.0版本。这个时候,因为依赖传递,Maven会引入Jar包D的1.0和2.0版本,这个时候就产生了包冲突。

Maven解决包冲突的措施

在某些场景下,Maven是可以自动处理包冲突问题的,它遵循以下两个原则:

  • 最短路径优先

    如果有两个引用关系链A->B->C和A->D->E->C,那么Maven会自动选择短的这个引用链上的C的版本包

  • 最先声明优先

    如果两个引用关系链的长度是一样的。比如A->B ->C和A->D->C,那么Maven会自动选取先声明的B或者D对应的C的版本包

  • 手动排除

    如果Maven的自动策略无法处理冲突问题的话,那么可以通过手动排除的方法,在Pom文件中使用<exclusion>标签排除冲突的Jar包

1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>1.4.4.RELEASE</version>
<exclusions>
<exclusion>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</exclusion>
</exclusions>
</dependency>
  • 版本锁定

    把公共依赖提取出来,放到父版本的pom文件中,子项目一律使用继承的方式引用父项目的pom文件。

查看包冲突的有关工具

  • Maven Helper
  • mvn dependency:tree

参考资料

Maven的传递性依赖及其jar包冲突解决

0%