夜雨聆风学习资料网

ARTICLE · 1154781

Spring Boot Maven 插件目标集合:spring-boot:run / repackage / build-image 等 goal 的语义与参数速查

Spring Boot Maven 插件目标集合:spring-boot:run / repackage / build-image 等 goal 的语义与参数速查

为什么 mvn package 打出来的 jar 能 java -jar 直接跑,而裸 Maven 打的 jar 不行?秘密全在这个插件里。

很多人用了很久 Spring Boot,却从未单独留意过 spring-boot-maven-plugin 的存在。直到某天 CI 报了一个"主清单属性缺失 / Main-Class not found / No main manifest attribute"的错误,才第一次意识到——原来那个能双击跑起来的 jar,不是 Maven 打包的默认产物,而是这个插件额外动过手脚的。它就是让"普通 jar 变 FAT jar(可执行 jar)"的幕后推手。

这篇是这个 Spring Boot 知识点汇总手册"命令集合篇"里专门讲 Spring Boot Maven 插件的一辑。H01 讲了生命周期和 Maven 命令,H03 讲的是插件的 goal(目标)——spring-boot:run、spring-boot:repackage、spring-boot:build-image 这些。这三者是 Spring Boot 工程真正天天接触的插件入口。定位同样是速查页,收藏它,看 goal 语义、查参数、避坑,一次到位。版本以 Spring Boot 3.x 为准。

禁止事项:以下每个 goal、每个参数都是真实存在的,不编造不存在的 goal(比如没有 spring-boot:deploy 这种目标)。goal 的名词和参数以 Maven 约定 + Spring Boot 官方 goal 为准。


一、这个插件到底是什么

spring-boot-maven-plugin 是 Spring Boot 官方提供的 Maven 插件,它的核心工作是"接管 Maven 打出来的普通 jar,把它重构成一个可以直接 java -jar 运行的可执行 FAT jar(内含应用代码、所有依赖、内嵌 Tomcat/Jetty)",并附带提供了本地运行、生成容器镜像等目标。

它的坐标:

<plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>3.3.x</version></plugin>

关键事实:在 Spring Boot 的 parent(spring-boot-starter-parent)里,这个插件已经被默认声明并配置好了,你新建的 Spring Initializr 项目通常不需要手动加 <plugin>,只需要在 <build><plugins> 里显式引一次(一般带 <configuration> 或自定义)。但只要用到它提供的 goal(尤其是 repackage),最好在项目里显式引用插件声明,并把插件放在 <plugins> 里,否则某些自定义会不生效。

这个插件到底提供哪些可用的 goal,直接 mvn org.springframework.boot:spring-boot-maven-plugin:help 或看 help:describe 可列全。本节末尾会放一张"goal → 功能 → 典型命令"总表,正文按常用度展开讲。

为什么要理解这个插件的 goal,而不是只知道命令

很多学习资料把 mvn spring-boot:run、mvn package 当命令背,但如果不理解"这是插件的 goal",碰到下面这些情况就会很无助:

  • 报错信息里出现 RepackageMojo、RunMojo 这类类名,你没法把它和"哪个 goal"对上;
  • 想给 repackage 加一个 classifier、给 run 指定 profile,却不知道参数应该挂在哪个 goal 下;
  • 想知道"为什么 package 会自动多打一个可执行 jar",你不明白是哪个 goal 绑到了 package 阶段。

所以本篇的讲解口径是"以 goal 为单元":每个 goal 给出了它的功能、可配参数、注意事项、典型命令。这样你不仅能"背出命令",还能在报错/加需求时自己反推"这是哪个 goal 在干活,参数应该怎么挂"。这是一种比记命令更能复用、更能扛住线上排查的知识组织方式。

插件里的 goal 大致分三类,先有个全局地图

把这个插件的 goal 按用途粗分三堆,记忆负担就小很多:

  1. 开发/构建类:run(本地跑)、repackage(打可执行包)、build-image(出镜像)、build-info(写构建元数据)。
  2. 辅助/诊断类:help(列帮助)、start/stop(集成测试时启停进程)。
  3. AOT/原生类:process-aot、process-test-aot,走 GraalVM 原生镜像是会用到。

