Maven 的打包方式

article/2025/9/19 15:05:41

一、前言

刚开始实习不到一个月的时候,从师兄手中接手了团队的项目,当时第一次听到了 “大包”、“小包” 的概念,只见师兄一顿操作,使用 Maven 将项目进行了打包。当时不太理解,只是记得两点:

  1. 如果想让项目作为一个依赖提供给他人使用,则将项目打为 “小包”
  2. 如果希望项目打出来 Jar 包可以作为一个独立服务运行,则将项目打为 “大包”

也就是说,可以将项目打包为两类:一类是作为依赖提供给他人使用,一类是作为独立服务使用。

下面将从这两类来讲解使用 Maven 将项目打包的方式。

二、将项目打为小包

所谓打包为 “小包”,其实就是仅打包项目中的代码到 JAR 包中,不打包依赖包

将项目打包为 “小包” 的做法很简单,就是在 pom.xml 文件中的 plugins 标签给注释掉即可,也就是不做任何打包插件的配置。如下图所示:

<build><!-- 项目最终打包成的名字 --><finalName>community</finalName>
</build>

然后执行打包命令:

# 先执行 clean,再执行 package,并且跳过测试用例的执行
mvn clean package -Dmaven.test.skip=true

最后,会在当前项目的 target 目录生成一个 “小包”:

在这里插入图片描述

使用反编译工具查看 Jar 包具体内容:

在这里插入图片描述

从 Jar 包内容可以看出来,项目的依赖都以 Maven 依赖的形式保存在了 pom.xml 文件中,源码的部分只有项目本身的代码。这种 Jar 包就是所谓的 “小包”。

三、将项目打包为大包

如果想要打包后的 Jar 包能够作为一个独立服务运行,必须满足以下两点:

  1. 在 Jar 包中的 META-INF/MANIFEST.MF 中指定 Main-Class,这样才能确定程序的入口在哪里;

  2. 要能加载到依赖包。

所以我们将项目打包为大包的中心思想也就是实现上面两点。使用 Maven 将项目打包为大包的方式就比较多了。主要有以下三种方式:

  • 方法一:使用 maven-jar-pluginmaven-dependency-plugin 插件
  • 方法二:使用 maven-assembly-plugin 插件
  • 方法三:使用 maven-shade-plugin 插件

注意:对于 SpringBoot 项目,在初始的 pom.xml 文件中就提供了 spring-boot-maven-plugin 插件用于将项目打包为可执行 Jar 包,不建议再使用其他任何插件(包括下面的三种插件)打包。

方法一:使用 maven-jar-pluginmaven-dependency-plugin 插件

此种打包方式有一个比较明显的缺点:打包后会在 target 目录下生成 lib 目录(存放依赖 Jar)和项目 Jar。也就是说由于依赖都存在于 lib 目录中,所以要想运行 Jar 包,必须将 Jar 包和 lib 目录放在同一个路径下。

pom.xml 文件中关于打包的配置信息如下:

<build><!-- 项目最终打包成的名字 --><finalName>community</finalName><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-jar-plugin</artifactId><configuration><archive><manifest><!-- 会在 MANIFEST.MF 中生成 Class-Path 项 --><!-- 系统会根据 Class-Path 项配置的路径加载依赖 --><addClasspath>true</addClasspath><!-- 指定依赖包所在目录,相对于项目最终 Jar 包的路径 --><classpathPrefix>lib/</classpathPrefix><!-- 指定 MainClass --><mainClass>com.ronz.community.CommunityApplication</mainClass></manifest></archive></configuration></plugin><!-- 配置依赖包 --><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-dependency-plugin</artifactId><!-- 相当于执行 mvn 命令,将依赖打包到指定目录 --><executions><execution><id>copy-dependencies</id><phase>package</phase><goals><goal>copy-dependencies</goal></goals><configuration><!--将依赖打包至 target 下的 lib 目录--><outputDirectory>${project.build.directory}/lib</outputDirectory></configuration></execution></executions></plugin></plugins>
</build>

这个思想也比较简单。

