利用FatJar彻底解决Jar包冲突(一)
利用FatJar彻底解决Jar包冲突
- 序
- FatJar的加载与隔离
- ⼀、 FatJar概念
- ⼆、FatJar的加载
- 三、FatJar的隔离
- 四、隔离机制验证
- 五、 FatJar的定位
- 六、 打包注意点
序
今天整理旧电脑里的资料,偶然翻到大概10年前实习时写的笔记,之前经常遇到Java依赖冲突的问题,想通过这种方式彻底解决Java里的依赖冲突,现在回过头来看,不知道是不是有点幼稚,欢迎交流,请轻喷。
FatJar的加载与隔离
⼀、 FatJar概念
将⼀个jar和他所依赖的jar都打在⼀个包中,这个包即为FatJar。
如何打FatJar
-
使⽤ maven shade 插件
使⽤ maven shade 插件解压依赖的jar并和原⼯程class混在⼀起打包成⼀个jar。 优点:打包⽅式简单,之后加载也较容易。 缺点:⽬录多且乱,对于jar包中的配置⽂件不利于定位;对于内部依赖冲突这种打包⽅式会⾃动排除冲突,覆盖class⽂ 件,不利于排查jar包本身内部的冲突,如下图。
建议:如果能够忍受这么乱的⽬录可以使⽤这种⽅式,因为加载class的时候很⽅便。

-
使⽤ SpringBoot 提供的打包插件
由于SpringBoot打出来的jar可以直接启动,这个jar就是FatJar,所以可以使⽤ Spring Boot 提供的打包插件将依赖的jar直接 打进FatJar,如下图。
优点:通⽤,spring boot,pandora boot都是基于这种⽅式,很多问题都有现成的解决⽅案⽐如之后遇到的autoconfig注⼊问题,autoconfig 提供了针对fatjar注⼊的插件。⽬录⼲净明了,如下图。
缺点:由于原⽣jar的加载只⽀持⼀层加载,即⽆法加载 jarin jar ⾥的class,所以这个问题需要调研,但是既然使⽤的是 Spring Boot的打包⽅式,Spring Boot本身肯定已经解决了这个问题。
考虑到通⽤性与“美感”,下⾯针对这种打包⽅式来解决相应的问题。

⼆、FatJar的加载
fatjar与普通jar的区别就是它将依赖的jar也打进了jar包⾥的lib⽬录下,所以需要加载jarin jar。对于Jar⾥的资源,定义 以‘!/ʼ来分割。原始的JarFile URL只⽀持⼀个‘!/ʼ,如图。

通过阅读Spring Boot启动加载相关的源码,发现Spring Boot扩展了这个协议,让它⽀持多个‘!/ʼ,从⽽就可以表示jarin jar的 资源了,如图。

既然这样,我们就可以复⽤Spring Boot 的 加载⽅式,即通过继承spring boot的launcher,复⽤它的createClassLoader来构造LaunchedURLClassLoader。

三、FatJar的隔离
解决了加载的问题之后,就需要研究如何与依赖FatJar的应⽤(以下简称为“应⽤”)隔离了,因为我们最终的⽬标就是针对 应⽤和jar⾥同类名(全路径)的两个class都能够加载且不冲突。根据java classloader的委托加载机制,我们可以确定我们 的FatJarClassLoader所处的位置如下:

应⽤的classloader使⽤的是AppClassLoader,需要创建⼀个与它同级的ClassLoader即FatJarClassLoader,通过设置 LaunchedClassLoader的parent为AppClassLoader的parent即ExtensionClassLoader就可以了,这样两个ClassLoader就可以 像上图那样隔离。

四、隔离机制验证
FatJarClassLoader 构造完成之后,我们就可以验证⼀下到底能不能解决冲突了。
- 原理层⾯验证
验证代码如下:

输出结果如下:

可以看到 FatJarClassLoader 的 parent 为 ExtensionClassLoader,所以与AppClassLoader是同级的。String的ClassLoader为 空是由于BootStrapClassLoader是通过C++编写的,是最“底”层的ClassLoader。 - 实例验证-构造冲突
在⼆⽅包中添加⽇志依赖如下:

该版本的Logger是有trace⽅法的,我们在⼆⽅包中可以直接调⽤进⾏确认:

将⼆⽅包打成普通的jar包,在应⽤中依赖该jar包(conflict-b),同时也依赖⼀个低版本的⽇志包:

在应⽤中调⽤⼆⽅包中的某个⽅法,该⽅法内部会调⽤Logger的trace⽅法:

报错如下:

可⻅构造冲突已经成功了! - 实例验证-利⽤FatJar解决冲突
将⼆⽅包打成FatJar之后在应⽤中通过反射调⽤原来会冲突的代码,调⽤成功:


