【Maven】Maven 基础教程(五): jar 包冲突问题

news2024/10/24 9:29:03

Maven 基础教程》系列,包含以下 5 篇文章:

  • Maven 基础教程(一):基础介绍、开发环境配置
  • Maven 基础教程(二):Maven 的使用
  • Maven 基础教程(三):build、profile
  • Maven 基础教程(四):搭建 Maven 私服 Nexus
  • Maven 基础教程(五): jar 包冲突问题

😊 如果您觉得这篇文章有用 ✔️ 的话,请给博主一个一键三连 🚀🚀🚀 吧 (点赞 🧡、关注 💛、收藏 💚)!!!您的支持 💖💖💖 将激励 🔥 博主输出更多优质内容!!!

Maven 基础教程(五): jar 包冲突问题

  • 8.jar 包冲突问题
    • 8.1 表现形式
      • 8.1.1 抛异常:找不到类
      • 8.1.2 抛异常:找不到方法
      • 8.1.3 没报错但结果不对
    • 8.2 本质
    • 8.3 解决办法
      • 8.3.1 IDEA 的 Maven Helper 插件
      • 8.3.2 Maven 的 enforcer 插件

8.jar 包冲突问题

编订依赖列表的程序员,初次设定一组依赖,因为尚未经过验证,所以确实有可能存在各种问题,需要做有针对性的调整。那么谁来做这件事呢?我们最不希望看到的就是:团队中每个程序员都需要自己去找依赖,即使是做同一个项目,每个模块也各加各的依赖,没有统一管理。那前人踩过的坑,后人还要再踩一遍。而且大家用的依赖有很多细节都不一样,版本更是五花八门,这就让事情变得更加复杂。

所以虽然初期需要根据项目开发和实际运行情况对依赖配置不断调整,最终确定一个各方面都 OK 的版本。但是一旦确定下来,放在父工程中做依赖管理,各个子模块各取所需,这样基本上就能很好的避免问题的扩散。

即使开发中遇到了新问题,也可以回到源头检查、调整 dependencyManagement 配置的列表,而不是每个模块都要改。

8.1 表现形式

由于实际开发时我们往往都会整合使用很多大型框架,所以一个项目中哪怕只是一个模块也会涉及到大量 jar 包。数以百计的 jar 包要彼此协调、精密配合才能保证程序正常运行。而规模如此庞大的 jar 包组合在一起难免会有磕磕碰碰。最关键的是由于 jar 包冲突所导致的问题非常诡异,这里我们只能罗列较为典型的问题,而没法保证穷举。

但是我们仍然能够指出一点:一般来说,由于我们自己编写代码、配置文件写错所导致的问题 通常能够在异常信息中看到我们自己类的全类名或配置文件的所在路径。如果整个错误信息中完全没有我们负责的部分,全部是框架、第三方工具包里面的类报错,这往往就是 jar 包的问题所引起的。

而具体的表现形式中,主要体现为 找不到类找不到方法

8.1.1 抛异常:找不到类

此时抛出的常见的异常类型:

  • java.lang.ClassNotFoundException:编译过程中找不到类。
  • java.lang.NoClassDefFoundError:运行过程中找不到类。
  • java.lang.LinkageError:不同类加载器分别加载的多个类有相同的全限定名。

我们来举个例子:

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.x.x</version>
</dependency>

httpclient 这个 jar 包中有一个类:org.apache.http.conn.ssl.NoopHostnameVerifier。这个类在较低版本中没有,但在较高版本存在。比如:

jar 包版本是否存在
4.3.6
4.4

那当我们确实需要用到 NoopHostnameVerifier 这个类,我们看到 Maven 通过依赖传递机制引入了这个 jar 包,所以没有明确地显式声明对这个 jar 包的依赖。可是 Maven 传递过来的 jar 包是 4.3.6 版本,里面没有包含我们需要的类,就会抛出异常。

冲突 体现在:4.3.64.4 这两个版本的 jar 包都被框架所依赖的 jar 包给传递进来了,但是假设 Maven 根据 版本仲裁 规则实际采纳的是 4.3.6

版本仲裁:Maven 的版本仲裁机制只是在没有人为干预的情况下,自主决定 jar 包版本的一个办法。而实际上我们要使用具体的哪一个版本,还要取决于项目中的实际情况。所以在项目正常运行的情况下,jar 包版本可以由 Maven 仲裁,不必我们操心;而发生冲突时 Maven 仲裁决定的版本无法满足要求,此时就应该由程序员明确指定 jar 包版本。

  • 最短路径优先:在下图的例子中,对模块 pro25-module-a 来说,Maven 会采纳 1.2.12 版本。

