【Maven】jar包冲突原因与最优解决方案

这篇具有很好参考价值的文章主要介绍了【Maven】jar包冲突原因与最优解决方案。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【Maven】jar包冲突原因与最优解决方案

前言

你是否经常遇到这样的报错:

java.lang.NoSuchMethodError
java.lang.ClassNotFoundException
java.lang.NoClassDefFoundError

以上报错就有可能是jar包冲突造成的,Maven中jar包冲突是开发过程中比较常见而又令人头疼的问题,我们需要知道 jar包冲突的原理,才能更好的去解决jar包冲突的问题。本文将从jar包冲突的原理和解决jar包冲突两个方面阐述Maven中jar包问题。

jar包冲突原因

当我们在maven项目中引入第三方组件时,三方组件中的依赖可能会与项目已有组件发生冲突。
比如三方组件中依赖httpclient的版本是4.5.x,而项目中已有的httpclient版本是3.1.x,那么此时就会产生一下两种情况:

如果用三方组件的高版本httpclient覆盖原有的低版本httpclient,有可能会导致原来项目启动运行失败。即使高版本兼容低版本,这样高风险的操作也是很危险的;

如果在三方maven依赖中对其对依赖的httpclient在引入时使用进行排除,使三方组件使用项目中的低版本httpclient,此时可能会因为版本不一致导致三方组件无法使用

在这样的情况下我们应当如何保证不影响项目原有依赖版本的情况下正常使用三方组件呢?此时可以考虑使用maven-shade-plugin插件,jar包冲突解决方案最后介绍。

依赖传递

首先我们需要了解jar包依赖的传递性。

关于依赖作用范围详解见:【Maven】属性scope依赖作用范围详解。

当我们需要A的依赖的时候,就会在pom.xml中引入A的jar包;而引入的A的jar包中可能又依赖B的jar包,这样Maven在解析pom.xml的时候,会依次将A、B 的jar包全部都引入进来。

举个例子:
在Spring Boot应用中导入Hystrix和原生Guava的jar包:

com.google.guava guava 20.0 org.springframework.cloud spring-cloud-starter-netflix-hystrix 1.4.4.RELEASE

利用Maven Helper插件得到项目导入的jar包依赖树:
【Maven】jar包冲突原因与最优解决方案
从图中可以看出Hystrix包含对Guava jar包依赖的引用: Hystrix -> Guava,所以在引入Hystrix的依赖的时候,会将Guava的依赖也引入进来。

冲突原因

假设有如下依赖关系:

A->B->C->D1(log 15.0):A中包含对B的依赖,B中包含对C的依赖,C中包含对D1的依赖,假设是D1是日志jar包,version为15.0

E->F->D2(log 16.0):E中包含对F的依赖,F包含对D2的依赖,假设是D2是同一个日志jar包,version为16.0

当pom.xml文件中引入A、E两个依赖后,根据Maven传递依赖的原则,D1、D2都会被引入,而D1、D2是同一个依赖D的不同版本。
当我们在调用D2中的method1()方法,而D1中是15.0版本(method1可能是D升级后增加的方法),可能没有这个方法,这样JVM在加载A中D1依赖的时候,找不到method1方法,就会报NoSuchMethodError的错误,此时就产生了jar包冲突。

注:
如果在调用method2()方法的时候,D1、D2都含有这个方法(且升级的版本D2没有改动这个方法,这样即使D有多个版本,也不会产生版本冲突的问题。)

举个例子:
【Maven】jar包冲突原因与最优解决方案
利用Maven Helper插件分析得出:Guava这个依赖包产生冲突。
我们之前导入了Guava的原生jar包,版本号是20.0;而现在提示Guava产生冲突,且冲突发生位置是Hystrix所在的jar包,所以可以猜测Hystrix中包含了对Guava不同版本的jar包的引用。

为了验证我们的猜想,使用Maven Helper插件打印出Hystrix依赖的jar tree:
【Maven】jar包冲突原因与最优解决方案
可以看到:Hystrix jar中所依赖的Guava jar包是15.0版本的,而我们之前在pom.xml中引入的原生Guava jar包是20.0版本的,这样Guava就有15.0 与20.0这两个版本,因此发生了jar包冲突。

jar包冲突解决方案

Maven 解析 pom.xml 文件时,同一个 jar 包只会保留一个,那么面对多个版本的jar包,需要怎么解决呢?

Maven默认处理策略

最短路径优先

Maven 面对 D1 和 D2 时,会默认选择最短路径的那个 jar 包,
即 D2。E->F->D2 比 A->B->C->D1 路径短1。

最先声明优先

如果路径一样的话,如: A->B->C1, E->F->C2 ,两个依赖路径长度都是 2,那么就选择最先声明。

排除依赖

移除依赖:用于排除某项依赖的依赖jar包

