Maven依赖冲突的产生缘由和解决方式

maven定义了许多dependency,每一个dependency内部也会定义它的dependency。web

首先咱们来看一下依赖冲突产生的缘由:

  1. 若是项目的依赖A和依赖B同时引入了依赖C。
  2. 若是依赖C在A和B中的版本不一致就可能依赖冲突。
  3. 好比 项目 <- A, B, A <- C(1.0),B <- C(1.1)。
  4. 那么maven若是选择高版本C(1.1)来导入(这个选择maven会根据不等路径短路径原则和同等路径第一声明原则选取),C(1.0)中的类c在C(1.1)中被修改而不存在了。
  5. 在编译期可能并不会报错,由于编译的目的只是把业务源代码编译成class文件,因此若是项目源代码中没有引入共有依赖C因升级而缺失的类c,就不会出现编译失败。除非源代码就引入了共有依赖C因升级而缺失的类c则会直接编译失败。
  6. 在运行期,颇有可能出现依赖A在执行过程当中调用C(1.0)之前有可是升级到C(1.1)就缺失的类c,致使运行期失败,出现很典型的依赖冲突时的NoClassDefFoundError错误。
  7. 若是是升级后出现原有的方法被修改而不存在的状况时,就会抛出NoSuchMethodError错误。

那么怎么来解决依赖冲突呢?

  1. 首先能够借助Maven查看依赖的依赖树来分析一下:mvn dependency:tree,或者使用IDEA的插件Dependency Analyzer插件来可视化地分析依赖关系。这个过程后能够明确哪些dependency引入了可能会冲突的依赖。
  2. 好比咱们的项目引入A的依赖C为1.1版本,引入的B会在内部依赖C的1.0版本,那么Dependency Analyzer插件会出现依赖冲突提示,会提示B引入的C的1.0版本和当前选用的C的1.1版本冲突于是被忽略(1.0 omitted for conflict with 1.1)。
  3. 若是这时候打war包出来启动颇有可能会遇到因依赖冲突而出现的NoClassDefFoundErrorNoSuchMethodError,致使编译期正常的代码没法在运行期跑起来。
  4. 因为A引入的C的版本高而B依赖的C版本低,咱们优先会选择兼容高版本C的方案,即试图把B的版本调高以使得引入的依赖C能够和A引入的依赖A达到一致的版本,以此来解决依赖冲突。固然这是一个理想情况。
  5. 若是找到了目前已有的全部的B的版本,均发现其依赖的C没有与A一致的1.1版本,好比B是一个许久未升级的旧项目,那么也能够考虑把A的版本拉低以使得C的版本降到与B一致的1.0版本,固然这也可能会反过来致使A不能正常工做。
  6. 上面已经能够看出来解决依赖冲突这件事情并不简单,很难顾及两边,不少状况下引入不一样版本依赖的极可能超过两方而是更多方。
  7. 那么来考虑一下妥协的方案,若是A引入的C使用的功能并不跟被抛弃的类或方法有关,而是其余在1.1版本中仍然没有改变的类或方法,那么能够考虑直接使用旧的1.0版本,那么可使用exclusion标签来在A中排除掉对C的依赖,那么A在使用到C的功能时会使用B引入的1.0旧版本C。即A其实向B妥协使用了B依赖的C。

来看个实例:spring

  1. 项目引入了4.2.5.RELEASEspring-webmvc,同时引入了3.2.1.RELEASEspring-security-web
  2. 用Dependency Analyzer插件分析获得的依赖关系是这样的:

  3. 为何maven会选择4.2.5.RELEASE引入呢,其实前文提到过:好比对spring-beans这个依赖来讲spring-webmvc:4.2.5.RELEASE-spring-beans:4.2.5.RELEASE的路径是2,而spring-security-web:3.2.1.RELEASE-spring-security-core:3.2.1.RELEASE-spring-beans:3.2.8.RELEASE的路径是3,所以根据不等路径取短路径原则则选取了前者,即spring-beans:4.2.5.RELEASE,注意的是引入时并非优先引入高版本的,同时若是依赖的路径长相等则取第一个出现的版本。
  4. 能够看到spring-security-web引入的一众spring依赖是3.X版本的,同spring-webmvc4.2.5.RELEASE版本不一致。
  5. 果不其然,打好的war包启动的时候即会报错终止。
  6. 那么在咱们的问题在于兼容高版本的spring-webmvc4.2.5.RELEASE的情形下寻找spring依赖版本一致的spring-security-web,在www.mvnrepository.com寻找合适版本依赖的spring-security-web,最终咱们看到了4.1.0.RELEASE版本彷佛还不错,其依赖的spring版本在4.2.5.RELEASE

  7. 所以咱们将这个版本的spring-security-web填入pom.xml看一下效果,确实已经没有依赖冲突产生了:

  8. 若是咱们找不到完美匹配4.2.5.RELEASEspring-security-web怎么办,好比全部的spring-security-web版本就是在4.2.3.RELEASE或者4.2.6.RELEASE,那么咱们就得想到妥协一下了:使用4.1.1.RELEASE版本的spring-security-web,它的spring依赖版本是4.3.1.RELEASE

那么咱们试图使用exclusive标签来忽略spring版本:express

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>4.1.1.RELEASE</version>
    <exclusions>
        <exclusion>
            <artifactId>spring-aop</artifactId>
            <groupId>org.springframework</groupId>
        </exclusion>
        <exclusion>
            <artifactId>spring-beans</artifactId>
            <groupId>org.springframework</groupId>
        </exclusion>
        <exclusion>
            <artifactId>spring-context</artifactId>
            <groupId>org.springframework</groupId>
        </exclusion>
        <exclusion>
            <artifactId>spring-core</artifactId>
            <groupId>org.springframework</groupId>
        </exclusion>
        <exclusion>
            <artifactId>spring-expression</artifactId>
            <groupId>org.springframework</groupId>
        </exclusion>
        <exclusion>
            <artifactId>spring-web</artifactId>
            <groupId>org.springframework</groupId>
        </exclusion>
    </exclusions>
</dependency>

依赖冲突会消失,spring-security-web会使用4.2.5.RELEASE版本的spring:mvc

相关文章
相关标签/搜索