在这里插入图片描述

  • 路径相同时先声明者优先:此时 Maven 采纳哪个版本,取决于在 pro29-module-x 中,对 pro30-module-ypro31-module-z 两个模块的依赖哪一个先声明。

在这里插入图片描述

8.1.2 抛异常:找不到方法

程序找不到符合预期的方法。这种情况多见于通过反射调用方法,所以经常会导致:java.lang.NoSuchMethodError

8.1.3 没报错但结果不对

发生这种情况比较典型的原因是:两个 jar 包中的类分别实现了同一个接口,这本来是很正常的。但是问题在于:由于没有注意命名规范,两个不同实现类恰巧是同一个名字。
在这里插入图片描述

具体例子是实际工作中遇到过:项目中部分模块使用 log4j 打印日志;其它模块使用 logback,编译运行都不会冲突,但是会引起日志服务降级,让你的 log 配置文件失效。比如:你指定了 error 级别输出,但是冲突就会导致 infodebug 都在输出。

8.2 本质

以上表现形式归根到底是两种基本情况导致的:

  • 同一 jar 包的不同版本

在这里插入图片描述

  • 不同 jar 包中包含同名类

这里我们拿 netty 来举个例子,Netty 是一个类似 Tomcat 的 Servlet 容器。通常我们不会直接依赖它,所以基本上都是框架传递进来的。那么当我们用到的框架很多时,就会有不同的框架用不同的坐标导入 Netty。可以参照下表对比一下两组坐标:

截止到 3.2.10.Final 版本以前的坐标形式从 3.3.0.Final 版本开始以后的坐标形式
org.jboss.netty netty 3.2.10.Finalio.netty netty 3.9.2.Final

但是偏偏这两个『不同的包』里面又有很多『全限定名相同』的类。例如:

org.jboss.netty.channel.socket.ServerSocketChannelConfig.class
org.jboss.netty.channel.socket.nio.NioSocketChannelConfig.class
org.jboss.netty.util.internal.jzlib.Deflate.class
org.jboss.netty.handler.codec.serialization.ObjectDecoder.class
org.jboss.netty.util.internal.ConcurrentHashMap$HashIterator.class
org.jboss.netty.util.internal.jzlib.Tree.class
org.jboss.netty.util.internal.ConcurrentIdentityWeakKeyHashMap$Segment.class
org.jboss.netty.handler.logging.LoggingHandler.class
org.jboss.netty.channel.ChannelHandlerLifeCycleException.class
org.jboss.netty.util.internal.ConcurrentIdentityHashMap$ValueIterator.class
org.jboss.netty.util.internal.ConcurrentIdentityWeakKeyHashMap$Values.class
org.jboss.netty.util.internal.UnterminatableExecutor.class
org.jboss.netty.handler.codec.compression.ZlibDecoder.class
org.jboss.netty.handler.codec.rtsp.RtspHeaders$Values.class
org.jboss.netty.handler.codec.replay.ReplayError.class
org.jboss.netty.buffer.HeapChannelBufferFactory.class
......

8.3 解决办法

很多情况下常用框架之间的整合容易出现的冲突问题都有人总结过了,拿抛出的异常搜索一下基本上就可以直接找到对应的 jar 包。我们接下来要说的是通用方法。

不管具体使用的是什么工具,基本思路无非是这么两步:

  • 第一步:把彼此冲突的 jar 包找到。
  • 第二步:在冲突的 jar 包中选定一个。具体做法无非是通过 exclusions 排除依赖,或是明确声明依赖。

8.3.1 IDEA 的 Maven Helper 插件

这个插件是 IDEA 中安装的插件,不是 Maven 插件。它能够给我们罗列出来同一个 jar 包的不同版本,以及它们的来源。但是对不同 jar 包中同名的类没有办法。
在这里插入图片描述

然后基于 pom.xml 的依赖冲突分析,如下:
在这里插入图片描述
查看冲突分析结果:

在这里插入图片描述

8.3.2 Maven 的 enforcer 插件

使用 Maven 的 enforcer 插件既可以检测同一个 jar 包的不同版本,又可以检测不同 jar 包中同名的类。

这里我们引入两个对 Netty 的依赖,展示不同 jar 包中有同名类的情况作为例子。