这三类合起来就是这个插件的全部面孔。日常高频只用前两类里的前三个(run、repackage、build-image),其余遇到再查。有了这张全局地图,往下逐条看就不会显得散。


二、spring-boot:run —— 本地跑起来

这是开发期最高频的 goal。它等价于"把你写好的 Application 主类直接跑起来",不必先打包成 jar。

# 最基本的本地启动mvn spring-boot:run# 把端口和启动参数用 -Dspring-boot.run.arguments 传进去mvn spring-boot:run -Dspring-boot.run.arguments="--server.port=8081"# 带 Maven profilemvn spring-boot:run -Dspring-boot.run.profiles=dev# 同时给程序传参数和指定 Spring profilemvn spring-boot:run -Dspring-boot.run.arguments="--server.port=8081 --spring.datasource.url=jdbc:mysql://127.0.0.1:3306/db"

功能拆解

  • spring-boot:run 默认继承测试/编译产物,直接找主类(@SpringBootApplication 所在类)并启动内嵌容器。适合本地开发、联调、验证配置。
  • 整个过程是"前台"的,进程不退出,会占用当前终端,Ctrl+C 停掉。

主要可选参数(都以 -D 前缀给):

参数
作用
示例
-Dspring-boot.run.profiles
指定运行的 Spring profile(注意:不是 -Dspring.profiles.active)
-Dspring-boot.run.profiles=dev
-Dspring-boot.run.arguments
传给应用 main 的启动参数(多个参数空格分隔)
--server.port=8081 --active=dev
-Dspring-boot.run.jvmArguments
传给 JVM 的额外参数
-Xmx512m
-Dspring-boot.run.main-class
手动指定 main 类
com.example.MyMain

注意点

  1. Spring profile 用 -Dspring-boot.run.profiles 传,不要用 -Dspring.profiles.active——后者在 spring-boot:run 场景下经常不生效,因为它对应的是运行期参数,而这个 goal 的转发渠道是 run.arguments。想用环境变量或者想让 profile 起效,-Dspring-boot.run.profiles=dev 是最直觉可靠的。
  2. 想热更新,配合 devtools;spring-boot:run 本身不改代码重跑,改代码要重启进程(除非配了 devtools 自动重启)。
  3. 当项目里跑了多个 Spring Boot 应用(微服务本地开发),记得用 server.port 区分,别互相占端口。

spring-boot:run 的参数其实是一组前缀一致的家族

run 的可配参数有一个共同的命名规律:几乎都以 -Dspring-boot.run.* 开头。摸清这个规律,即使没背全某个具体参数,也能猜着写。核心的几个:

参数(-D 前缀)
含义
典型值
spring-boot.run.profiles
运行的 Spring profile
dev
spring-boot.run.arguments
传给应用 main 的参数
--server.port=8081
spring-boot.run.jvmArguments
传给 JVM 的参数
-Xmx512m -Dfile.encoding=UTF-8
spring-boot.run.main-class
指定 main 类
com.example.Main
spring-boot.run.useTestClasspath
是否把测试类路径也纳入运行时(跑测试场景用)
true
spring-boot.run.workingDirectory
设置运行工作目录
/app
spring-boot.run.optimizedLaunch
是否优化启动(默认 true,含快照/延迟加载)
false

组合示例:

mvn spring-boot:run \  -Dspring-boot.run.profiles=dev \  -Dspring-boot.run.arguments="--server.port=8081 --app.retry=3" \  -Dspring-boot.run.jvmArguments="-Xmx512m -Xms256m"

理解这几个前缀族,run 绝大多数参数需求都能直接照抄拼出来。对照 Maven 类同参数(-DskipTests、-P 等)也更容易分清——run 家族的参数是挂在这个 goal 底下的,不是全局的 Maven 属性,别的 goal 不认这个命名。


三、spring-boot:repackage —— 让 jar 变成可执行 FAT jar

这是整个插件里最核心、也最容易被忽略的 goal,也是"为什么 package 出来能跑"的关键。

# 显式执行 repackagemvn spring-boot:repackage# 通常你不需要单独敲它,因为它在 package 阶段就默认绑定执行了mvn package

功能拆解

