Maven uber-jar(带依赖的打包插件)maven-shade-plugin

news2024/11/24 4:53:45

文章目录

  • 最基础的 maven-shade-plugin 使用
  • 生成可执行的 Jar 包 和 常用的资源转换类
  • 包名重命名
  • 打包时排除依赖
  • 与其他常用打包插件比较

本文是对 maven-shade-plugin 常用配置的介绍,更详细的学习请参照 Apache Maven Shade Plugin 官方文档

通过使用 maven-shade-plugin 插件进行 Maven 的打包操作,可以将项目中的依赖一同添加到最终的项目 Jar 包内,maven-shade-plugin 插件有两个目标,我们要学习的是插件的 shade 目标,建议使用时与 Maven 生命周期的 package 阶段绑定

这中打包后带依赖的 Jar 包一般称为 uper-jarfat-jar

不管 pom.xml 是否声明了 Maven 的默认打包插件 maven-jar-plugin,也不管是否声明了其他打包插件,maven-jar-plugin 都会在 package 阶段最先执行,而 maven-shade-plugin 插件的 shade 目标,正是对 maven-jar-plugin 打包后的 Jar 包进行二次打包,同时将项目依赖的添加进去

最基础的 maven-shade-plugin 使用

按照如下配置 15~29 行,就可以正确的将 maven-shade-plugin 插件的 shade 目标与 Maven 的生命周期 package 阶段绑定,之后我们就可以通过 Maven 生命周期命令 mvn package 执行插件完成打包操作:

<!-- 模拟项目中使用的依赖 -->
<dependencies>
<!-- 依赖 commons-lang3 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.12.0</version>
</dependency>
</dependencies>

<!-- 对项目构建进行配置 -->
<build>
    <plugins>
        <!-- 引入 maven-shade-plugin 插件 -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>3.2.1</version>
            <executions>
                <execution>
                    <!-- 绑定 Maven 生命周期的 package 阶段 -->
                    <phase>package</phase>
                    <goals>
                        <!-- package 阶段执行时,让其调用插件的 repackage 目标 -->
                        <goal>shade</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

打包后,会将打包文件放到 ${project.build.directory} 中(默认是 target 目录),如下图:

打包后的输出目录

上图中,我们重点关注两个文件:

  • ares5k-package-1.0-SNAPSHOT.jar:maven-shade-pluginmaven-jar-plugin 生成的 Jar 包进行二次打包后的 Jar 包,这个 Jar 包内已经包函项目的依赖了

  • original-ares5k-package-1.0-SNAPSHOT.jar:原始 Jar 包,maven-jar-plugin 生成的不包含项目依赖的 Jar 包,maven-shade-plugin 为了避免原始 Jar 包和新 Jar 包名字冲突,对原始 Jar 包进行了重命名,添加了 original- 前缀

maven-shade-plugin 生成的 Jar 包解压,观察其内部结构可以发现,maven-shade-plugin 打包后并没有将项目中依赖的 Jar 包直接存放,而是将依赖的 Jar 包解压后存放的:

这个特性很重要,因为 maven-shade-plugin 的另一个重要功能 <重命名> 就是基于这个实现的

解压后的目录

另外,通过上面配置创建的 Jar 包是不能够直接运行的,因为我们没有指定项目的入口类,所以只适合让其他项目引用

生成可执行的 Jar 包 和 常用的资源转换类

仿照下面 19~27 行,就可以生成可直接运行的 Jar 包:

<!-- 对项目构建进行配置 -->
<build>
    <plugins>
        <!-- 引入 maven-shade-plugin 插件 -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>3.2.1</version>
            <executions>
                <execution>
                    <!-- 绑定 Maven 生命周期的 package 阶段 -->
                    <phase>package</phase>
                    <goals>
                        <!-- package 阶段执行时,让其调用插件的 repackage 目标 -->
                        <goal>shade</goal>
                    </goals>
                </execution>
            </executions>
            <!-- 设置插件属性 -->
            <configuration>
                <transformers>
                    <!-- 添加入口类 -->
                    <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
						<!-- 入口类 -->
                        <mainClass>com.ares5k.BB</mainClass>
                    </transformer>
                </transformers>
            </configuration>
        </plugin>
    </plugins>
</build>

上面代码中 ManifestResourceTransformermaven-shade-plugin 插件提供的资源转换类,就是用来指定入口类的,除此之外,还有两个常用的资源转换类,AppendingTransformerServicesResourceTransformer