<dependencies>

    <dependency>
        <groupId>org.jboss.netty</groupId>
        <artifactId>netty</artifactId>
        <version>3.2.10.Final</version>
    </dependency>

    <dependency>
        <groupId>io.netty</groupId>
        <artifactId>netty</artifactId>
        <version>3.9.2.Final</version>
    </dependency>
    
</dependencies>

然后配置 enforcer 插件:

<build>
    <pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-enforcer-plugin</artifactId>
                <version>1.4.1</version>
                <executions>
                    <execution>
                        <id>enforce-dependencies</id>
                        <phase>validate</phase>
                        <goals>
                            <goal>display-info</goal>
                            <goal>enforce</goal>
                        </goals>
                    </execution>
                </executions>
                <dependencies>
                    <dependency>
                        <groupId>org.codehaus.mojo</groupId>
                        <artifactId>extra-enforcer-rules</artifactId>
                        <version>1.0-beta-4</version>
                    </dependency>
                </dependencies>
                <configuration>
                    <rules>
                        <banDuplicateClasses>
                            <findAllDuplicates>true</findAllDuplicates>
                        </banDuplicateClasses>
                    </rules>
                </configuration>
            </plugin>
        </plugins>
    </pluginManagement>
</build>

执行如下 Maven 命令:

mvn clean package enforcer:enforce

部分运行结果:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1497966.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2575. 找出字符串的可整除数组(Go语言)

https://leetcode.cn/problems/find-the-divisibility-array-of-a-string/ 在看题解之前&#xff0c;我的代码是以下这样&#xff1a; package mainimport ("fmt" )func main() {fmt.Println(divisibilityArray("998244353", 3)) }func divisibilityArray…

数据备份:守护你的数字资产,安全无忧!

一、数据备份&#xff1a;数字时代的“保险箱” 在数字化日益盛行的今天&#xff0c;我们的工作、学习和生活都离不开各种电子设备。无论是电脑中的文档、图片&#xff0c;还是手机里的联系人、短信&#xff0c;都承载着我们的重要信息和回忆。然而&#xff0c;电子设备并非永…

基于C/S架构的在线阅读器

项目简介 本项目实现了用户的基本阅读功能。项目内容涉及到IO&#xff0c;网络编程&#xff0c;C&#xff0c;QT等知识点。本次项目服务器搭建在ubuntu上&#xff0c;客户端ui在QT中实现&#xff0c;客户端和服务器使用套接字通信。 一、基本功能展示 &#xff08;1&#xff…

关于制作一个Python小游戏(三)

目录 前言: 在前面我们已经了解过了关于制作pygame的使用和在里面游戏中的简单操作的内容了,今天我们主要讲的就是关于敌机的出现和如何去操控游戏中英雄飞机和敌机的出现 1.敌机的设计: 1.1敌机出场的实现: 1.1.1游戏启动后,每个一秒钟出现一架敌方飞机 1.1.2每架敌机向屏…

ETL与抖音数据同步,让数据流动无阻

在当今数字化时代&#xff0c;数据的价值日益凸显&#xff0c;企业需要从各种渠道获取有关用户行为、市场趋势和竞争对手活动的数据。作为一家专注于数据集成和转换的领先平台&#xff0c;ETLCloud为企业提供了强大的数据同步和转换功能。而与此同时&#xff0c;抖音作为一款热…

vcomp140.dll丢失如何修复,5种修复方法轻松搞定vcomp140.dll问题

vcomp140.dll文件的丢失可能会引发一系列系统运行与软件功能上的问题。具体来说&#xff0c;这个动态链接库文件是Visual C Redistributable的一部分&#xff0c;对于许多基于此环境开发的应用程序至关重要。一旦缺失&#xff0c;可能会导致部分应用程序无法正常启动或运行&…

深入浅出(二)MVVM

MVVM 1. 简介2. 示例 1. 简介 2. 示例 示例下载地址&#xff1a;https://download.csdn.net/download/qq_43572400/88925141 创建C# WPF应用(.NET Framework)工程&#xff0c;WpfApp1 添加程序集 GalaSoft.MvvmLight 创建ViewModel文件夹&#xff0c;并创建MainWindowV…

SAP Parallel Accounting(平行分类账业务)配置及操作手册(超详细的说明和测试)

SAP Parallel Accounting(平行分类账业务)配置及操作手册 1、Overview 为了适应不同的会计准则&#xff0c;SAP在新总账中启用了多分类账&#xff0c;&#xff08;其作用简单来说就是&#xff0c;同时一笔记账&#xff0c;会产生多个账套的凭证。&#xff09;分类账可以对应一…

一文掌握:电力管理系统该的功能和界面设计