repackage 的作用:拿到 maven-jar-plugin 打出的普通 jar(只有你自己的 class,没有依赖),然后重写它,生成两样东西:

  1. 可执行 FAT jar:把依赖 jar 全部解压/内嵌进去,并写入 Main-Class: org.springframework.boot.loader.launch.JarLauncher 和应用的 Start-Class(真正的 main 类)。这样 java -jar 就能启动内嵌 Tomcat。
  2. 原始普通 jar:通常带 .original 后缀,留在 target/,供需要"纯净依赖"的场景(比如 spring-cloud 特例)用。

repackage 何时自动执行

Spring Boot 3 里,repackage 默认绑定在 package 阶段的 repackage 上,你只要 mvn package 就会自动触发它。所以通常情况下你根本不需要单独敲 mvn spring-boot:repackage——它已经在你执行 package 时静默完成了。这句话是理解"为什么 package 就能跑"的钥匙。

那什么时候要显式敲?——当你想脱离默认绑定,自己控制时机时(比如自定义插件配置之后手动跑一次验证),或排查为什么打出来的 jar 不能执行(先 mvn spring-boot:repackage 单独重跑一次,确认不是依赖/插件顺序问题)。

FAT jar 的内部结构:看懂它,你就懂"能不能跑"

为了让"repackage 到底干了什么"落到实处,值得看一眼前两者打包差异的根子。可执行(FAT)jar 之所以能 java -jar 直接跑,秘密在它的内部布局和清单文件。用 unzip 打开一个 spring-boot:repackage 后的 jar 会看到:

# 看可执行 jar 的内部结构(FAT jar 的特征目录是 BOOT-INF)unzip -l target/myapp-0.0.1-SNAPSHOT.jar | head -n 20# 看清单文件里写了什么(关键是 Main-Class 和 Start-Class)unzip -p target/myapp-0.0.1-SNAPSHOT.jar META-INF/MANIFEST.MF

一个典型的可执行 jar 里会有:

  • META-INF/MANIFEST.MF:Main-Class: org.springframework.boot.loader.launch.JarLauncher,和 Start-Class: com.example.demo.DemoApplication。
  • BOOT-INF/classes/:你的编译产物和 application.yml 等资源。
  • BOOT-INF/lib/:所有依赖 jar,被解包平铺进去。
  • BOOT-INF/classpath.idx、BOOT-INF/layers.idx 等:Spring Boot 3 额外写的类路径/分层索引。

Main-Class 指向 JarLauncher(它不是你的应用主类,而是 Spring Boot 提供的启动器),再由启动器读取 Start-Class(你的真正 main),再加载 BOOT-INF/lib 里的依赖,最终启动内嵌 Tomcat。这就是"一个自包含、能双击跑"的产物背后的机制。

