当前位置: 首页 > news >正文

聊聊如何避免多个jar通过maven打包成一个jar,多个同名配置文件发生覆盖问题

前言

不知道大家在开发的过程中,有没有遇到这种场景,外部的项目想访问内部nexus私仓的jar,因为私仓不对外开放,导致外部的项目没法下载到私仓的jar,导致项目因缺少jar而无法运行。

通常遇到这种场景,常用的解法有,外部项目跟内部nexus的网络打通,比如通过VPN。或者将私仓的jar直接下载下来给到外部项目。对于第二种方案有时候因为私仓的jar里面有依赖其他的内部jar,导致要下载多个jar的情况。这时候为了方便,我们可能会将这些jar合并成一个大jar,再给出去。而目前有些jar都是一些starter,会有一些同名的配置文件,比如spring.factories。如果不进行处理,直接打包,就会出现同名配置文件覆盖的情况

本文就是要来聊聊当多个jar合并成一个jar,如何解决多个同名配置文件覆盖的情况

解决思路

通过maven-shade-plugin这个插件,利用插件的org.apache.maven.plugins.shade.resource.AppendingTransformer来处理处理多个jar包中存在重名的配置文件的合并。他的核心是在于合并多个同名配置文件内容,而非覆盖

示例配置如下

 <build><plugins><!-- 防止同名配置文件,在打包时被覆盖,用来处理多个jar包中存在重名的配置文件的合并参考dubbo:https://github.com/apache/dubbo/blob/master/dubbo-all/pom.xml--><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-shade-plugin</artifactId><executions><execution><phase>package</phase><goals><goal>shade</goal></goals><configuration><transformers><transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"><resource>META-INF/spring.factories</resource></transformer><transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"><resource>META-INF/spring.handlers</resource></transformer><transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"><resource>META-INF/spring.schemas</resource></transformer><transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"><resource>META-INF/spring.tooling</resource></transformer></transformers></configuration></execution></executions></plugin>

打包后的配置文件的效果如下图


眼尖的朋友应该发现了,同名的配置内容是通过追加的方式,但仅仅追加,其实有时候还满足不了要求,比如spring.factories文件,他需要达到的效果应该是如下图