1.我们可以借助Maven Helper插件中的Dependency Analyzer分析冲突的jar包,然后在对应标红版本的jar包上面点击execlude,就可以将该jar包排除出去。
【Maven】jar包冲突原因与最优解决方案
再刷新以后冲突就会消失。

2.手动排除

手动在pom.xml中使用<exclusion>标签去排除冲突的jar包(上面利用插件Maven Helper中的execlude方法其实等同于方法1):

<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>

mvn分析包冲突命令:

mvn dependency:tree

版本锁定

版本锁定原则:一般用在继承项目的父项目中
正常项目都是多模块的项目,如moduleA和moduleB共同依赖X这个依赖的话,那么可以将X抽取出来,同时设置其版本号,这样X依赖在升级的时候,不需要分别对moduleA和moduleB模块中的依赖X进行升级,避免太多地方(moduleC、moduleD…)引用X依赖的时候忘记升级造成jar包冲突,这也是实际项目开发中比较常见的方法。

首先定义一个父pom.xml,将公共依赖放在该pom.xml中进行声明:

<properties>
    <spring.version>spring4.2.4</spring.version>
<properties>

<dependencyManagement>
    <dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-beans</artifactId>
			<version>${spring.versio}</version>
		</dependency>
	</dependencies>
</dependencyManagement>

这样如moduleA和moduleB在引用Spring-beans jar包的时候,直接使用父pom.xml中定义的公共依赖就可以:
moduleA在其pom.xml使用spring-bean的jar包(不用再定义版本):

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

moduleB在其pom.xml使用spring-bean的jar包如上类似:

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

maven-shade-plugin插件

作用是将依赖的包在package阶段一起打入jar包中,以及对依赖的jar包进行重命名从而达到隔离的作用。这里为了解决上面的问题我们主要使用第二个功能特性,使得相同依赖不同版本达到共存的目的。

1.环境准备
这里用fastjson来模拟使用maven-shade-plugin解决项目中不同版本共存问题。原项目此时使用的是1.1.15版本的fastjson

<!-- 原项目 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.1.15</version>
</dependency>