首先说 maven-jar-plugin 插件,它的思想就是:指定启动类、指定依赖包相对于项目最终 Jar 包所在的路径、给 MANIFEST.MF 文件添加 Class-Path 属性(运行项目 Jar 包时会根据 Class-Path 属性来找到具体依赖 Jar 包的路径)。

接着是 maven-dependency-plugin 插件,它的主要思想就是:指定所有依赖被打包为 Jar 包后的存放路径。

pom.xml 文件配置完毕之后,就可以运行打包命令了:

# 跳过测试用例执行 package 命令
mvn package -Dmaven.test.skip=true

打包完成后,会在项目的 target 目录下生成 lib 文件夹(存放项目的所有依赖)和项目的 Jar 包:

在这里插入图片描述

为了增强对这种方法的认识,通过反编译工具查看编译后生成的 community.jar 包,其内容如下:

在这里插入图片描述

可以看到,在 MANIFEST.MF 文件中生成了 Class-Path 属性,该属性的值是当前项目的所有依赖 Jar 包的路径(即 lib 目录中的 Jar 包);当然还有 MainClass 属性,由于图片尺寸的原因,没有截到。

这种方式打包出来的 Jar 包,在代码层面只包含了项目本身的代码。而项目的依赖都以 Jar 包的形式放在了项目 Jar 包同级别目录下的 lib 目录中,这些依赖 Jar 包的路径在 MANIFEST.MF 文件中都以路径的方式指明了。

方法二:使用 maven-assembly-plugin 插件

使用 maven-assembly-plugin 插件打出来的包只有一个 Jar 包,这个 Jar 包中包含了项目代码以及依赖的代码。也就意味着此种方式打出来的 Jar 包可以直接通过 java -jar xxx.jar 的命令来运行。

pom.xml 文件中关于打包的配置信息如下:

    <build><!-- 项目最终打包成的名字 --><finalName>community</finalName><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-assembly-plugin</artifactId><configuration><archive><!-- 指定启动类 --><manifest><mainClass>com.ronz.community.CommunityApplication</mainClass></manifest></archive><!-- 指定启动类 --><descriptorRefs><descriptorRef>jar-with-dependencies</descriptorRef></descriptorRefs></configuration><!-- 相当于在执行 package 打包时,在后面加上 assembly:single  --><executions><execution><id>make-assembly</id><phase>package</phase><goals><goal>single</goal></goals></execution></executions></plugin></plugins></build>

这个插件的使用思想也比较简单:首先还是指定启动类;然后配置描述符参数,这个是插件提供的预置参数,不用更改;接下来就是打包时追加的命令了。

然后执行 Maven 打包命令:

# 先后执行 clean、package,并跳过测试文件的执行
mvn clean package -Dmaven.test.skip=true

打包完成之后,会在 target 目录下生成一个 Jar 包:

在这里插入图片描述

使用反编译工具查看 Jar 包:

在这里插入图片描述

从上图中可以清楚的看到:项目的所有依赖都以源码文件的形式整合在了一起

方法三:使用 maven-shade-plugin 插件(推荐使用)

根据 Maven 的官方文档介绍,maven-shade-plugin 是一个强大的打包插件。它同样可以将项目的依赖以及项目的源码打包成一个可执行 Jar 包

pom.xml 文件中关于打包的配置信息如下:

<build><!-- 项目最终打包成的名字 --><finalName>community</finalName><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-shade-plugin</artifactId><version>3.2.4</version><executions><execution><phase>package</phase><goals><goal>shade</goal></goals><configuration><transformers><!-- 指定启动类 --><transformerimplementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"><mainClass>com.ronz.community.CommunityApplication</mainClass></transformer><!-- 下面的配置仅针对存在同名资源文件的情况,如没有则不用配置--><!-- 有些项目包可能会包含同文件名的资源文件(例如属性文件)--><!-- 为避免覆盖,可以将它们的内容合并到一个文件中 --><transformerimplementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"><resource>META-INF/spring.handlers</resource></transformer><transformerimplementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"><resource>META-INF/spring.schemas</resource></transformer></transformers></configuration></execution></executions></plugin></plugins>
</build>

