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

news2025/1/18 9:51:41

前言

不知道大家在开发的过程中,有没有遇到这种场景,外部的项目想访问内部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>

读取配置示例代码

 @SneakyThrows
    public 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

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

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

相关文章

HUN工训中心:开关电路和按键信号抖动

工训中心的牛马实验 1.实验目的&#xff1a; 1) 认识开关电路&#xff0c;掌握按键状态判别、开关电路中逻辑电平测量、逻辑值和逻辑函数电路。 2) 掌握按键信号抖动简单处理方法。 3) 实现按键计数电路。 2.实验资源&#xff1a; HBE硬件基础电路实验箱、示波器、万用表…

Java学习--网络编程

1. 网络编程入门 1.1 网络编程概述 计算机网络 是指将地理位置不同的具有独立功能的多台计算机及其外部设备&#xff0c;通过通信线路连接起来&#xff0c;在网络操作系统&#xff0c;网络管理软件及网络通信协议的管理和协调下&#xff0c;实现资源共享和信息传递的计算机系统…

【GIT】git不同仓库设置不同用户名和邮箱

随着业务的拓展&#xff0c;工作中&#xff0c;我们除了要在公司的git库提交代码&#xff0c;还可能在阿里云效codeup、gitcode等上提交代码。 之前git安装时使用的是全局名称&#xff0c;导致所有的git提交都使用了相同的用户名和邮箱。 查看全局用户名和邮箱 $ git config -…

论文调研——23.2.28

文章目录Diffusion Models: A Comprehensive Survey of Methods and ApplicationsADVERSARIAL TRAINING METHODS FOR SEMI-SUPERVISED TEXT CLASSIFICATIONHuman Emotion Knowledge Representation Emerges in LargeAPI 调优上&#xff1a; Decoder Tuning: Efficient Language…

记录一下atlas200模块无法正常通过别的设备SSH连接192.168.1.2问题笔记粗心者用

atlas200远程登录发现登录不了 串口打印内核信息正常显示&#xff0c;如下几个错误记录分别在windows上和ubuntu上分别做了测试&#xff0c;之前都是 安装完 kex_exchange_identification: read: Connection reset 华为atlas200模块 登录显示这个目前没有解决&#xff0c;应该…

220V转5V非隔离2W电源--超低成本

目录 详情 产品特性和优势 设计电路 MP150芯片资料 详情 MP150 是一款原边调节器&#xff0c;可以在无光耦合器的条件下提供精确的恒压&#xff08;CV&#xff09;调节。MP150 支持降压、升降压、升压和反激拓扑。它内部集成了 500V MOSFET&#xff0c;可简化结构&#xff0…

HUN工训中心:三位数码管扫描显示实验报告

工训中心牛马实验 1.1操作说明及现象描述 根据老师发的安装包下载和安装好Quartus II软件。在电脑安装USB硬件驱动&#xff0c;再与PC接口连接好&#xff0c;可以打开任务管理器查看是否安装好。之后打开Quartus II软件&#xff0c;按照菜单Tool->programmer->add file…

6大类,不同类型单板布线策略

PCB布线策略 一、类型一主要特征如下 严格的长度规则、严格的串扰规则、拓扑规则、差分规则、电源地规则等。 二、关键网络的处理&#xff1a;总线定义Class 要求满足一定的拓扑结构、stub及其长度(时域)约束条件 图-1 平衡菊花链和中间驱动菊花链图 设置虚拟管脚来控制拓扑…

张驰咨询:六西格玛常见问题解答

以下是张驰咨询对一些关于六西格玛常见问题的解答&#xff1a; 1、六西格玛是什么&#xff1f; 六西格玛是一种改善企业质量流程管理的技术&#xff0c;以“零缺陷”的完美商业追求&#xff0c;带动质量大幅提高、成本大幅度降低&#xff0c;最终实现企业财务成效的提升与企业…

SpringCloud系列知识快速复习 -- part 1(SpringCloud基础知识,Docker,RabbitMQ)