一、什么电力管理系统 电力管理系统是一个用于监控、控制和优化电力系统运行的软件系统。它集成了实时数据采集、数据分析、决策支持和远程控制等功能&#xff0c;旨在提高电力系统的运行效率、可靠性和安全性。 电力管理系统是一个集成了数据采集、监控、分析和控制等功能的软…

应用层协议--HTTP

目录 一.HTTP是什么&#xff1f; 二.HTTP的请求和响应 a.请求&#xff1a; b.响应&#xff1a; 三.URL 四.Header 1.Host 2. Content-Length 3. Content-Type a. 请求 b. 响应 4. Referer 5. User-Agent 6. Cookie 一.HTTP是什么&#xff1f; HTTP是一种应用层协议&#xff0c…

Java并发编程-实现多线程的四种方式

创建线程的四种方式 创建线程的四种方式包括使用继承 Thread 类、实现 Runnable 接口、使用 Callable 和 Future 接口以及利用线程池。每种方式都有其特定的优势和适用场景。通过继承 Thread 类或实现 Runnable 接口&#xff0c;可以定义线程要执行的任务&#xff0c;并通过调用…

图形系统开发实战课程:进阶篇(上)——10.应用实例:交通路网

图形开发学院&#xff5c;GraphAnyWhere 课程名称&#xff1a;图形系统开发实战课程&#xff1a;进阶篇(上)课程章节&#xff1a;“图形样式”原文地址&#xff1a;https://www.graphanywhere.com/graph/advanced/2-10.html 第十章 应用实例&#xff1a;交通路网 \quad 在前面几…

Spring学习 基础(一)

Spring基础 IoC容器&#xff08;Inversion of Control&#xff09;: Spring 的核心是其控制反转&#xff08;IoC&#xff09;容器&#xff0c;它负责管理对象的生命周期和相互之间的依赖关系。通过依赖注入&#xff08;Dependency Injection&#xff09;&#xff0c;Spring能够…

python 基础知识点(蓝桥杯python科目个人复习计划59)

今日复习内容&#xff1a;做题 例题1&#xff1a;建造房屋 问题描述&#xff1a; 小蓝和小桥是两位年轻的建筑师&#xff0c;他们正在设计一座新的城市。 在这个城市中&#xff0c;有N条街道&#xff0c;每条街道上有M个位置可以建造房屋&#xff08;一个位置只能建造一个房…

【java】后序遍历二叉树

采用递归方式实现 节点类 public class Node {private int value;//父节点private Node fNode;//左节点private Node left;//右节点private Node right;//是否已经打印过private boolean sign false;public Node() {}public boolean isSign() {return sign;}public void setS…

融资项目——OpenFeign的降级与熔断

当一个微服务调用其他微服务时&#xff0c;如果被调用的微服务因各种原因无法在规定时间内提供服务&#xff0c;则可以直接使用本地的服务作为备选&#xff0c;即进行降级熔断。 如之前所提到的微服务为例&#xff1a; 如果希望实现降级熔断&#xff0c;可以在本地创建一个实现…

【Spring云原生系列】Spring Cloud Stream:消息驱动架构(MDA)解析,实现异步处理与解耦合!

&#x1f389;&#x1f389;欢迎光临&#xff0c;终于等到你啦&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;持续更新的专栏《Spring 狂野之旅&#xff1a;从入门到入魔》 &a…

HYBBS 表白墙网站PHP程序源码

安装教程 上传程序安装&#xff0c;然后设置账号密码&#xff0c;登陆后台切换模板手机PC都要换开启插件访问前台。 安装完成后如果不能正常访问就删除install安装文件夹 安装完成后右上角登录后点击头图进入后台 找到插件 安装表白墙配置插件 找到模板 将表白墙模板同时设…

18个惊艳的可视化大屏(第19辑):工业制造、智能工厂

实时监控和数据展示 可视化大屏可以集成和展示各种传感器、设备和系统的实时数据。通过将数据可视化展示在大屏上&#xff0c;工厂管理人员可以实时监控生产线的状态、设备的运行情况、生产效率等重要指标。这有助于及时发现问题、做出决策&#xff0c;并提高生产效率和质量。…

多模态入门

VIT处理图像 CNN VS Transformer 多模态BLIP模型 网络结构 视觉编码器: 就是 ViT 的架构。将输入图像分割成一个个的 Patch 并将它们编码为一系列 Image Embedding,并使用额外的 [CLS] token 来表示全局的图像特征。视觉编码器不采用之前的基于目标检测器的形式,因为 ViLT 和…