mvn package
本章承接
mvn test,进入 default 生命周期的打包阶段。理解mvn package的作用和产物生成机制,是掌握 Maven 构建"最终交付物"的关键——JAR/WAR 就是项目的可运行产物。
核心机制
package 阶段将编译后的代码打包为可分发格式,如 JAR(Java Archive,普通 Java 项目)或 WAR(Web Application Archive,Web 项目)。
注意这里的"可分发格式"——package 的产物是可以脱离源码、独立部署的文件。JAR 可以直接用 java -jar 运行(如果是可执行 JAR),WAR 可以直接丢进 Tomcat 部署。
package 阶段在生命周期中的位置
default 生命周期的相关阶段链:
... → compile → test-compile → test → package → verify → install → deploy
执行 mvn package 时,Maven 会自动依次执行从 validate 到 package 的所有前置阶段。也就是说,mvn package 隐式包含了 compile 和 test。
谁在做真正的打包工作?
打包工作由与 <packaging> 类型对应的插件完成:
| packaging 类型 | 默认绑定的插件 | 产物 |
|---|---|---|
jar | maven-jar-plugin | .jar 文件 |
war | maven-war-plugin | .war 文件 |
pom | 无 | 无(多模块项目的父项目) |
maven-plugin | maven-plugin-plugin | Maven 插件包 |
默认情况下,普通 Java 项目的 <packaging> 是 jar,因此 mvn package 会调用 maven-jar-plugin 将 target/classes/ 中的内容打包为 JAR。
JAR 包里有什么?
一个标准的 Maven JAR 包内部结构:
employee-system-1.0.0.jar
├── META-INF/
│ ├── MANIFEST.MF ← 清单文件(包含版本、主类等信息)
│ └── maven/ ← Maven 元数据
│ └── com.feixiang/
│ └── employee-system/
│ ├── pom.xml
│ └── pom.properties
└── com/feixiang/ ← 编译后的 .class 文件
├── service/EmployeeService.class
├── model/Employee.class
└── ...
注意:JAR 包不包含 pom.xml 中声明的依赖 JAR。运行时需要通过 classpath 或可执行 JAR 的 Class-Path 清单项引用依赖。
生活类比:产品装箱
想象一家电子产品工厂:
compile阶段是生产电路板test阶段是质检,确保每块电路板功能正常package阶段是装箱:把电路板、说明书、保修卡装进一个包装盒(JAR),贴上标签(MANIFEST.MF),封箱待发货- 这个包装盒就是最终交给物流(运维李眉)的"可分发产物"
图示
上图展示了 mvn package 的核心流程:将 target/classes 中的编译产物和资源文件,连同生成的清单文件,压缩为单个 JAR 或 WAR 文件。packaging 类型决定了使用哪个插件和生成哪种格式的产物。
完整示例
场景
飞翔科技的 employee-system 项目开发完成,CTO 大翔要求小崔打包一个可部署的 JAR 文件,交给运维李眉部署到测试环境。
操作前:项目状态
pom.xml 中的关键配置:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.feixiang</groupId>
<artifactId>employee-system</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging> <!-- 默认就是 jar,可省略 -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>3.2.0</version>
</plugin>
</plugins>
</build>
</project>
操作步骤
步骤一:执行打包
mvn clean package
步骤二:观察输出
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------< com.feixiang:employee-system >------------------
[INFO] Building employee-system 1.0.0
[INFO] from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
...
[INFO] --- compiler:3.11.0:compile (default-compile) @ employee-system ---
[INFO] Compiling 5 source files to target\classes
[INFO]
[INFO] --- surefire:3.1.2:test (default-test) @ employee-system ---
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] --- jar:3.3.0:jar (default-jar) @ employee-system ---
[INFO] Building jar: C:\Users\xiaocui\workspace\employee-system\target\employee-system-1.0.0.jar
[INFO]
[INFO] --- spring-boot:3.2.0:repackage (repackage) @ employee-system ---
[INFO] Replacing main artifact with repackaged archive
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
关键输出解读:
jar:3.3.0:jar——maven-jar-plugin生成基础 JARspring-boot:3.2.0:repackage—— Spring Boot 插件将基础 JAR 重新打包为可执行 FAT JAR(包含所有依赖)target\employee-system-1.0.0.jar—— 最终产物路径
package 前后的产物对比
package 前(仅 compile 后):
target/
├── classes/
│ ├── com/feixiang/...(.class 文件)
│ └── application.properties
└── test-classes/
└── com/feixiang/...(测试 .class 文件)
package 后:
target/
├── classes/ ← 编译产物(仍在)
├── test-classes/ ← 测试编译产物(仍在)
├── surefire-reports/ ← 测试报告(仍在)
├── employee-system-1.0.0.jar ← ✅ 新生成的 JAR 包
├── employee-system-1.0.0.jar.original ← Spring Boot 原始 JAR(备份)
└── maven-archiver/
└── pom.properties
跳过测试打包
在某些场景下(如快速验证打包流程、测试环境临时构建),可能需要跳过测试直接打包:
# 方式一:跳过测试执行(不编译也不运行测试)
mvn clean package -DskipTests
# 方式二:跳过测试运行(编译测试但不执行)
mvn clean package -Dmaven.test.skip=true
两者的区别:
| 参数 | 测试代码编译 | 测试执行 | 使用场景 |
|---|---|---|---|
-DskipTests | ✅ 编译 | ❌ 不执行 | 需要测试类存在,但暂时不跑 |
-Dmaven.test.skip=true | ❌ 不编译 | ❌ 不执行 | 完全不需要测试,节省编译时间 |
注意:CTO 大翔明确规定,生产环境构建严禁跳过测试。-DskipTests 只允许在本地快速验证时使用。
易错点与常见问题
误区一:package 产物包含所有依赖
错误认知:"我执行 mvn package 生成的 JAR,直接 java -jar 就能运行,因为它包含了所有依赖。"
纠正:标准 Maven JAR 不包含依赖。maven-jar-plugin 生成的 JAR 只包含项目自身的 .class 文件和资源。要运行它,必须手动指定 classpath:
# 标准 JAR 需要指定 classpath
java -cp "target/employee-system-1.0.0.jar:lib/*" com.feixiang.EmployeeSystemApplication
如果你需要"FAT JAR"(包含所有依赖的可执行 JAR),需要使用额外插件:
- Spring Boot 项目:
spring-boot-maven-plugin会自动生成 FAT JAR - 普通 Java 项目:使用
maven-shade-plugin或maven-assembly-plugin
误区二:package 会安装到本地仓库
错误认知:"我执行了 mvn package,其他项目就能引用这个 JAR 了。"
纠正:mvn package 只生成 JAR 到 target/ 目录,不会安装到本地仓库(~/.m2/repository)。要让其他项目引用,需要执行 mvn install,这将在下一章详细讲解。
误区三:WAR 包和 JAR 包可以随意切换
错误认知:"我把 pom.xml 里的 <packaging> 从 jar 改成 war,项目就能直接部署到 Tomcat 了。"
纠正:WAR 包需要项目具备 Web 应用结构(如 src/main/webapp/WEB-INF/web.xml),并且通常需要继承 spring-boot-starter-parent 或配置 maven-war-plugin。单纯改 packaging 标签,可能导致打包产物缺少必要的 Web 资源,部署时报错。
误区四:package 产物名可以随意改
典型问题:小崔希望生成的 JAR 文件名不包含版本号,方便脚本引用。
纠正:可以通过 maven-jar-plugin 的 <finalName> 配置自定义产物名:
<build>
<finalName>employee-system</finalName>
</build>
这样生成的产物将是 target/employee-system.jar,而不是 target/employee-system-1.0.0.jar。
小结
mvn package 是 Maven 构建流程中生成可分发产物的核心命令。它将编译和测试通过的代码打包为 JAR 或 WAR 文件。核心要点:
mvn package隐式执行compile和test,确保产物质量- packaging 类型决定产物格式:
jar→ JAR,war→ WAR - 标准 JAR 不包含依赖,需要额外插件生成 FAT JAR
-DskipTests和-Dmaven.test.skip=true可跳过测试,但生产环境严禁使用package不安装到本地仓库,其他项目无法直接引用
本章与全局的关系:本章讲解了打包命令。下一章 mvn install 将展示如何将打包产物安装到本地仓库,供其他项目依赖使用。