AppendingTransformer

这个资源转换类的作用,是在打包时,当碰见项目中或依赖中有同名的资源文件,要对文件内容进行追加操作,而不是覆盖

最常见的场景就是项目中引入大量 Spring 依赖时,每个 Spring 依赖都有自己的 META-INF/spring.handlersMETA-INF/spring.schemasMETA-INF/spring.factories 等文件,这种情况下利用 maven-shade-plugin 插件打包,就会发生文件覆盖的问题,如果被覆盖的文件内容和新文件的内容一样,可能会掩盖这个现象,但是当内容不一样时,就会导致项目运行错误

以 Spring 为例,演示同名文件追加操作的配置 16~27 行:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>3.2.1</version>
    <executions>
        <execution>
            <!-- 绑定 Maven 生命周期的 package 阶段 -->
            <phase>package</phase>
            <goals>
                <!-- package 阶段执行时,让其调用插件的 repackage 目标 -->
                <goal>shade</goal>
            </goals>
        </execution>
    </executions>
    <!-- 设置插件属性 -->
    <configuration>
        <transformers>
            <!-- 设置 META-INF/spring.handlers 文件追加而非覆盖 -->
            <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                <resource>META-INF/spring.handlers</resource>
            </transformer>
			<!-- 设置 META-INF/spring.schemas 文件追加而非覆盖 -->
            <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                <resource>META-INF/spring.schemas</resource>
            </transformer>
            <!-- 设置 META-INF/spring.factories文件追加而非覆盖 -->
            <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                <resource>META-INF/spring.factories</resource>
            </transformer>
        </transformers>
    </configuration>
</plugin>

ServicesResourceTransformer

这个资源转换类的作用,是在打包时,当碰见同名的 SPI 接口文件时,会将文件内容追加而不是覆盖

我们使用 Java SPI 机制时,会在 META-INF/services 中创建与接口同名的文件来记录具体的实现类,当有多个同名文件时,说明该接口有多个实现类,这时候就可以利用 ServicesResourceTransformer 将文件内容合并,而不是覆盖 16~21 行:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>3.2.1</version>
    <executions>
        <execution>
            <!-- 绑定 Maven 生命周期的 package 阶段 -->
            <phase>package</phase>
            <goals>
                <!-- package 阶段执行时,让其调用插件的 repackage 目标 -->
                <goal>shade</goal>
            </goals>
        </execution>
    </executions>
    <!-- 设置插件属性 -->
    <configuration>
        <transformers>
            <!-- 设置 Java SPI 追加而非覆盖 -->
            <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
        </transformers>
    </configuration>
</plugin>

包名重命名

重命名功能也是 maven-shade-plugin 插件的一个重要功能,这个功能可以对项目中或依赖中的包名进行重命名,包名重命名后还会把引用的地方一并修改,非常方便

这个功能的使用场景,主要是版本冲突,举个例子吧,前置条件如下:

  1. 我们的项目 A 中引用了 commons-lang3,另一个需要依赖我们项目的工程 B 也引入了 commons-lang3

  2. 两个 commons-lang3 的版本不一样,拥有相同包名、类名,但功能不同的类

基于上述条件,两个 commons-lang3 中相同包名类名的类只会被 JVM 加载一个,这就可能导致项目运行时出问题,比如调用的是未被加载的类中的功能,这时候就可以使用包名重命名功能防止 JVM 只加载一个类的情况

下面配置中 17~27 行就是对包名重新命名:

<!-- 引入 maven-shade-plugin 插件 -->
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>3.2.1</version>
    <executions>
        <execution>
            <!-- 绑定 Maven 生命周期的 package 阶段 -->
            <phase>package</phase>
            <goals>
                <!-- package 阶段执行时,让其调用插件的 repackage 目标 -->
                <goal>shade</goal>
            </goals>
        </execution>
    </executions>
    <!-- 设置插件属性 -->
    <configuration>
      <relocations>
          <!-- 包名重命名 -->
          <relocation>
              <!-- 旧包名 -->
              <pattern>org.apache.commons.lang3</pattern>
              <!-- 新包名 -->
              <shadedPattern>shade.org.apache.commons.lang3</shadedPattern>
          </relocation>
      </relocations>
    </configuration>
</plugin>

重命名后,项目中引用的地方也会自动修改,我们通过反编译看一下重命名后使用原包名的 .class 文件,引用的包名已经自动替换成新的名字:

包名重命名