这个配置思想很简单:首先指定执行 package 命令时附加命令,这个是固定的,不需要改变;接下来就是指定项目的启动类;然后就是配置一个文件合并操作,主要是因为有的项目可能会有重名的资源文件,为了避免前面的资源文件被后面的覆盖掉,可以将重名的配置文件合并为一个文件,对于无重名资源文件的项目则无需配置。

执行 Maven 打包命令:

# 先后执行 clean、package,并跳过测试文件的执行
mvn clean package -Dmaven.test.skip=true

打包完成之后,会在 target 目录下生成一个 Jar 包,此 Jar 包中也是将项目依赖的源码包含进来了,可以通过 java -jar xxx.jar 命令直接运行 Jar 包。

四、总结

经过我的实际测试, 使用第二种方式的 assembly 打包出来的 Jar 包多多少少有些问题,但是使用第三种方式打包出来的 Jar 包一般都是可用的。所以在将项目打包为大包时,还是推荐使用第三种打包的方式

参考

  • Apache Maven Shade Plugin
  • Apache Maven Assembly Plugin
  • maven 打包项目的几种方式

http://chatgpt.dhexx.cn/article/LIy03WuR.shtml

相关文章

Java中main()方法的使用

一、main()方法的使用说明 main()方法作为程序的入口。main()方法也是一个普通的静态方法。main()方法可以作为我们与控制台交互的方式&#xff08;之前&#xff1a;使用Scanner&#xff09;。 二、main()方法使用举例 MainDemo.java public class MainDemo {public static…

JAVA中main方法如何执行。

public class Test{public static void main(String[] args){System.out.println("hello world");}} 上述就是java代码中的一段简单的main函数的代码&#xff0c;代码执行后输出结果为&#xff1a;hello world main函数&#xff1a; 在java中main函数是一个特殊的…

深入理解Java的main方法

一、简单介绍 在我们的Java程序中都会出现一个名称为main的方法&#xff0c;我们发现没有这个方法我们的程序就无法运行。 其实任何一个Java程序的运行入口都是这个main方法&#xff0c;也就是说&#xff0c;程序在运行的时候&#xff0c;第一个执行的方法就是main()方法&#…

main方法详解

学习Java有一段时间了&#xff0c;一直没用过博客来写写自己在学习过程中遇到的问题和学习心得&#xff0c;看到别人在诸如博客这样的社区写一些总结觉得挺好的。索性&#xff0c;今天开始自己也用起博客记录学习。一来呢&#xff0c;能更好的总结学习知识。二来&#xff0c;有…

java中main方法的作用

main方法是我们学习Java语言学习的第一个方法&#xff0c;也是每个java使用者最熟悉的方法,每个Java应用程序都必须有且仅有一个main方法。在eclipse里可以使用输入main&#xff0c;在按住Alt/的方式快速创建main方法。可以说main方法是最简单的方法&#xff0c;因为main方法几…

Java中的main()方法如何运用?

​ 在Java中&#xff0c;main()方法是Java应用程序的入口方法&#xff0c;也就是说&#xff0c;程序在运行的时候&#xff0c;第一个执行的方法就是main()方法&#xff0c;这个方法和其他的方法有很大的不同&#xff0c;比如方法的名字必须是main&#xff0c;方法必须是public …

解析java中的main方法