反过来,**你随手判断一个 target/*.jar 是不是可执行 FAT jar,最稳的判据就是"有没有 BOOT-INF 目录"**。没有 BOOT-INF 的一般是普通 jar(或 repackage 没生效),自然 java -jar 会报主清单缺失。

常见踩坑

  • 主清单属性缺失 / No main manifest attribute:说明 repackage 没执行或没成功。最常见原因:<plugin> 声明放到了 <plugins> 之外(比如放到了 <pluginManagement>)、或多模块里父模块没正确生效。java -jar 报这个错,先确认 target 里是不是可执行 FAT jar(unzip -l 看是否有 BOOT-INF/)。
  • 可执行 jar 不能被其他模块引用:FAT jar 里内嵌的依赖结构(BOOT-INF/lib)不适合作为普通依赖被依赖。因此要么用 .original(普通 jar),要么把可执行 jar 的 classifier 改成 exec,让 Maven 默认 artifact 保持普通 jar,Spring Boot 官方建议 repackage 配 classifier 优化结构。
  • repackage 是 pom 级 goal,别把它和"打包整个多模块"的完整动作混淆:mvn package 才是连编译带测试带重打一整条。

相关配置(pom 里 plugin 的典型配置)

<build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><!-- 通常 version 由 parent 管理,可不写或显式指定 --><configuration><mainClass>com.example.demo.DemoApplication</mainClass><!-- 把可执行 jar 用 classifier 区分,保留普通 jar 供依赖 --><classifier>exec</classifier></configuration><executions><!-- 显式绑定 repackage 到 package 阶段(Spring Boot 3 默认已有,显式写出来便于理解/自定义) --><execution><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins></build>

配置后典型产物:

# 默认无 classifier:一个可执行 jar(覆盖普通 jar)# `java -jar app-0.0.1-SNAPSHOT.jar` 可跑mvn clean packagels target/*.jar# app-0.0.1-SNAPSHOT.jar                     <- FAT/可执行# app-0.0.1-SNAPSHOT.jar.original           <- 普通 jar(未重打)# 配了 classifier=exec:# app-0.0.1-SNAPSHOT.jar(普通,可被依赖)  + app-0.0.1-SNAPSHOT-exec.jar(可执行)

关于可执行 jar 的"可用边界",值得单独说清楚

理解了 FAT jar 是怎么造的,还有一个日常会产生错觉的点要交代:**FAT jar 适合"交付、部署、Docker",但不适合"当成普通依赖被别人引用"**。原因在于它内部结构的特殊打包方式,别人把它加进自己的 classpath 时,BOOT-INF/lib 里的那些平铺依赖并不在主 jar 的类加载路径上,会带来"引用不到类"的怪问题。

所以当你的 Spring Boot 工程同时扮演"可执行应用"和"被其他模块依赖的公共库"两个角色时,最稳的做法是:让 repackage 的产物用 classifier(如 exec)区分开——默认 artifact 保留普通 jar 供人依赖,-exec.jar 或带 classifier 的版本供人 java -jar 运行。这种方式在微服务拆包、公司内部公共依赖共建时尤其重要,能避免"别人引了你的 jar 却跑不起来"的续集。

另一个要点是关于"分层(Layered)"。Spring Boot 2.3+ 支持分层 jar(layers.idx),把依赖、应用代码、资源分层存放。好处是:构建 Docker 镜像/推送时,build-image 内部可以复用不变层(依赖层基本不变就不用重传),大幅加速镜像构建与拉取;同时分层也能帮助做"增量部署缓存"。对要频繁出镜像、容器镜像偏大的工程,这是打包侧值得专门配置的进阶项,而不只是"能跑就行"。

这两点合起来提醒:可执行 jar 不是"越小越普通越好",而是要为"在哪跑、被谁用"来定结构和 classifier。想清楚了,你的 repackage 才算真正用到位。

顺带补一句关于 "jar" 与 "war" 的取舍(虽然 Spring Boot 3 默认 jar):如果历史包袱/老派部署环境要求打成 war 放进外部 Tomcat,repackage 同样支持 war 的重打,机制类似(<packaging>war</packaging> + 非内嵌部署)。绝大多数新项目无需,但真要踩到"必须用外部容器跑"的场景,记住"repackage 不是只能处理 jar"这一点,能让你少走弯路。


四、spring-boot:build-image —— 生成 OCI 容器镜像

Spring Boot 3 里用这个 goal 直接产出容器镜像(不用手写 Dockerfile + 手动 docker build)。

# 生成 OCI 镜像(沿用默认 builder:Paketo buildpack)mvn spring-boot:build-image# 指定镜像名mvn spring-boot:build-image \  -Dspring-boot.build-image.imageName=registry.example.com/myapp:1.0

功能拆解

build-image 用 Cloud Native Buildpacks(默认 Paketo builder)把你的应用打包成一个可直接 docker run 的 OCI 容器镜像。它不依赖 Dockerfile,由 buildpack 自动探测 Spring Boot 结构、选 JDK、配置启动。

它背后做的事情,大致可以拆成几步(方便理解它"为什么能免 Dockerfile"):

  1. 拉取/准备 builder:默认用 paketobuildpacks/builder:base 这类基础 builder,内含生命周期和运行环境。
  2. 运行 buildpack 生命周期的几个 step(detect/target/analyze/build/export):buildpack 会先探测你用的是不是 Spring Boot 工程、探测需要哪种运行库与 JRE/BellSoft Liberica/Retina 等,然后按指标组装出文件系统层。
  3. 导出为镜像:最终生成一个包含启动器、JRE、应用层的可运行镜像,并打上你指定的 imageName。

正因为整个生命周期由 buildpack 接管,你通常不需要自己写 FROM openjdk + COPY + CMD java -jar 的 Dockerfile。对"团队想统一容器构建标准、又不想维护一堆 Dockerfile"的场景,这是 Spring Boot 官方主推的一条省心事路线。

前置条件与注意点

  • 需要能拉取 buildpack 资源:首次构建要联网下载 builder 及依赖(paketobuildpacks/builder 相关的镜像),离线环境会有阻力。
  • 依赖本机 Docker 或兼容的镜像仓库环境:build-image 需要能访问镜像构建环境(一般是本机 Docker daemon)。没有装 Docker 会报错。
  • 产物先是镜像,不是 target/*.jar;构建完成后可用 docker run 启动。
  • 常用参数(-D 前缀):spring-boot.build-image.imageName 指定镜像库/标签;-Dspring-boot.build-image.pullPolicy 控制拉取策略;-Dspring-boot.build-image.createCache 等。
  • 不想用 build-image、想用传统的 Dockerfile + docker build,完全没问题——build-image 只是官方提供的一条"免写 Dockerfile"的新路,不是唯一选择。

何时用:团队走 GitOps / 用较新的 CI,希望"从源码到镜像一步到位",不维护 Dockerfile 时;或想统一用 buildpack 作为镜像构建标准时。

一个实用技巧:build-image 里如何控制镜像名和 tag

多数人用它时的第一个需求就是"别给我默认那个 docker.io/library/<工程名>:latest,我要换成私服地址和版本号"。这通过 build.imageName 完成:

# 显式指定镜像库路径 + tagmvn spring-boot:build-image \  -Dspring-boot.build-image.imageName=registry.example.com/acme/myapp:1.2.3# 或放到 pom 的插配置里,让该环境构建统一走这个镜像名
<plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><image><name>registry.example.com/acme/${project.artifactId}:${project.version}</name></image></configuration></plugin>

配好后 mvn spring-boot:build-image,产物就会带你的私服前缀 + 项目版本。这样在 CI 里推送镜像、在 K8s 里引用镜像名时都是确定的名字,避免"镜像名不一致造成到处找不到"的线上坑。这也是 build-image 场景里最值得记住的配置之一。


五、其它次要 goal 与"看插件里都有啥"

除了上面三个,插件还提供一些次要 goal,知道存在即可,用到时候再查其参数:

goal
一句话作用
典型场景
spring-boot:help
打印插件 goal 的帮助/参数
想列全 goal 时先跑它
spring-boot:build-info
生成 build-info.properties(含 group、artifact、version、时间戳)
想暴露应用版本元数据给 Actuator/健康检查
spring-boot:process-test-aot
处理测试阶段的 AOT(Ahead-of-Time)原生编译相关
用 GraalVM native / AOT 时
spring-boot:process-aot
处理应用级 AOT(如原生镜像)
原生编译链路
spring-boot:start
 / stop
集成测试时启动/停止应用进程(配合 surefire/failsafe)
基于程序化启动的集成测试

次要 goal 里最值得先学会的一个:build-info

在前面那堆次要的 goal 里,build-info 是非常实用却常被忽略的一个。它的作用是生成一个 META-INF/build-info.properties,里面写入 group、artifact、版本号、构建时间等元数据。搭配 Spring Boot Actuator 的 /actuator/info(或 configprops 等),就能让运维在线上直接看到"这个包是哪个构建出来的、什么时候打的、是哪个版本",排障时"这线上跑的是不是最新版"这种灵魂拷问就有答案了。

# 生成 build-info.properties(配合Actuator/info端点展示)mvn spring-boot:build-info

默认它并不在 package 链路里自动绑定,想让它随构建定期生成,可以在插件的 executions 里把它也绑进 build 流程。绑定后,curl /actuator/info 或 mvn package 打出的 jar 里就会带着这份构建元数据。对需要追溯版本、审计发布的环境而言,这几乎是零成本的"包可溯源"。

<executions><execution><goals><goal>build-info</goal></goals></execution><execution><goals><goal>repackage</goal></goals></execution></executions>

其它 goal 的用途速记

剩下的 goal 大多围绕"集成测试启停"和"AOT 原生"两个冷门方向,这里用一段话串起来,见到能认、用到再去查即可:

  • **start / stop**:在集成测试(surefire/failsafe)过程中非阻塞地启动/停止应用进程,供测试代码以真实 HTTP 的方式打它,属于"起真服务测接口"的利器,比手动起 jar 再测更规整。
  • **process-aot**:对应用做 AOT 处理,输出适合 Spring AOT 的副产物,是 GraalVM 原生镜像编译链路上的关键一环。
  • **process-test-aot**:对应测试侧的 AOT,用原生路线跑测试时用。
  • **help**:看插件所有 goal 与参数,本质是"这个插件能不能查我想要的参数"的自检入口。

这几类 goal 平时低频,但记住它们的"存在与归类",遇到冷门需求(比如"我要在测试里起真实的 Tomcat 应用")时就不会两眼一抹黑,能顺口说出"是不是可以靠这个 goal 来做"。

列全部 goal 的官方姿势

# 列出该插件所有帮助信息 / 所有 goal 的参数mvn org.springframework.boot:spring-boot-maven-plugin:helpmvn org.springframework.boot:spring-boot-maven-plugin:help -Ddetail

这是"我想确认插件里到底有哪些 goal、每个 goal 的参数名是什么"的权威答案来源。比网上转述的二手列表更可靠,也符合"别靠记忆硬造参数"的原则——凡是怀疑参数名,就用 help 核实一遍最稳。


六、插件是否被绑定执行的排查思路

当你怀疑"我的 repackage 到底跑没跑"时,按下面顺序定位:

  1. 先看代码包结构:ls target/*.jar 之后 unzip -l app.jar | grep BOOT-INF。有 BOOT-INF/(FAT 结构)说明 repackage 干过活;只有 com/ 目录没有 BOOT-INF 则没重打。
  2. 看 pom 生效配置:mvn help:effective-pom(H01 讲过)里搜 spring-boot-maven-plugin,确认它是否存在于 <build><plugins>,以及 version、execution(repackage 是否绑定到 package 阶段)。若只在 <pluginManagement> 里出现、<plugins> 里没有,则不生效。
  3. 看构建日志:mvn clean package(带 -X 或正常日志)里能看到类似 spring-boot-maven-plugin:3.x:repackage 执行的行。没出现说明绑定有问题。
  4. 单独重跑一次:mvn spring-boot:repackage 验证能否独立完成重构——能,则之前的问题多为绑定/顺序;不能,则重点看依赖和 JDK 配置。

拿到一个"跑不起来的 jar",完整排查流程串一下

把整篇的 goal 知识整合成一条实战 S.O.P.,方便直接照着走。场景:mvn package 成功,但 java -jar target/xxx.jar 启动报错、No main manifest,或起来就崩。

# 第 1 步:确认它到底是不是 FAT jar(有没有 BOOT-INF)unzip -l target/myapp-0.0.1-SNAPSHOT.jar | grep -c 'BOOT-INF/'# 第 2 步:看清单里的 Main-Class / Start-Class 是否齐全unzip -p target/myapp-0.0.1-SNAPSHOT.jar META-INF/MANIFEST.MF# 第 3 步:确认插件是否绑定,看构建日志 / effective-pommvn help:effective-pom | grep -A 3 'spring-boot-maven-plugin'# 第 4 步:单独验证 repackage 本身能不能跑通mvn clean packagemvn spring-boot:repackagejava -jar target/myapp-0.0.1-SNAPSHOT.jar

判断逻辑一句话串起来:

  • 没有 BOOT-INF/ → repackage 没干成 → 去查插件绑定(effective-pom 里是否在 <plugins>)、是否把 <plugin> 误放进了 <pluginManagement>。
  • 有 BOOT-INF/ 但 java -jar 仍报 NoClassDefFound → 多半是某依赖没进 BOOT-INF/lib(比如 scope 配成了 provided,或该依赖在运行时依赖树里缺失),对照 mvn dependency:tree 核。
  • Main-Class 在,但 Start-Class 错或缺失 → 检查 mainClass 配置指向的类是否真实存在、包名是否有误。

这四条命令 + 三则判据,基本把"Spring Boot 可执行 jar 跑不起来"这一类问题覆盖住了。它也是"H01 生命周期、H03 goal、H04 进程排障"串联起来的一个典型应用——先分清是构建产物问题,再往运行时走。


六、一张"goal → 功能 → 典型命令"表

把上面的内容压成一张速查表:

Goal
功能
典型命令
何时用
run
本地跑应用
mvn spring-boot:run
本地开发/联调
repackage
打成可执行 FAT jar
mvn package
(默认触发)
打包发布前
build-image
生成 OCI 容器镜像
mvn spring-boot:build-image
免 Dockerfile 构建镜像
build-info
生成版本 build-info.properties
用 Actuator 展示版本时
暴露构建元数据
process-aot
AOT 编译
原生镜像链路
GraalVM 场景
help
列插件 goal/参数
mvn spring-boot:help
查插件
start
 / stop
集成测试启停进程
集成测试脚本里
自动化测试

一张按"我现在想干什么"选 goal 的决策速记

很多时候不是 goal 不懂,是临到用时拿不准"这步该用哪个"。给你一张反向选择的清单,照着问题挑即可:

  • 想本地起服务、联调、边改边看 → mvn spring-boot:run(profile 用 run.profiles,应用参数用 run.arguments)。
  • 要给测试/部署一个能 java -jar 的自包含包 → mvn package(自动走 repackage)。
  • 打出来的 jar 跑不起来 / 想验证 repackage 有没有生效 → unzip -l 看 BOOT-INF/ + mvn spring-boot:repackage 单独重跑 + help:effective-pom 查绑定。
  • 要免 Dockerfile 出一个容器镜像 → mvn spring-boot:build-image(记得配 imageName)。
  • 想在 Actuator 里报出"当前包的版本/构建时间" → mvn spring-boot:build-info(并把它绑进 executions 随构建生成)。
  • 不确定插件里有哪些 goal 或参数名 → mvn ...:help 官方核实,别猜。

这张速记的核心逻辑非常一致:**先明确你要的"产物或结果",再逆推出:"要本地跑→run,要可执行包→repackage,要镜像→build-image,要元数据→build-info"**。goal 是"输出导向"的,理解成"每个 goal 对应一类产出"就最省脑。


八、一句话收藏夹(goal 语义速记)

  • mvn spring-boot:run = 本地跑,参数走 -Dspring-boot.run.*(profile 用 run.profiles)。
  • mvn package = 编译 + 测试 + repackage(FAT jar)一气呵成。
  • 可执行 jar = Main-Class: JarLauncher + Start-Class: 你的主类 + BOOT-INF/。
  • jar 打错/少依赖了 = 看 BOOT-INF/ 在不在、effective-pom 里插件在不在 <plugins>。
  • --classifier=exec = 保住普通 jar、另出可执行 jar,结构更工程化。
  • build-image = 免 Dockerfile 的容器镜像,依赖网络与 Docker/buildpack。
  • 报"主清单属性缺失" = 先查 repackage 是否绑定成功。

三个 goal 的状态机:run 到 deploy 的一条主干

把 run、repackage、build-image 看成同一条部署链路上的三个节点,就很好记它们各自查什么、先后怎么过渡:

开发(run)→ 构建(repackage,出 FAT jar)→ 容器(build-image,出镜像)→ 环境(跑起来)

在实践中,这条主干两端还有两个常见衔接:

  1. run 与 repackage 的衔接:本地先用 run 调通,确认逻辑没问题,再 package 产出 repackage 后的 FAT jar。跑的既是同一套代码,但一个"跑工程源码"、一个"跑自包含包"。
  2. repackage 与 build-image 的衔接:build-image 实际上也是在已有 FAT jar 基础上,用 buildpack 把它佐上 JRE 和启动器络成一个可运行镜像。所以"能 mvn package 成功"往往是 build-image 成功的前提——jar 都包不对,镜像自然也起不来。

记住这条主线,遇到"我该用哪个 goal"时,就顺着问一句"我现在在链路哪一端":在开发端用 run,在交付端用 repackage,要上容器用 build-image。这样选 goal 就不再纠结,而是水到渠成。

顺带一提,这三个 goal 的命名也有规律可循,能帮你记忆:run(跑)、repackage(重打包)、build-image(造镜像),动词直白对应产物;而 start/stop、build-info、process-aot 则各自对应"启停进程 / 写元数据 / AOT 处理"这几种侧面能力。名字即语义,见到不认识的多半也能猜个大概——这是 Spring Boot 插件 goal 命名相对友好的一点,比死记更要实在。


九、工程提醒(收藏向)

  1. 新建 Spring Initializr 项目,插件一般已被 parent 兜底;真要自定义(换 mainClass、配 classifier)才需要显式写 <plugin>。
  2. repackage 的默认绑定在 package 阶段,别以为"要手动跑"。
  3. FAT jar 不能当普通依赖被别人引用,用 classifier 保住普通 jar 是一条成熟做法。
  4. build-image 有网络和 Docker 依赖,离线/受限环境要评估。
  5. 排查可执行性问题,从 BOOT-INF/ + effective-pom + 构建日志三处下手,最快。
  6. 想知道插件当下版本、有哪些 goal,mvn ...:help 是官方答案来源,别靠记忆硬造参数。

收个尾:一张 goal 诊断的最终检查清单

把前面的实战判断压成最后一页自查清单,遇到"构建产物不对"时逐条过一遍:

  • [ ] 我要的是本地跑还是自包含包?——决定用 run 还是 package/repackage。
  • [ ] target/*.jar 里有 BOOT-INF/ 吗?——没有就是 repackage 没干成。
  • [ ] Main-Class 是 JarLauncher、Start-Class 是我自己的主类吗?——都不对就查 mainClass 配置。
  • [ ] 插件在 <build><plugins> 里(还是不在 <pluginManagement>)吗?——effective-pom 里核实。
  • [ ] 想上容器的,imageName 按私服路径配了吗?配好再 build-image。
  • [ ] 想线上溯源版本的,build-info 绑进 executions 了吗?绑了才有 /actuator/info 数据。
  • [ ] 参数总是拿不准 / 报错里出现 Mojo 类名——用 mvn ...:help 回到官方参数表。

把这 7 项变成肌肉记忆,Spring Boot Maven 插件这颗"隐藏的螺丝"在你手里就不再是玄学,而是一套可复述、可执行的判断流程。

到这里也就完赛了:run、repackage、build-image 这三个词各自背后都站着一个更细的目标与一组参数;你不是"记住三个命令",而是"记住三类产出"——本地跑、可执行包、容器镜像。遇到 Spring Boot 工程,随手就能说出这层对应,再用 mvn ...:help 随时核参数,这套插件的检修闭环就握在手里了。

记住一条主线:**run 帮你本地跑,repackage 给你可执行包,build-image 送它上容器**。这三件事串起来,就是 Spring Boot 工程从"开发"到"部署"在 Maven 层的主干。

这篇 goal 集合和本手册其它辑如何衔接

这一篇讲的是"Spring Boot Maven 插件里每个 goal 的语义",但它和构建命令那部分是咬合的:

  • 和 Maven 命令:run/repackage/build-image 都是"阶段-目标"机制的具体实例——repackage 绑在 package 阶段、build-image 直接敲 goal 名也行。命令管"哪条命令、哪个阶段",这里管"这个 goal 到底做了什么、参数挂哪"。
  • 和 H02(Gradle 命令):Gradle 侧对应物是 bootRun/bootJar/bootBuildImage。两边 goal 与 task 的名称不同,但"本地跑、打可执行包、出镜像"的三段语义是一一对应的。
  • 和 H04(Linux 排障):run 起出来的、repackage 打出来的、build-image 推出的,最终都是一个 Java 进程。到了服务器上,它就是 H04 里 ps/top/ulimit 要盯的那个对象。构建玩具转成运行态故障,排查起点就在那里。

把这层衔接看清楚,你会明白:命令集合不是一个一个死记的孤岛,而是"怎么把一段 Java 代码从你的机器推向运行环境"这条主线上、不同节点上站着的不同工人。goal 只是其中一岗,但把这一岗的活儿看明白,整条链路的通感就通了。

相关学习资料