打包时排除依赖


排除整个依赖

打包时,想排除不需要的依赖,可以使用下面配置 17~24 行,这种方式是将整个依赖排除掉,语法为 groupId:artifactId

<!-- 引入 maven-shade-plugin 插件 -->
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>3.2.1</version>
    <executions>
        <execution>
            <!-- 绑定 Maven 生命周期的 package 阶段 -->
            <phase>package</phase>
            <goals>
                <!-- package 阶段执行时,让其调用插件的 repackage 目标 -->
                <goal>shade</goal>
            </goals>
        </execution>
    </executions>
    <!-- 设置插件属性 -->
    <configuration>
        <artifactSet>
			<!-- 排除依赖,groupId:artifactId -->
            <excludes>
                <exclude>org.apache.commons:commons-lang3</exclude>
            </excludes>
        </artifactSet>
    </configuration>
</plugin>

排除依赖中的部分文件

也可以更详细的配置,排除依赖中的某些文件,而不是整个依赖排除,17~30 行 :

<!-- 引入 maven-shade-plugin 插件 -->
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>3.2.1</version>
    <executions>
        <execution>
            <!-- 绑定 Maven 生命周期的 package 阶段 -->
            <phase>package</phase>
            <goals>
                <!-- package 阶段执行时,让其调用插件的 repackage 目标 -->
                <goal>shade</goal>
            </goals>
        </execution>
    </executions>
    <!-- 设置插件属性 -->
    <configuration>
        <filters>
            <!-- 过滤 commons-lang3 依赖中的内容-->
            <filter>
                <artifact>org.apache.commons:commons-lang3</artifact>
                <excludes>
                    <!-- 排除  commons-lang3 依赖中 org/apache/commons/lang3/math 下的所有文件-->
                    <exclude>org/apache/commons/lang3/math/**</exclude>
                    <!-- 排除  commons-lang3 依赖中 META-INF 下所有扩展名是 .txt 的文件-->
                    <exclude>META-INF/*.txt</exclude>
                </excludes>
            </filter>
        </filters>
    </configuration>
</plugin>

排除项目中未使用的依赖

将项目中,引用后,实际没有使用的依赖排除掉,这个功能可以帮我们排除多余的包,来减小最后生成的 Jar 包的大小,

用的时候要注意一点,因为插件判断依赖是否使用,是通过依赖中的类是否被项目中的类引用来作为依据,假设我们使用 XML 版的 Spring,在 <bean> 中配置了依赖中的类,这个时候插件是无法检测到的,所以仍会将其排除

<!-- 引入 maven-shade-plugin 插件 -->
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>3.2.1</version>
    <executions>
        <execution>
            <!-- 绑定 Maven 生命周期的 package 阶段 -->
            <phase>package</phase>
            <goals>
                <!-- package 阶段执行时,让其调用插件的 repackage 目标 -->
                <goal>shade</goal>
            </goals>
        </execution>
    </executions>
    <!-- 设置插件属性 -->
    <configuration>
        <!-- 将项目中未使用的依赖排除 -->
        <minimizeJar>true</minimizeJar>
    </configuration>
</plugin>

与其他常用打包插件比较

除了 maven-shade-plugin 之外,常用的打包插件还有 maven-jar-pluginspring-boot-maven-plugin,使用方法在我其他文章中也有记录,在此将它们作一个简单的比较

maven-shade-plugin

maven-shade-plugin 也可以将项目的依赖打进最终的项目 Jar 包中,但是其与 spring-boot-maven-plugin 不同的是,spring-boot-maven-plugin 是直接将依赖的 Jar 包放进项目的 Jar 包中,而 maven-shade-plugin 则是将依赖的 Jar 包解压,然后将解压后的文件放进最终的项目 Jar 包中

maven-shade-plugin 将依赖的 Jar 包解压后添加到项目的 Jar 包中的做法,为 maven-shade-plugin 带来了另一个重要的功能 <重命名>,因为将依赖的 Jar 包解压后都是以文件形式存在,所以 maven-shade-plugin 支持对对依赖的某个具体文件进行重命名,maven-shade-plugin 在重命名时,不只是将文件名字修改,连我们项目中对其引用的地方都会一同修改

重命名的做法可以避免版本冲突,想详细了解的可以参考我 maven-shade-plugin 的文章

spring-boot-maven-plugin

spring-boot-maven-plugin 是 Spring 提供的一个 Maven 打包插件,可以通过 maven 的插件命令运行,但是一般习惯将它与 maven 生命周期绑定,然后通过 maven 生命周期命令运行,它的特点是可以将项目中依赖的 Jar 包添加到最终生成的项目 Jar 包中

spring-boot-maven-plugin 主要是对 maven-jar-plugin 生成的项目 Jar 包进行二次打包,并将项目依赖的 Jar 包添加进项目的 Jar 包中

maven-jar-plugin

maven 生命周期中 package 阶段的默认插件,不管是否在 pom.xml 中主动声明,也不管是否有其他的 package 阶段插件被绑定,其在 package 阶段都会被最先执行

使用 maven-jar-plugin 打包时,不会将依赖的 Jar 包添加到生成的项目 Jar 包中,所以当项目中使用依赖时,需要自己准备依赖的 Jar 包,这样 maven-jar-plugin 打出的项目 Jar 包才能被成功运行

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

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

相关文章

BetaFlight飞控启动运行过程简介疑问跟踪

BetaFlight飞控启动&运行过程简介疑问跟踪 1. 源由2. 【已解存疑】问题一&#xff1a;6.1 Why desiredPeriodCycles is so important to Betaflight task?3. 【已解】问题二&#xff1a;6.2 What root cause has made gyro task to been overrun, so scheduler has to ski…

轮式机械臂小车实现语音控制

1. 功能说明 本文实例将实现语音控制R214e样机运动&#xff08;前进、后退、左转、右转、拿起、放下&#xff09;的功能。 2. 电子硬件 在这个示例中&#xff0c;我们采用了以下硬件&#xff0c;请大家参考&#xff1a; 主控板 Basra主控板&#xff08;兼容Arduino Uno&#x…

港科夜闻|推进湾区产学研融合发展,香港科大(广州)—广州市属国企校企合作专题交流会圆满举行...

关注并星标 每周阅读港科夜闻 建立新视野 开启新思维 1、推进湾区产学研融合发展&#xff0c;香港科大(广州)—广州市属国企校企合作专题交流会圆满举行。本次交流会由香港科大(广州)、广州市人民政府国有资产监督管理委员会主办&#xff0c;越秀集团协办&#xff0c;旨在充分发…

springboot+jsp网上图书商城销售系统java

开发环境 开发语言&#xff1a;Java 框架&#xff1a;springboot 技术&#xff1a;JSP JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven…

ubuntu 20.04虚拟内存

ubuntu 20.04虚拟内存 1. 查看自己的虚拟内存 使用top命令或者使用free命令 2. 创建虚拟内存配置文件 # 新建文件夹 cd ~ mkdir swap cd swap# bs 为块的大小&#xff0c;count 创建多少个块 sudo dd if/dev/zero ofswapfile bs1M count2048# 修改权限 sudo chmod 0600 …

系统分析师:六、企业信息化战略与实施 练习题

目录 1、十二五的七大国家战略新兴产业体系 2、标准化的三个方面 3、信息的属性 4、电子政务描述 5、数据产品管理 6、企业门户 7、企业应用集成 8、电子商务标准体系 9、电子商务标准范畴 10、知识管理显性和隐形知识 11、决策支持系统 12、CRM解决方案 13、CRM核…

用Vue写教务系统学生管理

文章目录 一.首先创建新的Demo二.在APP里面绑定DemoStudent三.源码附上四.效果图&#xff08;新增记录还未实现&#xff09; 一.首先创建新的Demo 二.在APP里面绑定DemoStudent <template><img alt"Vue logo" src"./assets/logo.png"><!--…

华为OD机试真题 Java 实现【最小的调整次数】【2023Q1 100分】

一、题目描述 有一个特异性的双端队列&#xff0c;该队列可以从头部或尾部添加数据&#xff0c;但是只能从头部移出数据。 小A依次执行2n个指令往队列中添加数据和移出数据。 其中n个指令是添加数据 (可能从头部添加、也可能从尾部添加)&#xff0c;依次添加1到n&#xff0c…

超稳定ChatGPT镜像网站,小白适用,赶紧收藏【持续更新中】

&#x1f482;作者简介&#xff1a; THUNDER王&#xff0c;一名热爱财税和SAP ABAP编程以及热爱分享的博主。目前于江西师范大学会计学专业大二本科在读&#xff0c;同时任汉硕云&#xff08;广东&#xff09;科技有限公司ABAP开发顾问。在学习工作中&#xff0c;我通常使用偏后…

无代码开发引路,重塑企业数字生产力

互联网时代&#xff0c;信息的传递与共享成为企业运营的核心环节。传统的软件开发模式已经不能满足企业数字化转型的需要&#xff0c;而无代码开发平台可以在不改变底层技术架构的前提下&#xff0c;提供一种高效、低成本、灵活、易用的软件开发解决方案。相较于传统代码编写&a…

[山海关crypto 训练营 day13]

日常鼓励自己&#xff1a;世界上只有想不通的人&#xff0c;没有走不通的路。 [长安杯 2021]checkin 题目代码和数据 from Crypto.Util.number import * from secret import flag p getPrime(1024) q getPrime(16) n p*q m bytes_to_long(flag) for i in range(1,p-q):m…

事务AOP

事务&AOP 事务管理 在数据库中我们已经学过事务了。 事务是一组操作的集合&#xff0c;它是不可再分的工作单位。事务会把所有的操作作为一个整体&#xff0c;一起向数据库提交或者撤销操作请求。所以这组操作要么是同时成功&#xff0c;要么同时失败。 事务的操作主要…

【共用体和枚举】

共用体 一种构造类型的数据结构 共用体和结构体类似&#xff0c;也是一种构造类型的数据结构。 既然是构造类型的&#xff0c;就需要先定义出类型&#xff0c;然后用类型定义变量。 定义共用体类型的方法和结构体非常相似&#xff0c;把struct 改成union 就可以了。 在进行某…

美颜SDK的应用与优化:一次全面探究

在直播过程中&#xff0c;美颜技术可以帮助主播实现更好的视觉效果&#xff0c;从而吸引更多的关注和粉丝。因此&#xff0c;直播美颜SDK的应用和优化已经成为了直播行业中的一个重要研究方向。 一、直播美颜SDK的应用 1. 美颜滤镜 美颜滤镜是直播美颜SDK中最常用的一种技术…

【Mybatis】Mybatis处理一对多、多对多关系映射-四

唠嗑部分 上篇文章我们说了Mybatis中ORM映射的几种方式&#xff0c;相关文章&#xff1a; 【Mybatis】简单入门及工具类封装-一 【Mybatis】如何实现ORM映射-二 【Mybatis】Mybatis的动态SQL、缓存机制-三 这篇文章我们来说说Mybatis如何处理一对一、一对多、多对多的关系映射…

Linux下网络编程(2)——socket的函数们

accept()函数 服务器调用 listen()函数之后&#xff0c;就会进入到监听状态&#xff0c;等待客户端的连接请求&#xff0c;使用 accept()函数获取客户端的连接请求并建立连接。函数原型如下所示&#xff1a; int accept(int sockfd, struct sockaddr *addr, socklen_t *addrle…

RK3588光电载荷处理板研制进展

本来就是一个很小众的市场&#xff0c;但是偶尔也会有同行询问&#xff0c;这儿就简单汇报一下后期的进展 板子已经开发完成&#xff0c;并有幸得到了两个订单&#xff0c;虽然量不是很大&#xff0c;但是也很开心由于一段时间的努力和付出&#xff0c;将该设备应用在了国防事业…

【Linux】Linux文件目录结构

Linux文件目录结构 在 Linux 中&#xff0c;其文件目录结构是一颗类似于多叉树的结构&#xff0c;所有目录都在 / &#xff08;根目录&#xff09;下面&#xff0c;每个非叶节点代表一个目录&#xff0c;叶节点代表文件。 一般结构如下所示&#xff1a; usr :“Unix Software …

LitCTF 2023 - crypto复现

文章目录 Hex&#xff1f;Hex&#xff01;梦想是红色的原来你也玩原神factordbP_Leake的学问Euler* Where is P?The same common divisormd5babyLCG* easy_math* Virginia* Is this only base?你是我的关键词(Keyworld)隐晦的聊天记录* baby_xor收获与体会 Hex&#xff1f;He…

RabbitMQ入门 安装 SpringAMQP简单队列、工作队列、发布订阅(扇出模式,广播模式)、Direct模式(Roting模式)、Topic模式

一、RabbitMQ介绍 1. MQ介绍 1. 为什么要有MQ 同步调用与异步调用 程序里所有的通信&#xff0c;有两种形式&#xff1a; 同步通信&#xff1a;通信时必须得到结果才会执行下一步 异步通信&#xff1a;通信时不必等待结果&#xff0c;可以直接处理下一步 同步调用 解析&…