如何解决Maven依赖包冲突

涉及到的项目:find-usage、shaded-dependency

项目之间关系:find-usage依赖shaded-dependency


项目依赖多的时候有可能会遇到NoSuchMethodErrorClassNotFoundException这两个错误,一般是因为项目依赖的版本发生了冲突导致的。

使用spring-beans举个例子,在find-usage项目中单独依赖spring-beans时依赖树如下

依赖配置:
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.3.10</version>
        </dependency>
    </dependencies>

依赖树(mvn dependency:tree ):

[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ find-usage ---
[INFO] org.tiger:find-usage:jar:1.0-SNAPSHOT
[INFO] \- org.springframework:spring-beans:jar:5.3.10:compile
[INFO]    \- org.springframework:spring-core:jar:5.3.10:compile
[INFO]       \- org.springframework:spring-jcl:jar:5.3.10:compile
[INFO] ------------------------------------------------------------------------

当我在find-usage项目的pom.xml显示依赖spring-core时,依赖树如下

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>5.3.10</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>4.1.4.RELEASE</version>
</dependency>

[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ find-usage ---
[INFO] org.tiger:find-usage:jar:1.0-SNAPSHOT
[INFO] +- org.springframework:spring-beans:jar:5.3.10:compile
[INFO] \- org.springframework:spring-core:jar:4.1.4.RELEASE:compile
[INFO]    \- commons-logging:commons-logging:jar:1.2:compile
[INFO] ------------------------------------------------------------------------
 

因为maven的依赖解析规则(1.依赖树中离项目最近的得到使用,也就是在树的同一级。2.如果两个依赖再同一级,那么声明在前的优先使用),会使spring-beans:5.3.10依赖的spring-core:5.3.10会被我显式依赖的spring-core:4.1.4.RELEASE覆盖掉,同样的spring-core:5.3.10依赖的spring-jcl:5.3.10也会被删除。

为了严谨,这里引用官网的Maven依赖解析规则描述

  • Dependency mediation - this determines what version of an artifact will be chosen when multiple versions are encountered as dependencies. Maven picks the "nearest definition". That is, it uses the version of the closest dependency to your project in the tree of dependencies. You can always guarantee a version by declaring it explicitly in your project's POM. Note that if two dependency versions are at the same depth in the dependency tree, the first declaration wins.
    • "nearest definition" means that the version used will be the closest one to your project in the tree of dependencies. Consider this tree of dependencies:
        A
        ├── B
        │   └── C
        │       └── D 2.0
        └── E
            └── D 1.0

      In text, dependencies for A, B, and C are defined as A -> B -> C -> D 2.0 and A -> E -> D 1.0, then D 1.0 will be used when building A because the path from A to D through E is shorter. You could explicitly add a dependency to D 2.0 in A to force the use of D 2.0, as shown here:

        A
        ├── B
        │   └── C
        │       └── D 2.0
        ├── E
        │   └── D 1.0
        │
        └── D 2.0      
  • Dependency management - this allows project authors to directly specify the versions of artifacts to be used when they are encountered in transitive dependencies or in dependencies where no version has been specified. In the example in the preceding section a dependency was directly added to A even though it is not directly used by A. Instead, A can include D as a dependency in its dependencyManagement section and directly control which version of D is used when, or if, it is ever referenced.
  • Dependency scope - this allows you to only include dependencies appropriate for the current stage of the build. This is described in more detail below.
  • Excluded dependencies - If project X depends on project Y, and project Y depends on project Z, the owner of project X can explicitly exclude project Z as a dependency, using the "exclusion" element.
  • Optional dependencies - If project Y depends on project Z, the owner of project Y can mark project Z as an optional dependency, using the "optional" element. When project X depends on project Y, X will depend only on Y and not on Y's optional dependency Z. The owner of project X may then explicitly add a dependency on Z, at her option. (It may be helpful to think of optional dependencies as "excluded by default.")

怎么样才能既保留spring-core:4.1.4.RELEASE又保留spring-core:jar:5.3.10以及它的相关依赖呢?

目前大致有两种方式:

  1. 通过自定义类加载器,通过loadclass方法改写双亲委托机制,然后用不同的类加载器去加载不同版本的spring-core,当然使用spring-core的类也要被同一个加载器加载,通过maven-dependency-plugin插件去下载被覆盖的依赖(比如spring-core:jar:5.3.10以及相关依赖)。如下在编译阶段把依赖下载到lib文件夹下。(有兴趣留言,单独写篇文章讨论)
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-dependency-plugin</artifactId>
                    <version>2.8</version>
                    <executions>
                        <execution>
                            <phase>compile</phase>
                            <goals>
                                <goal>copy</goal>
                            </goals>
                            <configuration>
                                <artifactItems>
                                    <artifactItem>
                                        <groupId>org.springframework</groupId>
                                        <artifactId>spring-core</artifactId>
                                        <version>5.3.10</version>
                                        <outputDirectory>${class.path}/lib</outputDirectory>
                                    </artifactItem>
                                </artifactItems>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>

  2. 使用maven-shade-plugin插件修改依赖包的包名称,通过包名解决冲突。

类加载器方法费时费力,这里不过多介绍,主要介绍下maven-shade-plugin的解决办法。

建立shaded-dependency项目,pom.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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.tiger</groupId>
    <artifactId>shaded-dependency</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.3.10</version>
        </dependency>
    </dependencies>


    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.2.0</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <!--                        <id>shade-spring-core</id>-->
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <artifactSet>
                                <includes>
                                    <include>*:*</include>
                                </includes>
                            </artifactSet>
                            <createSourcesJar>true</createSourcesJar>
                            <relocations>
                                <relocation>
                                    <pattern>org.springframework</pattern>
                                    <shadedPattern>shaded.org.springframework</shadedPattern>
                                </relocation>
                            </relocations>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

打包效果:

shade把core还有它的依赖解压成class文件然后再统一打包到一个jar中(uber jar)。

然后在find-usage项目中加入对shaded-dependency的依赖:

    <dependencies>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.3.10</version>
        </dependency>

        <dependency>
            <groupId>org.tiger</groupId>
            <artifactId>shaded-dependency</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

    </dependencies>

依赖树:

[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ find-usage ---
[INFO] org.tiger:find-usage:jar:1.0-SNAPSHOT
[INFO] +- org.springframework:spring-beans:jar:5.3.10:compile
[INFO] |  \- org.springframework:spring-core:jar:5.3.10:compile
[INFO] |     \- org.springframework:spring-jcl:jar:5.3.10:compile
[INFO] \- org.tiger:shaded-dependency:jar:1.0-SNAPSHOT:compile
[INFO] ------------------------------------------------------------------------

 使用效果:

具体使用哪个自己决定吧! 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值