Maven依赖冲突

什么是Maven依赖冲突

我相信你在开发中一定碰到过下面这些情况,尤其是在变更maven依赖后

  1. 代码编写完成,启动报错java.lang.NoSuchMethodException,但是你检查了一圈发现IDE正确识别到了有这个方法
  2. 代码编写完成,启动报错java.lang.ClassNotFoundException,同样,IDE编辑器爷识别到有这个类
  3. 再或者,启动不报错,运行时,当调用到某给方法或者类时,出现上面两种异常

以上三种情况,大多数原因都是依赖冲突导致的。

依赖冲突就是maven依赖中存在如下情况:

  • 一个依赖同时存在多版本
  • maven在打包时只会选取其中一个版本,遵循最短路径原则。
  • 如果你的应用运行在某些其他“容器”里面,例如Spark等可能依赖会和“容器”本身本来就存在的依赖冲突。

所以,如果你运气好,maven取到的一个版本恰好能满足其他代码的依赖调用,那就不会报错。

但是大多数情况就是由于取到的版本不对出现依赖缺失报错问题。

如何解决依赖冲突

前面我们提到,一个依赖同时存在多版本导致冲突,那我们要解决的就是保证工程依赖中只保留一个唯一的依赖版本。

看个例子

modelA

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>demo</artifactId>
        <groupId>top.oneyoung.maven-conflict-demo</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>modelA</artifactId>
    <dependencies>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.78</version>
        </dependency>
    </dependencies>

</project>

modelB

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>demo</artifactId>
        <groupId>top.oneyoung.maven-conflict-demo</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>modelB</artifactId>
    <dependencies>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.75</version>
        </dependency>
    </dependencies>

</project>

modelC

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>demo</artifactId>
        <groupId>top.oneyoung.maven-conflict-demo</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>modelC</artifactId>

    <dependencies>
        <dependency>
            <groupId>top.oneyoung.maven-conflict-demo</groupId>
            <artifactId>modelA</artifactId>
        </dependency>
        <dependency>
            <groupId>top.oneyoung.maven-conflict-demo</groupId>
            <artifactId>modelB</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

</project>

这里的关系是,modelA、modelB、modelC 都是demo的子模块,C依赖B和A。

Maven依赖冲突

B和C都引入了fastjson依赖,但是他们的版本并不一致,这就产生冲突了。看图

Maven依赖冲突

两种解决方案

排除其中一个依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>demo</artifactId>
        <groupId>top.oneyoung.maven-conflict-demo</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>modelC</artifactId>

    <dependencies>
        <dependency>
            <groupId>top.oneyoung.maven-conflict-demo</groupId>
            <artifactId>modelA</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>com.alibaba</groupId>
                    <artifactId>fastjson</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>top.oneyoung.maven-conflict-demo</groupId>
            <artifactId>modelB</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

C依赖A的模块排除了fastjson依赖,所以现在只存在B中依赖的fastjson了。

这种方式一般适用于,引用别人的包,因为你改不了别人依赖的版本,只能进行手动排除,使用自己的版本。

统一版本

如果是你自己能控制引入的依赖,建议进行依赖统一,我们这里示例的A、B、C三个模块都有一个父模块,所以我我们可以将这种常用的依赖进行父模块管理,dependencyManagement

demo xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <packaging>pom</packaging>
    <modules>
        <module>modelA</module>
        <module>modelB</module>
        <module>modelC</module>
    </modules>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>top.oneyoung.maven-conflict-demo</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>demo</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>top.oneyoung.maven-conflict-demo</groupId>
                <artifactId>modelA</artifactId>
                <version>${project.version}</version>
            </dependency>
            <dependency>
                <groupId>top.oneyoung.maven-conflict-demo</groupId>
                <artifactId>modelB</artifactId>
                <version>${project.version}</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.78</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

</project>

在进行父模块版本管理后,子模块再引用改依赖就不用指定版本了,默认使用依赖管理的版本。spring boot parent就是如此的。

modelA

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>demo</artifactId>
        <groupId>top.oneyoung.maven-conflict-demo</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>modelA</artifactId>
    
    <dependencies>
       <!--不用指定版本-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
        </dependency>
    </dependencies>

</project>

工具推荐

如果你在使用IDEA进行开发,不妨安装下这个插件 Maven Helper

点击安装:https://plugins.jetbrains.com/plugin/7179-maven-helper

Maven依赖冲突

上一篇:目录设计以及树的遍历


下一篇:《Python数据可视化编程实战》——5.4 在matplotlib中创建动画