后面我通过maven-shade-plugin的官方示例(https://maven.apache.org/plugins/maven-shade-plugin/examples/resource-transformers.html)试图想找到解决方案,但是有点遗憾,没找到。于是在我面前就有两条路,一条是放弃maven-shade-plugin插件,比如选择其他类似的插件,比如maven-assembly-plugin,这种方案我试过,发现maven-assembly-plugin这个插件的扩展配置,比maven-shade-plugin复杂一些,于是放弃。最后选择了在maven-shade-plugin基础再扩展一下。

扩展的思路

我并没采用直接修改maven-shade-plugin插件的方式,而是在maven-shade-plugin打包后的基础上,再进行插件定制。实现的思路也不难,就是修改maven-shade-plugin打成jar后的spring.factories文件内容,将


调整成形如下即可

自定义maven插件spring-factories-merge-plugin

核心思路

1、如何读取配置文件spring.factories中key重复的内容,而不被覆盖

如果是直接使java.util.properties的读取,当配置文件中有key重复时,比如有多个org.springframework.boot.autoconfigure.EnableAutoConfiguration时,最后会出现value值被覆盖的情况。

解决方案,我们可以利用org.apacche.commons.configuration.PropertiesConfiguration来进行处理

在项目的pom引入GAV

 <dependency><groupId>org.apache.commons</groupId><artifactId>commons-configuration2</artifactId><version>${commons-configuration2}</version></dependency>

读取配置示例代码

 @SneakyThrowspublic static Map<String, Set<String>> readFactoriesFile(InputStream input)  {// 读取 spring.factories 内容//利用PropertiesConfiguration取配置文件中key重复的内容,而不被覆盖PropertiesConfiguration properties = new PropertiesConfiguration();properties.read(new InputStreamReader(input));Map<String, Set<String>> multiSetMap = new LinkedHashMap<>();Iterator<String> keys = properties.getKeys();while(keys.hasNext()) {String key = keys.next();String[] values = properties.getStringArray(key);Set<String> collectSet = new LinkedHashSet<>();buildKeyValues(values, collectSet);multiSetMap.put(key,collectSet);}return multiSetMap;}

2、如何将修改后的配置文件,重新写入jar

我这边的思路就是直接利用IO进行操作了

示例如下

 public static void writeFactoriesFile(String factoriesBaseClassPathDir,String finalJarName) throws IOException {String jarFilePath = String.format(factoriesBaseClassPathDir + "/target/" + finalJarName).replace("\\", "/").replaceAll("//+", "/");if(!jarFilePath.endsWith(".jar")){jarFilePath = jarFilePath + ".jar";}JarFile jarFile = new JarFile(jarFilePath);if(jarFile != null){List<JarEntry> jarFiles = jarFile.stream().collect(Collectors.toList());@ Cleanup FileOutputStream fos = new FileOutputStream(jarFile.getName(), true);@ Cleanup JarOutputStream jos = new JarOutputStream(fos);for (JarEntry jarEntry : jarFiles) {if(jarEntry.getName().startsWith(SpringFactoriesLoader.FACTORIES_RESOURCE_LOCATION)){try {@ Cleanup InputStream input = jarFile.getInputStream(jarEntry);Map<String, Set<String>> factoriesMap = readFactoriesFile(input);jos.putNextEntry(new JarEntry(jarEntry.getName()));generateFactoriesContent(factoriesMap,jos);} catch (IOException e) {e.printStackTrace();}}else{//表示将该JarEntry写入jar文件中 也就是创建该文件夹和文件jos.putNextEntry(new JarEntry(jarEntry));jos.write(streamToByte(jarFile.getInputStream(jarEntry)));}}}}

项目中如何配置插件

<build><plugins><!-- 防止同名配置文件,在打包时被覆盖,用来处理多个jar包中存在重名的配置文件的合并参考dubbo:https://github.com/apache/dubbo/blob/master/dubbo-all/pom.xml--><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-shade-plugin</artifactId><executions><execution><phase>package</phase><goals><goal>shade</goal></goals><configuration><transformers><transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"><resource>META-INF/spring.factories</resource></transformer><transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"><resource>META-INF/spring.handlers</resource></transformer><transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"><resource>META-INF/spring.schemas</resource></transformer><transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"><resource>META-INF/spring.tooling</resource></transformer></transformers></configuration></execution></executions></plugin><plugin><groupId>com.github.lybgeek.jar</groupId><artifactId>spring-factories-merge-plugin</artifactId><version>0.0.1-SNAPSHOT</version><executions><execution><phase>package</phase><goals><goal>springFactoriesMerge</goal></goals></execution></executions><configuration><factoriesBaseClassPathDir>${basedir}</factoriesBaseClassPathDir><finalJarName>${project.artifactId}-${project.version}</finalJarName></configuration></plugin></plugins></build>

这边有个小细节是当maven-shade-plugin和spring-factories-merge-plugin的执行生命周期都是相同阶段,比如都是在package时,则maven-shade-plugin放置顺序得在spring-factories-merge-plugin之前,因为spring-factories-merge-plugin是对maven-shade-plugin打包后的结果进行二次加工。如果maven-shade-plugin不放置顺序得在spring-factories-merge-plugin之前,则spring-factories-merge-plugin的执行阶段就要比maven-shade-plugin靠后,比如maven-shade-plugin在package阶段执行,则spring-factories-merge-plugin就得在install或者deploy阶段执行

打包后的效果图如下

总结

之前在看开源框架的时候,很经常都是聚焦在源码上,而不会去注意一些maven插件,这次因为有这打jar的需求。我发现不管是springboot还是dubbo本身就集成一些宝藏插件,比如这个maven-shade-plugin插件,我就是dubbo那边找到的,地址在
https://github.com/apache/dubbo/blob/master/dubbo-all/pom.xml
。比如版本占位符插件flatten-maven-plugin在dubbo和springboot都有看到使用。如果后面有对maven插件由需求,推荐可以从springboot或者dubbo那边去搜,估计会有意想不到的收获

demo链接

https://github.com/lyb-geek/springboot-learning/tree/master/springboot-jar-merge

相关文章:

聊聊如何避免多个jar通过maven打包成一个jar,多个同名配置文件发生覆盖问题

前言 不知道大家在开发的过程中&#xff0c;有没有遇到这种场景&#xff0c;外部的项目想访问内部nexus私仓的jar&#xff0c;因为私仓不对外开放&#xff0c;导致外部的项目没法下载到私仓的jar&#xff0c;导致项目因缺少jar而无法运行。 通常遇到这种场景&#xff0c;常用…...

Flume 使用小案例

案例一&#xff1a;采集文件内容上传到HDFS 1&#xff09;把Agent的配置保存到flume的conf目录下的 file-to-hdfs.conf 文件中 # Name the components on this agent a1.sources r1 a1.sinks k1 a1.channels c1 # Describe/configure the source a1.sources.r1.type spoo…...

DLO-SLAM代码阅读

文章目录DLO-SLAM点评代码解析OdomNode代码结构主函数 main激光回调函数 icpCB初始化 initializeDLO重力对齐 gravityAlign点云预处理 preprocessPoints关键帧指标 computeMetrics设定关键帧阈值setAdaptiveParams初始化目标数据 initializeInputTarget设置源数据 setInputSour…...

X和Ku波段小尺寸无线电设计

卫星通信、雷达和信号情报(SIGINT)领域的许多航空航天和防务电子系统早就要求使用一部分或全部X和Ku频段。随着这些应用转向更加便携的平台&#xff0c;如无人机(UAV)和手持式无线电等&#xff0c;开发在X和Ku波段工作&#xff0c;同时仍然保持极高性能水平的新型小尺寸、低功耗…...

推荐算法 - 汇总

本文主要对推荐算法整体知识点做汇总&#xff0c;做到总体的理解&#xff1b;深入理解需要再看专业的材料。推荐算法的意义推荐根据用户兴趣和行为特点&#xff0c;向用户推荐所需的信息或商品&#xff0c;帮助用户在海量信息中快速发现真正所需的商品&#xff0c;提高用户黏性…...

Android 系统的启动流程

前言&#xff1a;从开机的那一刻&#xff0c;到开机完成后launcher将所有应用进行图标展示的这个过程&#xff0c;大概会有哪一些操作&#xff1f;执行了哪些代码&#xff1f;作为Android开发工程师的我们&#xff0c;有必要好好的梳理一遍。既然要梳理Android系统的启动流程&a…...

自学5个月Java找到了9K的工作,我的方式值得大家借鉴 第二部分

我的学习心得&#xff0c;我认为能不能自学成功的要素有两点。 第一点就是自身的问题&#xff0c;虽然想要转行学习Java的人很多&#xff0c;但是非常强烈的想要转行学好的人是小部分。而大部分人只是抱着试试的心态来学习Java&#xff0c;这是完全不可能的。所以能不能学成Jav…...

Vue 3 第五章:reactive全家桶

文章目录1. reactive1.1. reactive函数创建一个响应式对象1.2. 修改reactive创建的响应式对象的属性2. readOnly2.1. 使用 readonly 函数创建一个只读的响应式对象2.2. 如何修改嵌套在只读响应式对象中的对象?3. shallowReactive3.1. 使用 shallowReactive 函数创建一个浅层响…...

【联机对战】微信小程序联机游戏开发流程详解

现有一个微信小程序叫中国象棋项目&#xff0c;棋盘类的单机游戏看着有缺少了什么&#xff0c;现在给补上了&#xff0c;加个联机对战的功能&#xff0c;增加了可玩性&#xff0c;对新手来说&#xff0c;实现联机游戏还是有难度的&#xff0c;那要怎么实现的呢&#xff0c;接下…...

优化基于axios接口管理的骚操作

优化基于axios接口管理的骚操作&#xff01; 本文针对中大型的后台项目的接口模块优化&#xff0c;在不影响项目正常运行的前提下&#xff0c;增量更新。 强化功能 1.接口文件写法简化&#xff08;接口模块半自动化生成&#xff09; 2.任务调度、Loading调度&#xff08;接口层…...

【Django功能开发】如何正确使用定时任务(启动、停止)

系列文章目录 【Django开发入门】ORM的增删改查和批量操作 【Django功能开发】编写自定义manage命令 文章目录系列文章目录前言一、django定时任务二、django-apscheduler基本使用1.安装django-apscheduler2.配置settings.py的INSTALLED_APPS3.通过命令生成定时记录表3.如何创…...

7个好用到爆的音频、配乐素材网站,BGM都在这里了

现在只要有一部手机&#xff0c;人人都能成为视频创作者。一个好的视频不能缺少的就是内容、配乐&#xff0c;越来越注重版权的当下&#xff0c;音效素材使用不当造成侵权的案例层出不穷。为了避免侵权&#xff0c;找素材让很多创作者很头疼。 今天我就整理了7个可以免费下载&…...

JUC(二)

1.可重入锁–ReentrantLock原理 1.1.非公平锁的实现原理 1.1.1.加锁解锁流程 1>.先从构造器开始看,默认为非公平锁,可以在构造函数中设置参数指定公平锁 public ReentrantLock() {sync = new NonfairSync(); }public ReentrantLock...

ATS认证教学

我用的版本是ATS7.11、系统版本是用最新的ios13.2.1 定义 ATS旨在分析通过UART、USB和蓝牙传输传输的iAP流量、通过USB和无线&#xff08;蓝牙和Wi-Fi&#xff09;传输的CarPlay流量、通过Wi-Fi传输的AirPlay 2流量以及闪电音频流量。 ATS是Apple’s Accessory Test System的…...

【操作系统】进程管理

进程与线程 1. 进程 进程是资源分配的基本单位 进程控制块 (Process Control Block, PCB) 描述进程的基本信息和运行状态&#xff0c;所谓的创建进程和撤销进程&#xff0c;都是指对 PCB 的操作。 下图显示了 4 个程序创建了 4 个进程&#xff0c;这 4 个进程可以并发地执行…...

一分钟掌握技术术语:API(接口)

很多产品经理在项目开发过程中经常听到&#xff1a;你调我这个接口就好了&#xff1b;这个功能你写个接口给我&#xff1b;有什么不懂的就看下API接口文档。 开发经常说的接口是什么意思呢&#xff1f;术语解释&#xff1a;API&#xff08;Application Programming Interface&…...

RabbitMQ之交换机

交换机 在上一节中,我们创建了一个工作队列。我们假设的是工作队列背后,每个任务都恰好交付给一个消费者(工作进程)。在这一部分中,我们将做一些完全不同的事情-我们将消息传达给多个消费者。这种模式称为“发布/订阅”. 为了说明这种模式,我们将构建一个简单的日志系统。它…...

Tensorflow深度学习对遥感图像分类,内存不够怎么办?

问题描述在使用Tensorflow-cpu对图像分类的时候&#xff0c;在预读数据过程中&#xff0c;由于数据量过大&#xff0c;内存不足&#xff0c;导致计算失败。使用环境&#xff1a;win10系统 Pycharm tensorflow-cpu2.5.0 CPU: i7 8700 内存64G图1 CPU配置图图2 内存信息图使用数据…...

基础存贮模型介绍

基础存贮模型 这里主要讨论在需求量稳定的情况下&#xff0c;贮存量需要多少的问题。当贮存量过大时&#xff0c;会提高库存成本&#xff0c;也会造成积压资金&#xff1b;当贮存量过小时&#xff0c;会导致一次性订购费用增加&#xff0c;或者不能及时满足需求。 下面讨论不允…...

JNDIExploit使用方法

JNDIExploit 一款用于 JNDI注入 利用的工具&#xff0c;大量参考/引用了 Rogue JNDI 项目的代码&#xff0c;支持直接植入内存shell&#xff0c;并集成了常见的bypass 高版本JDK的方式&#xff0c;适用于与自动化工具配合使用。 对 feihong-cs 大佬的项目https://github.com/fe…...

建议一般人不要全职做副业

欢迎关注勤于奋每天12点准时更新国外LEAD相关技术全职做国外LEAD&#xff0c;听起来不错&#xff0c;但是效果不一定好&#xff0c;没有自控力来全职做&#xff0c;基本要废了自己&#xff0c;最好抽时间来做。我现在就是全职做国外LEAD&#xff0c;外加其他一些项目&#xff0…...

pytorch入门6--数据分析(pandas)

pandas是基于Numpy构建的&#xff0c;提供了众多比NumPy更高级、更直观的数据处理功能&#xff0c;尤其是它的DataFrame数据结构&#xff0c;可以用处理数据库或电子表格的方式来处理分析数据。 使用Pandas前&#xff0c;需导入以下内容&#xff1a; import numpy as np from …...

淘宝API接口开发系列,详情接口参数说明

onebound.taobao.item_get 公共参数 名称类型必须描述keyString是 调用key&#xff08;必须以GET方式拼接在URL中&#xff09; 注册Key和secret&#xff1a; https://o0b.cn/anzexi secretString是调用密钥api_nameString是API接口名称&#xff08;包括在请求地址中&#xff0…...

keep-alive

keep-alive 是 Vue 的内置组件&#xff0c;当它包裹动态组件时&#xff0c;会缓存不活动的组件实例&#xff0c;而不是销毁它们。 keep-alive 包裹动态组件时&#xff0c;会缓存不活动的组件实例&#xff0c;而不是销毁它们 使用场景 使用原则&#xff1a;当我们在某些场景下…...

Maven的生命周期及常用命令

文章目录1、Maven生命周期1.1、clean生命周期1.2、default生命周期1.3、site生命周期2、Maven常用命令1、Maven生命周期 Maven有三套生命周期系统&#xff1a; 1、clean生命周期 2、default生命周期 3、site生命周期 三套生命周期之间相互独立&#xff0c;每套生命周期包含一…...

【Java开发】JUC基础 03:线程五大状态和主要方法

1 概念介绍&#x1f4cc; 五大状态&#xff1a;new&#xff1a;Thread t new Thread(); 线程对象一旦被创建就进入到了新生状态&#xff1b;就绪状态&#xff1a;当调用start()方法&#xff0c;线程立即进入就绪状态&#xff0c;但不意味着立即调度执行&#xff1b;运行状态&a…...

docker打包容器 在另一个机器上运行

1&#xff1a;将运行中的容器变为镜像docker commit 容器id 镜像名&#xff08;docker commit 89e81386d35e aabbcc&#xff09;2:将容器打包成tar包docker save -o xxx.tar 镜像名 &#xff08;当前路径下会生成一个tar的文件&#xff09;3&#xff1a;将tar包压缩为gz包tar -…...

2023年全国最新保安员精选真题及答案9

百分百题库提供保安员考试试题、保安职业资格考试预测题、保安员考试真题、保安职业资格证考试题库等&#xff0c;提供在线做题刷题&#xff0c;在线模拟考试&#xff0c;助你考试轻松过关。 91.护卫对象在公共场所参加活动前&#xff0c;保安员需要事先&#xff08;&#xff0…...

arduino-sentry2之卡片篇

欧克,今天在学生的强烈要求下 我又重启arduino的sentry2调试篇 目前实验结果,可以检测到10张交通卡片 也就是如图所示十张 具体视频如下: https://live.csdn.net/v/279170 具体代码如下: #include <Arduino.h> #include <...

七、JUC并发工具

文章目录JUC并发工具CountDownLatch应用&源码分析CountDownLatch介绍CountDownLatch应用CountDownLatch源码分析有参构造await方法countDown方法CyclicBarrier应用&源码分析CyclicBarrier介绍CyclicBarrier应用CyclicBarrier源码分析CyclicBarrier的核心属性CyclicBarr…...

建设工业网站/seo综合查询什么意思

第十章 接口 接口和抽象类提供了一种将接口与实现分离的更加结构化的方法。 这种机制在编程语言中不常见&#xff0c;例如 C 只对这种概念有间接的支持。而在 Java 中存在这些关键字&#xff0c;说明这些思想很重要&#xff0c;Java 为它们提供了直接支持。 首先&#xff0c…...

wordpress参数手册/厦门网站设计公司

本文参考自油管上某个国外大神的公开演讲视频&#xff0c;学习了一下觉得很不错&#xff0c;所以在项目中也使用了这些不错的技巧。 1. watch 与 computed 的巧妙结合 如上图&#xff0c;一个简单的列表页面。 你可能会这么做&#xff1a; created(){this.fetchData() },watch:…...

企业网站排名怎么做/淘宝关键词优化技巧

要求&#xff1a; 用户在输入框输入内容时&#xff0c;上面的大号字体盒子con显示&#xff08;这里面的字号更大&#xff09; 实现思路&#xff1a; 表单检测用户输入&#xff1a;给表单添加键盘事件同时把快递单号里面的值value获取过来赋值给con盒子innerText做为内容如果…...

祥云网站建设/搜狗推广效果好吗

好久没有写博客了&#xff0c;这次准备写写我这几天的研究成果——Android插件化开发框架CJFrameForAndroid。 好久没有写博客了&#xff0c;这次准备写写我这几天的研究成果——Android插件化开发框架CJFrameForAndroid。背景交代 首先。你须要知道什么是插件化开发。就拿最常…...

无锡网站推广哪家好/搜索引擎优化seo专员

在完成了基本的Shopify功能配置之后&#xff0c;我们接下来要配置和设计好我们的Shopify店铺。完成了店铺的设计和配置之后&#xff0c;我们的电商独立站点就可以正式上线对外了。1.Shopify店铺主题模板选择用Shopify来做独立站的一个好处就是&#xff1a;我们可以建一个独立的…...

wordpress server error/短视频新媒体推广

Swagger(Api管理)主要应用于前后端分离的项目&#xff0c;实时更新最新API&#xff0c;降低集成风险。RestFul Api文档在线自动生成工具》Api文档与Api定义同步更新直接运行&#xff0c;可以在线测试API接口支持多种语言#在项目中使用Swagger需要Springfox依赖;& swagger2&…...