假引入一个三方依赖,该依赖使用1.2.75版本的fastjson
```xml
<!-- 将引入依赖 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.75</version>
</dependency>

2.解决方案
搭建一个新的模块rename-dependencies,专门用于存放1.2.75依赖。在pom文件中添加1.2.75的依赖,然后添加maven-shade-plugin插件。rename-dependencies的pom如下

<?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">
    <groupId>com.sk</groupId>
    <artifactId>rename-dependencies</artifactId>
    <version>1.0-SNAPSHOT</version>
    <modelVersion>4.0.0</modelVersion>

    <dependencies>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.75</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.2.4</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <filters>
                                <filter>
                                    <artifact>*:*</artifact>
                                    <excludes>
                                        <exclude>META-INF/*.SF</exclude>
                                        <exclude>META-INF/*.DSA</exclude>
                                        <exclude>META-INF/*.RSA</exclude>
                                    </excludes>
                                </filter>
                            </filters>
                            <relocations>
                                <relocation>
                                    <pattern>com.alibaba</pattern>
                                    <shadedPattern>shade.com.alibaba</shadedPattern>
                                </relocation>
                            </relocations>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

从配置文件中可以看到,由于maven-shade-plugin插件在解决这个问题上其实是通过对依赖进行重命名而达到隔离的目的,所以配置主要是集中在relocations中。这里将以com.alibaba开头的包全部重命名为以shade.com.alibaba开头。

3.引入依赖
将rename-dependencies进行打包,打包好之后在原项目中引入rename-dependencies的依赖。此时在引入rename-dependencies之后,可以在项目下看到该依赖中的fastjson包名发生了变化
【Maven】jar包冲突原因与最优解决方案

此时在代码中调用fastjson相关方法,会提示选择所需要包,如下图,此时问题解决,两个版本的fastjson可同时使用已经兼容。

【Maven】jar包冲突原因与最优解决方案

一些需要注意的坑

描述: 引入依赖找不到重命名的shade包

原因:重命名的模块和需要引入依赖的模块在一个项目中,idea优先找本项目,所以没有走仓库

解决方案:
将模块从项目maven中移除,右键项目-maven-unlink maven projects
新建一个项目专门来做依赖

总结

本文从jar包冲突的原理和解决jar包冲突两个方面阐述Maven引入jar包依赖的问题;

其中在解决方案选择方面:

如果Maven不能根据默认处理策略解决掉,就需要从移除依赖或者升级现有依赖处理;

但是升级现有依赖风险比较大,有时会对原项目不兼容的代码进行大量修改,就比如有次项目引入封装的工作流组件时,其中组件内使用的mybatis-plus为3.5.1版本,原项目使用的3.3.1,先是对组件排除掉mybatis-plus,但是后面发现mybatis-plus中有一个反射工具类无法使用,只有高版本才能使用,于是就对原项目做依赖升级,但是需要对原项目不兼容的代码进行大量修改,比如mybatisconfig类和大量接口返回类型修改;

最优选择:maven-shade-plugin 保证不影响项目原有依赖版本的情况下正常使用三方组件。

参考:

https://blog.csdn.net/qq_38550836/article/details/111567355

https://blog.csdn.net/noaman_wgs/article/details/81137893文章来源地址https://www.toymoban.com/news/detail-421876.html

到了这里,关于【Maven】jar包冲突原因与最优解决方案的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Maven第八章:如何解决Maven的jar版本冲突

    前言 本文重点讲解Maven依赖冲突原因,maven依赖原则以及如何利用idea Maven Helper插件分析解决问题。 背景 开发过程中引入第三方jar遇到依赖冲突的,非常影响开发,甚至大部分时间都在调试版本兼容。

    2024年02月06日
    浏览(41)
  • maven依赖jar包时版本冲突的解决

    在pom.xml配置文件中,如果有两个名称相同版本不同的依赖声明,那么先写的会生效。 直接依赖优先于传递依赖,如果传递依赖的jar包版本冲突了,那么可以自己声明一个指定版本的依赖jar,即可解决冲突。 传递依赖冲突时,可以在不需要的jar的传递依赖中声明排除,从而解

    2024年02月03日
    浏览(63)
  • Maven解决jar包版本冲突的4种方法

      先解释下maven的依赖传递:a jar包引入了b jar包,如果项目中引入了a jar包,其实也会把a依赖的b jar包引入。那现在有a、c这2个jar包,a jar包依赖的是1.0.0版本的b jar包,c jar包也依赖了b jar包,版本是2.0.0;如果项目中引入了a、c jar包,那b jar包到底引入哪个版本呢,是1.0.0还

    2024年02月15日
    浏览(37)
  • java-IDEA MAVEN查看依赖树,解决jar包重复和冲突

       如果这里面的依赖关系有红线,就说明有包冲突,一般都是版本不一致,可以在idea里下一个插件 Maven Helper, 点击install并重启IDEA  打开pom.xml文件,在下方会出现Dependency Analyzer,选择它会出现重复依赖列表,选择对应的依赖,右键红色部分选择Exclude,然后选择上面的reimport就可

    2024年02月13日
    浏览(51)
  • 【多线程】| 线程冲突解决方案

    同一进程内的线程是共享同一内存空间的,所以在多个线程的进程里,线程是可以同时操作这个进程空间的数据的,这样就容易造成线程冲突的情况。 举个小李子:一个房子里(代表一个进程),只有一个厕所(代表一个资源)。屋子里面有两个人A和B(代表两个线程),共

    2024年02月05日
    浏览(46)
  • Elasticsearch并发写入版本冲突解决方案

    搜索公众号, AmCoder 干货及时送达👇  众所周知,es经常被用于存储日志数据,其中在某些场景下,日志产生的时机不同,并且需要将多类具备关联关系的日志写入同一个document,就会带来同一个文档可能会被其它文档覆盖,或者missing等问题。 大家都知道es是不支持事务的,

    2023年04月19日
    浏览(51)
  • 宝塔面板+Nextcloud搭建教程——可能是目前最优解决方案

    个人/企业云盘项目,网上有许多种解决方案。既有像索鸟快传,Cloudreve等主要面向个人用户的项目,也不乏许多如Nextcloud, owncloud, 可道云等优秀的商用项目。 Nextcloud是一个极为优秀的个人/团体/商用网盘解决方案,自发布以来,已经经过了二十多个版本的更新迭代。其前身是

    2024年02月11日
    浏览(36)
  • EIP-6963: 多钱包冲突的解决方案

    最近提出的 EIP-6963 旨在提供一个解决方案,以解决当用户试图在一个单一的网络浏览器中使用多个钱包供应商时出现的冲突问题。在这种情况下,这些冲突的钱包会导致用户体验下降,阻碍用户对其以太坊界面的控制,并使与 dApp 互动的过程变得复杂。 原始提案文档地址:

    2024年03月10日
    浏览(49)
  • Git_常用命令+代码冲突解决方案

    –local: 配置对当前仓库有效 –global: 配置对当前用户(指的是当前用于登录系统的用户)的所有仓库有效 设置用户名及邮箱 注意:由于此处设置的用户名中间有空格所以要使用双引号,正常设置时无需使用双引号 设置仓库的认证方式 credentail.helper后的参数可选: cache: 在第

    2024年04月14日
    浏览(54)
  • git请求合并时出现冲突的解决方案

    请求合并时出现冲突,一般是有多人修改了同一个地方导致的,我们一般在本地解决好冲突后再上传到远端仓库,然后再次发起合并。 本流程适合无主分支权限的情况 在出现冲突的开发分支上解决流程: 1、先拉取主干分支 main到本地开发分支dev git pull origin main 2、借助工具

    2024年02月11日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包