解析java中的main方法 1.一个java源文件(.java文件)可以有多个class类 1.1 分析 例如下面代码编译时不会报错,只不过编译后会生成多个对应的class类文件 其中有多少个类就生成class类文件 1.2 示例代码 class B{} public class Practice {public static void main(String[…

Java main方法_解释Java中的main方法,及其作用_一个java文件中可包含多个main方法

public static void main(String[] args) {}或者 public static void main(String args[]) {}main方法是我们学习Java语言学习的第一个方法&#xff0c;也是每个java使用者最熟悉的方法,每个Java应用程序都必须有且仅有一个main方法。在eclipse里可以使用输入main&#xff0c;…

Java 中的main方法原理介绍。

//深入了解main方法 // 解释main方法的形式为什么是这样写的&#xff1f; // public static void main(String[] args) // 问题1&#xff1a;是谁调用main方法&#xff1f; // java虚拟机调用main方法&#xff0c;所以main的访问权限是public // 问题2&#xff1a;为什么是st…

main()方法java

main()方法java 实际上&#xff0c;程序中的main()方法不是必须要放在唯一的一个公共类中的&#xff0c;它可以放在其它类当中&#xff0c;但正常情况下这样做会报错&#xff0c;这是为什么呢&#xff1f;是因为程序在执行的过程中第一步是调用&#xff1a; 文件名.main() 正常…

Java--main()方法

文章目录 一、main()方法使用二、mian()方法调用 一、main()方法使用 1、访问控制权限是公有的&#xff08;public&#xff09; 2、main() 方法是静态的。如果要在 main() 方法中调用本类中的其他方法&#xff0c;则该方法也必须是静态的&#xff0c;否则需要先创建本类的实例…

java学习之main方法

目录 一、main方法的注意事项 二、在IDEA中传入参数 一、main方法的注意事项 形式&#xff1a;public static void main(String[] args){}&#xff0c;main方法是一个静态方法&#xff0c;访问修饰符是&#xff1a;public&#xff0c;形参是String数组 args 注意事项&#xf…

深入理解 main 方法

目录 深入理解 main 方法 public static void main(String[] args) { }注意事项 深入理解 main 方法 public static void main(String[] args) { } 在Java 中&#xff0c;main() 方法是 Java 应用程序的入口方法&#xff0c;程序在运行的时候&#xff0c;第一个执行的方法就是…

java中main()方法详解

package javase2; /** public static void main(String [] args){....}* pblic&#xff1a;公共的&#xff0c;访问权限最大&#xff0c;由于main要被jvm调用&#xff0c;所以要权限够大* static&#xff1a;静态不需要创建对象&#xff0c;方便jvm调用* void&#xff1a;方法的…

Java中的main方法

main方法是java应用程序中的一个入口&#xff0c;它被声明为公有静态方法&#xff0c;参数是一个字符串数组&#xff0c;返回值为void类型。 一个java文件可以有无数个main方法&#xff0c;但是一个类中只能有一个main方法&#xff0c;当想要运行不同的main时&#xff0c;可以…

Java的main方法

Java的main方法 我们在编写程序的时候经常使用到main方法&#xff0c;但是main方法具体是什么意思呢&#xff1f; 1、main方法的调用者是java虚拟机。 2、因为调用者是java虚拟机&#xff0c;java虚拟机不在该类中也不在该类所在的包中更不是该类的子类&#xff0c;所以main函数…

Java基础——main方法

深入理解main方法 解释main方法的形式&#xff1a;public static void main(String [] args){} main方法是由java虚拟机调用的。java虚拟机需要调用类的main()方法&#xff0c;所以该方法的访问权限必须是public。Java虚拟机在执行main()方法时不必创建对象&#xff0c;所以该…

详解Java中的main方法

main方法格式定义 public: main方法启动时由JVM进行加载&#xff0c;public的可访问权限是最高的&#xff0c;所有需要声明为public;static: 方法的调用要么是通过对象&#xff0c;要么是通过类&#xff0c;而main方法的话&#xff0c;因为是由虚拟机调用的&#xff0c;所以无…

java中类的main方法总结

一、java中每个类都需要有main方法吗&#xff1f; 每个类可以有也可以没有main方法&#xff0c; 甚至所有类里可以都没有main方法。如果你想从某个类做为入口开始运行整个程序。那么就把他设成 public &#xff0c;之后再里面写个main方法作为入口。 每个项目都要有一个主类…

main方法

main方法 一、main()方法的使用说明1. main()方法作为程序的入口2.main&#xff08;&#xff09;方法也是一个普通的静态方法3.main&#xff08;&#xff09;方法可以作为我们与控制台交互的方式&#xff0c;&#xff08;之前&#xff0c;使用Scanner&#xff09; 二、main方法…