可⻅,⼆⽅包是通过隔离的⽅式加载的,加载的是⼆⽅包⾃带的⾼版本的Logger;⽽应⽤本身加载的还是⽼版本的 Logger。
五、 FatJar的定位
由于是在运⾏期加载FatJar,所以需要⼿动定位FatJar的位置,这边普通Java程序和Java Web 定位⽅式不同,⽬前只是同时 使⽤这两种⽅式进⾏加载,还没有其他更通⽤的解决⽅案。 当依赖⽅是普通 Java 程序时,所有依赖的jar包路径可以通过如下代码获得:

⽽当依赖⽅是Java Web 应⽤时,根据Tomcat 中应⽤的⽬录结构:

得到 jar 包路径的步骤为:
1. AppClassLoader.getResource(“/”).getPath()
2. 上溯并进⼊lib⽬录即可
六、 打包注意点
使⽤Spring Boot插件打出的FatJar是包含pom.xml 的,如下图:

可⻅pom⽂件位置和普通jar包的位置是⼀致的,这样的话应⽤maven其实是可以识别到FatJar的pom⽂件的,这样会将 FatJar依赖的jar包加⼊到应⽤⾥,还是需要⼿动排除冲突,这就跟我们的初衷不⼀致了。所以需要将pom⽂件在打包时排 除,如下代码:

相关文章:
利用FatJar彻底解决Jar包冲突(一)
利用FatJar彻底解决Jar包冲突 序FatJar的加载与隔离⼀、 FatJar概念⼆、FatJar的加载三、FatJar的隔离四、隔离机制验证五、 FatJar的定位六、 打包注意点 序 今天整理旧电脑里的资料,偶然翻到大概10年前实习时写的笔记,之前经常遇到Java依赖冲突的问题…...
Spring MVC笔记
01 什么是Spring MVC Spring MVC 是 Spring 框架中的一个核心模块,专门用于构建 Web 应用程序。它基于经典的 MVC 设计模式(Model-View-Controller),但通过 Spring 的特性(如依赖注入、注解驱动)大幅简化了…...
BurpSuite插件jsEncrypter使用教程
一、前言 在当今Web应用安全测试中,前端加密已成为开发者保护敏感数据的常用手段。然而,这也给安全测试人员带来了挑战,传统的抓包方式难以获取明文数据,测试效率大打折扣。BurpSuite作为一款强大的Web安全测试工具,其…...
【C#实现手写Ollama服务交互,实现本地模型对话】
前言 C#手写Ollama服务交互,实现本地模型对话 最近使用C#调用OllamaSharpe库实现Ollama本地对话,然后思考着能否自己实现这个功能。经过一番查找,和查看OllamaSharpe源码发现确实可以。其实就是开启Ollama服务后,发送HTTP请求&a…...
Android Glide 框架线程管理模块原理的源码级别深入分析
一、引言 在现代的 Android 应用开发中,图片加载是一个常见且重要的功能。Glide 作为一款广泛使用的图片加载框架,以其高效、灵活和易用的特点受到了开发者的青睐。其中,线程管理模块是 Glide 框架中至关重要的一部分,它负责协调…...
每天记录一道Java面试题---day32
MySQL索引的数据结构、各自优劣 回答重点 B树:是一个平衡的多叉树,从根节点到每个叶子节点的高度差不超过1,而且同层级的节点间有指针相互连接。在B树上的常规检索,从根节点到叶子节点的搜索效率基本相当,不会出现大…...
Vue3 Pinia 符合直觉的Vue.js状态管理库
Pinia 符合直觉的Vue.js状态管理库 什么时候使用Pinia 当两个关系非常远的组件,要传递参数时使用Pinia组件的公共参数使用Pinia...
深度学习与大模型基础-向量
大家好!今天我们来聊聊向量(Vector)。别被这个词吓到,其实向量在我们的生活中无处不在,只是我们没注意罢了。 1. 向量是什么? 简单来说,向量就是有大小和方向的量。比如你从家走到学校&#x…...
【网络编程】完成端口 IOCP
10.11 完成端口 10.11.1 基本概念 完成端口的全称是I/O 完成端口,英文为IOCP(I/O Completion Port) 。IOCP是一个异 步I/O 的 API, 可以高效地将I/O 事件通知给应用程序。与使用select() 或是其他异步方法不同 的是,一个套接字与一个完成端口关联了起来…...
《苍穹外卖》SpringBoot后端开发项目重点知识整理(DAY1 to DAY3)
目录 一、在本地部署并启动Nginx服务1. 解压Nginx压缩包2. 启动Nginx服务3. 验证Nginx是否启动成功: 二、导入接口文档1. 黑马程序员提供的YApi平台2. YApi Pro平台3. 推荐工具:Apifox 三、Swagger1. 常用注解1.1 Api与ApiModel1.2 ApiModelProperty与Ap…...
管理网络安全
防火墙在 Linux 系统安全中有哪些重要的作用? 防火墙作为网络安全的第一道防线,能够根据预设的规则,对进出系统的网络流量进行严格筛选。它可以阻止未经授权的外部访问,只允许符合规则的流量进入系统,从而保护系统免受…...
明日直播|Go IoT 开发平台,开启万物智联新征程
在物联网技术飞速发展的当下,物联网行业却深受协议碎片化、生态封闭、开发低效等难题的困扰。企业和开发者们渴望找到一个能突破这些困境,实现高效、便捷开发的有力工具。 3 月 11 日(星期二)19:00,GitCode 特别邀请独…...
系统架构设计师—系统架构设计篇—软件架构风格
文章目录 概述经典体系结构风格数据流风格批处理管道过滤器对比 调用/返回风格主程序/子程序面向对象架构风格层次架构风格 独立构件风格进程通信事件驱动的系统 虚拟机风格解释器基于规则的系统 仓库风格(数据共享风格)数据库系统黑板系统超文本系统 闭…...
【MySQL_04】数据库基本操作(用户管理--配置文件--远程连接--数据库信息查看、创建、删除)
文章目录 一、MySQL 用户管理1.1 用户管理1.11 mysql.user表详解1.12 添加用户1.13 修改用户权限1.14 删除用户1.15 密码问题 二、MySQL 配置文件2.1 配置文件位置2.2 配置文件结构2.3 常用配置参数 三、MySQL远程连接四、数据库的查看、创建、删除4.1 查看数据库4.2 创建、删除…...
【Zinx】Day5-Part4:Zinx 的连接属性设置
目录 Day5-Part4:Zinx 的连接属性设置给连接添加连接配置的接口连接属性方法的实现 测试 Zinx-v1.0总结 Day5-Part4:Zinx 的连接属性设置 在 Zinx 当中,我们使用 Server 来开启服务并监听指定的端口,当接收到来自客户端的连接请求…...
【英伟达AI论文】多模态大型语言模型的高效长视频理解
摘要:近年来,基于视频的多模态大型语言模型(Video-LLMs)通过将视频处理为图像帧序列,显著提升了视频理解能力。然而,许多现有方法在视觉主干网络中独立处理各帧,缺乏显式的时序建模,…...
小程序事件系统 —— 32 事件系统 - 事件分类以及阻止事件冒泡
在微信小程序中,事件分为 冒泡事件 和 非冒泡事件 : 冒泡事件:当一个组件的事件被触发后,该事件会向父节点传递;(如果父节点中也绑定了一个事件,父节点事件也会被触发,也就是说子组…...
全球首款 5G-A 人形机器人发布
全球首款 5G-A 人形机器人于世界移动通信大会(MWC2025)上由中国移动、华为、乐聚联合发布。以下是关于这款机器人的详细介绍: 名称与背景 名称9:这款人形机器人名为 “夸父”,是中国移动、华为与乐聚机器人在 GTI 平台…...
Tomcat 新手入门指南
Tomcat 新手入门指南 Apache Tomcat 是一个开源的 Java Servlet 容器和 Web 服务器,广泛用于部署和运行 Java Web 应用程序。以下是 Tomcat 的入门指南,帮助你快速上手。 1. 安装 Tomcat 步骤 1: 下载 Tomcat 访问 Apache Tomcat 官网。选择适合的版…...
Flink-DataStreamAPI-生成水印
下面我们将学习Flink提供的用于处理事件时间戳和水印的API,也会介绍有关事件时间、流转时长和摄取时间,下面就让我们跟着官网来学习吧 一、水印策略介绍 为了处理事件时间,Flink需要知道事件时间戳,这意味着流中的每个元素都需要…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...
vue3 字体颜色设置的多种方式
在Vue 3中设置字体颜色可以通过多种方式实现,这取决于你是想在组件内部直接设置,还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法: 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...
Java入门学习详细版(一)
大家好,Java 学习是一个系统学习的过程,核心原则就是“理论 实践 坚持”,并且需循序渐进,不可过于着急,本篇文章推出的这份详细入门学习资料将带大家从零基础开始,逐步掌握 Java 的核心概念和编程技能。 …...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...
使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
以下是一个完整的 Angular 微前端示例,其中使用的是 Module Federation 和 npx-build-plus 实现了主应用(Shell)与子应用(Remote)的集成。 🛠️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...
管理学院权限管理系统开发总结
文章目录 🎓 管理学院权限管理系统开发总结 - 现代化Web应用实践之路📝 项目概述🏗️ 技术架构设计后端技术栈前端技术栈 💡 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 🗄️ 数据库设…...
MySQL 8.0 事务全面讲解
以下是一个结合两次回答的 MySQL 8.0 事务全面讲解,涵盖了事务的核心概念、操作示例、失败回滚、隔离级别、事务性 DDL 和 XA 事务等内容,并修正了查看隔离级别的命令。 MySQL 8.0 事务全面讲解 一、事务的核心概念(ACID) 事务是…...
Golang——6、指针和结构体
指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...
毫米波雷达基础理论(3D+4D)
3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文: 一文入门汽车毫米波雷达基本原理 :https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...