SpringCloud知识快速复习SpringCloud基础知识微服务特点SpringCloud常用组件服务拆分和提供者与消费者概念Eureka注册中心原理Ribbon负载均衡原理负载均衡策略饥饿加载Nacos注册中心服务分级存储模型权重配置环境隔离Nacos与Eureka的区别Nacos配置管理拉取配置流程配置热更新配…

大家挤破脑袋都想进的腾讯,你为什么想要辞职?

前几天&#xff0c;我在网上看到一个故事。 故事的主人翁是19届的校招生&#xff0c;目前入职腾讯&#xff0c;工作了一个月。这一个月给他的感受是大量的写测试用例&#xff0c;感觉自己写测试用例的能力熟练了不少&#xff0c;测试技能倒是没有多大的提高&#xff0c;真正需…

一文解决Python所有报错

前言 Python是一种强大的编程语言&#xff0c;但是它也有一些报错&#xff0c;这些报错可能会让你感到困惑。本文将介绍如何解决Python中的常见报错。 首先&#xff0c;让我们来看看Python中最常见的报错&#xff1a;SyntaxError。这种报错表明你的代码中有语法错误&#xff0c…

Javaweb增删改查之【查】

Javaweb增删改查之【查】1.前端页面2.java链接数据库——集成mybatis2.1 建立层2.2 实体层entity2.3 mapper&#xff08;dao层&#xff09;2.4 mybatis配置文件2.5工具层util3.后台功能3.1servlet前几天跟着b站up主学javaweb登录&#xff0c;突然还是觉得这几年学了c是真的挺好…

对象扩展、函数的扩展、Symbol、Iterator 迭代器、Set结构、Map 数据结构——ES6+

目录 一、对象扩展 二、函数的扩展 三、Symbol 四、Iterator 迭代器 五、Set结构 六、Map 数据结构 一、对象扩展 1. 对象简写 对于对象属性&#xff0c;属性名与属性值对应的变量相同时&#xff0c;可简写为属性名 对于对象方法&#xff0c;将 &#xff1a;function…

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

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

Javaweb复习之HTTPTomcatServelet

1.Web概述 1.1 Web和JavaWeb的概念 Web是全球广域网&#xff0c;也称为万维网(www)&#xff0c;能够通过浏览器访问的网站。 JavaWeb就是用Java技术来解决相关web互联网领域的技术栈 1.2 JavaWeb技术栈 B/S 架构&#xff1a;Browser/Server&#xff0c;浏览器/服务器 架构模…

仓库管理系统包括哪些方面?

想要弄清楚仓库管理系统包括哪些方面&#xff0c;得先了解仓库管理体系包含哪些方面。 一般来说&#xff0c;仓库管理体系包含以下四大方面&#xff1a; 数据精确度体系&#xff1a;如何通过制定相关政策及操作规范&#xff0c;提升库内数据的准确度。管理体系&#xff1a;构…

React的生命周期详细讲解

什么是生命周期&#xff1f; 所谓的React生命周期&#xff0c;就是指组件从被创建出来&#xff0c;到被使用&#xff0c;最后被销毁的这么一个过程。而在这个过程中&#xff0c;React提供了我们会自动执行的不同的钩子函数&#xff0c;我们称之为生命周期函数。**组件的生命周期…

【论文阅读】-姿态识别

记录论文阅读&#xff0c;希望能了解我方向的邻域前沿吧 粗读 第一篇 ATTEND TO WHO YOU ARE: SUPERVISING SELF-ATTENTION FOR KEYPOINT DETECTION AND INSTANCE-AWARE ASSOCIATION 翻译&#xff1a;https://editor.csdn.net/md?not_checkout1&spm1001.2014.3001.5352…

设计模式系列 - 代理模式及动态代理详解

定义 为其他对象提供一种代理以控制对这个对象的访问。在某些情况下&#xff0c;一个对象不适合或者不能直接引用另一个对象&#xff0c;而代理对象可以在客户端和目标对象之间起到中介的作用。 结构 抽象角色&#xff1a;通过接口或抽象类声明真实角色实现的业务方法。 代…