Maven Daemon mvnd 深入实战:从 Maven 3.9+ 平滑迁移到 mvnd

欢迎你来读这篇博客,这篇博客主要是关于 Maven Daemon,也就是 mvnd

如果说 mvn 是每次构建都重新起炉灶,那么 mvnd 就是让 Maven 后厨一直热着锅:JVM 不用每次重新启动,Maven 插件类加载器可以复用,JIT
编译后的热点代码也能继续发挥作用。对于多模块 Java 项目,尤其是 Spring Boot / Spring Cloud 工程,这个优化非常实在。

序言

Maven 很稳定,也很经典。但经典工具也有经典工具的代价:每次执行 mvn clean installmvn testmvn package,都会经历一套完整流程:

1
2
3
4
5
6
7
8
9
启动 JVM
启动 Maven
读取 settings.xml
扫描项目
解析 POM
加载插件
构建 reactor
执行生命周期
退出 JVM

一次两次还好,但在本地开发时,如果你频繁跑测试、打包、安装模块,这些重复启动成本就会变得明显。

mvnd 的目标就是减少这种重复成本。

它不是一个新的构建系统,也不是 Gradle 的替代品,而是 Maven 的 daemon 化执行方式。它尽量保持和 mvn 一样的命令体验,但把真正的
Maven 构建放到一个长期存活的后台 daemon 进程中执行。

截至 2026-06-12,Apache Maven 官方下载页显示:

  • Apache Maven 3 当前推荐版本:3.9.16
  • Maven 4 预览版本:4.0.0-rc-5
  • Maven Daemon 当前稳定版本:1.0.6
  • Maven Daemon 2.x 预览版本:2.0.0-rc-3

其中要特别注意:

mvnd 1.x 对应 Maven 3.x,适合当前生产环境中的 Maven 3.9+ 项目;mvnd 2.x 对应 Maven 4.x,目前仍是预览线,不建议生产构建直接切过去。

本文重点讲的是:

从 Maven 3.9+ 迁移到 mvnd 1.x

正文

一、mvnd 是什么

mvnd 全称是 Maven Daemon。

它是 Apache Maven 官方维护的 Maven daemon 执行器,目标是通过 daemon 机制提升 Maven 构建速度。

官方仓库对它的架构说明可以概括为:

  • mvnd 内置 Maven,不要求你额外安装 Maven
  • 真正的构建运行在长期存活的后台 daemon 进程中
  • 一个 daemon 可以服务多个连续的 mvnd 客户端请求
  • mvnd 客户端本身是 GraalVM 构建出来的 native executable,启动更快、内存占用更低
  • 如果没有空闲 daemon,可以并行启动多个 daemon 来处理请求

简单画一下:

flowchart LR
    A[命令行执行 mvnd] --> B[mvnd native client]
    B --> C{是否有可用 daemon}
    C -->|有| D[复用已有 Maven daemon]
    C -->|没有| E[启动新的 Maven daemon]
    D --> F[执行 Maven 构建]
    E --> F
    F --> G[返回构建结果]
    G --> H[daemon 保持存活等待下次构建]

你平时用的时候,命令基本就是把 mvn 换成 mvnd

1
2
3
4
mvnd clean verify
mvnd test
mvnd package
mvnd install

但工程迁移不能只做“字符串替换”。因为 mvnd 默认会并行构建模块,这一点对老项目、插件复杂项目、测试隔离差的项目有影响。

这也是本文后面要重点讲迁移步骤的原因。

二、mvnd 为什么会更快

mvnd 的加速来源主要有五个。

1. JVM 常驻

普通 mvn 每次执行都会启动一个新的 JVM。

sequenceDiagram
    participant User as 用户
    participant JVM as 新 JVM
    participant Maven as Maven
    User ->> JVM: mvn clean test
    JVM ->> Maven: 启动 Maven
    Maven ->> Maven: 加载配置、插件、项目
    Maven -->> User: 构建结束
    JVM -->> JVM: 退出

mvnd 会保留 daemon。

sequenceDiagram
    participant User as 用户
    participant Client as mvnd client
    participant Daemon as Maven daemon
    User ->> Client: mvnd test
    Client ->> Daemon: 发送构建请求
    Daemon ->> Daemon: 复用已启动 JVM
    Daemon -->> Client: 返回结果
    Client -->> User: 输出结果
    Daemon -->> Daemon: 保持存活

这意味着连续构建时,不用反复承担 JVM 启动成本。

2. Maven 插件类加载器可以复用

Maven 构建真正干活的是各种插件:

  • maven-compiler-plugin
  • maven-surefire-plugin
  • maven-failsafe-plugin
  • maven-resources-plugin
  • maven-jar-plugin
  • spring-boot-maven-plugin
  • protobuf-maven-plugin
  • flatten-maven-plugin
  • jacoco-maven-plugin

普通 mvn 每次构建都要重新加载插件相关类。

mvnd 的 daemon 存活后,插件类加载器可以在多次构建之间复用。官方说明里也特别提到,Maven 插件类加载器会被缓存,插件 jar
不需要每次重新读取和解析。

不过要注意:

SNAPSHOT 版本的 Maven 插件不会被缓存。

这点很合理。因为 SNAPSHOT 插件本来就可能频繁变化,缓存它反而容易出问题。

3. JIT 热点代码能继续保留

JVM 的即时编译器会把热点代码优化成本地机器码。

普通 mvn 构建结束后 JVM 退出,这些优化成果也就没了。

mvnd 的 daemon 不退出,JIT 编译后的热点代码可以继续留在进程里。下一次构建时,Maven Core、Maven 插件、JDK 本身的一些热点路径都可能直接受益。

这也是为什么 mvnd 的第二次、第三次构建通常比第一次更明显。

第一次是热锅,后面才是猛火。

4. mvnd client 是 native executable

mvnd 客户端本身是用 GraalVM 构建出来的 native executable。

也就是说,命令行侧的启动成本比普通 Java 程序更低。

它的结构大概是:

1
2
3
4
5
6
7
8
9
mvnd native client
|
| 发送请求
v
long living Maven daemon
|
| 执行 Maven 构建
v
项目构建结果

5. 默认并行构建

mvnd 默认会用多个 CPU 核心并行构建模块。

官方说明中,默认线程数大致按下面的思路计算:

1
Math.max(Runtime.getRuntime().availableProcessors() - 1, 1)

也就是说,普通多模块项目里,你不写 -Tmvnd 也可能已经在并行构建了。

这既是优势,也是迁移时最需要小心的地方。

如果你的项目模块之间依赖关系清晰、插件线程安全、测试隔离良好,这会很香。

如果你的项目里有全局临时文件、共享端口、共享数据库、写死路径、顺序依赖,那默认并行可能会把隐藏问题一下子掀出来。

三、mvn、mvn -T、mvnd、mvnw、Build Cache、mvnsh 的区别

这些工具经常被混在一起说,但它们解决的问题并不一样。

工具 核心作用 解决的问题 是否替代 mvn
mvn 标准 Maven CLI 标准构建 基准工具
mvn -T Maven 并行构建 多模块并行执行 不替代,只增强
mvnd Maven Daemon 减少 JVM / Maven / 插件重复启动成本,并默认并行 可以作为本地开发和 CI 加速入口
mvnw Maven Wrapper 固定项目使用的 Maven 版本 不替代 mvn,只是包装器
Maven Build Cache Extension 构建结果缓存 复用未变化模块的构建产物 不替代 Maven,可与 mvnd 配合
mvnsh Maven Shell Maven 4 中的交互式常驻 shell Maven 4 技术预览能力

再换一种更工程化的说法:

  • mvnw 解决“团队 Maven 版本一致”
  • mvnd 解决“每次构建重复启动太慢”
  • mvn -T 解决“多模块构建没有吃满 CPU”
  • Maven Build Cache Extension 解决“没变化的模块还在重复构建”
  • mvnsh 是 Maven 4 方向上的交互式常驻 Maven shell

它们不是互斥关系。

比较推荐的组合是:

1
2
3
4
5
团队版本统一:mvnw
本地开发加速:mvnd
多模块并行:mvnd 默认并行或显式 -T
CI 构建加速:mvnd + Maven Build Cache Extension
正式发布:mvn 或严格验证后的 mvnd,且保守关闭不确定缓存

四、从 Maven 3.9+ 迁移到 mvnd 的总体策略

不要一上来就全团队替换。

推荐按这个顺序:

flowchart TB
    A[确认当前 Maven 版本和 JDK] --> B[安装 mvnd 1.x]
    B --> C[执行 mvnd --version]
    C --> D[用 -T1 串行模式跑通基准构建]
    D --> E[和 mvn 构建结果对比]
    E --> F[打开 mvnd 默认并行]
    F --> G[排查线程安全和测试隔离问题]
    G --> H[本地开发推广]
    H --> I[CI 灰度接入]
    I --> J[配合 Build Cache 优化]

核心原则:

先保证等价,再追求更快。

构建优化不是赛车游戏,不能只看油门。尤其是企业项目,构建结果可信比快几秒更重要。

五、迁移前检查清单

迁移前先确认这些信息。

1. 当前 Maven 版本

1
mvn -v

如果你当前已经是 Maven 3.9.x,迁移到 mvnd 1.x 通常比较顺。

如果还在 Maven 3.6.x3.8.x,建议先升级到 Maven 3.9.x,让项目先适配新版 Maven,再迁移到 mvnd

2. 当前 JDK 版本

1
2
java -version
echo $JAVA_HOME

Maven 3.9+ 要求 JDK 8+ 才能运行;如果你是 Spring Boot 3.x 项目,通常已经是 JDK 17 或 JDK 21。

如果你本地有多个 JDK,迁移 mvnd 时一定要明确 daemon 使用哪个 JDK。

3. 当前 Maven 配置

重点看:

1
2
3
4
5
~/.m2/settings.xml
.mvn/maven.config
.mvn/jvm.config
MAVEN_OPTS
JAVA_HOME

迁移时要特别关注:

  • 私服地址
  • mirror 配置
  • server 账号
  • profile 激活
  • JVM 参数
  • 内存参数
  • toolchains 配置

4. 当前构建是否支持并行

先用普通 Maven 测一下:

1
2
mvn -T1 clean verify
mvn -T1C clean verify

如果 mvn -T1C 都不稳定,mvnd 默认并行大概率也会踩坑。

这种情况下,先让 mvnd 用串行模式迁移:

1
mvnd -T1 clean verify

六、安装 mvnd

方式一:SDKMAN!

如果你使用 SDKMAN! 管理 Java 工具链,可以直接:

1
sdk install mvnd

查看可用版本:

1
sdk list mvnd

安装后验证:

1
mvnd --version

如果你之前手动安装过 mvnd,需要检查:

1
~/.m2/mvnd.properties

因为 SDKMAN! 通常会帮你管理 JAVA_HOMEMVND_HOME,以前手动写的配置可能会干扰当前安装。

方式二:Homebrew

macOS 可以用 Homebrew。

如果你想使用稳定的 Maven 3.x 对应版本,优先选择 mvnd@1

1
brew install mvndaemon/homebrew-mvnd/mvnd@1

如果你想体验 Maven 4 预览线,则是:

1
brew install mvndaemon/homebrew-mvnd/mvnd

官方说明里提到,mvnd formula 安装的是预览 2.x,内置 Maven 4;mvnd@1 安装的是稳定 1.x,内置 Maven 3.9.x。

所以对于“从 Maven 3.9+ 迁移到 mvnd”的生产项目,建议选择:

1
mvnd@1

安装后确认:

1
mvnd --version

如果命令找不到,可能需要根据 Homebrew 输出把对应 bin 目录加入 PATH

方式三:MacPorts

1
sudo port install mvnd

方式四:手动安装

进入 Apache Maven 官方下载页,下载与你平台匹配的 maven-mvnd-1.0.6 包。

常见平台包括:

  • macOS Apple Silicon:darwin-aarch64
  • macOS Intel:darwin-amd64
  • Linux Arm64:linux-aarch64
  • Linux x86_64:linux-amd64
  • Windows x86_64:windows-amd64

解压后配置环境变量。

Linux / macOS 示例:

1
2
export MVND_HOME=/opt/maven-mvnd-1.0.6
export PATH=$MVND_HOME/bin:$PATH

验证:

1
mvnd --version

macOS 如果手动解压后遇到安全隔离问题,可以执行:

1
xattr -r -d com.apple.quarantine maven-mvnd-1.0.6-darwin-aarch64

Windows 如果遇到:

1
VCRUNTIME140.dll was not found

需要安装 Microsoft Visual C++ Redistributable。

七、确认 mvnd 使用的是哪个 Maven 和 JDK

执行:

1
mvnd --version

你需要重点看:

1
2
3
4
5
6
Maven Daemon version
Apache Maven version
Maven home
Java version
Java runtime
OS name

对于 Maven 3.9+ 项目,建议确认:

1
2
3
Maven Daemon 1.x
Apache Maven 3.9.x
Java version 17/21 或你的项目要求版本

如果你发现它跑到了 Maven 4,说明你可能装的是 mvnd 2.x,这时要切回 mvnd 1.x

八、最小迁移:把 mvn 替换成 mvnd

普通命令:

1
mvn clean verify

迁移为:

1
mvnd clean verify

运行测试:

1
mvnd test

打包:

1
mvnd package

安装到本地仓库:

1
mvnd install

跳过测试:

1
mvnd clean package -DskipTests

指定 profile:

1
mvnd clean verify -Pdev

指定模块:

1
mvnd -pl finance-adapter -am clean test

这些命令和 mvn 基本一致。

九、迁移时强烈建议先用 -T1

因为 mvnd 默认并行构建,所以第一次迁移时不要急着开全速。

先这样跑:

1
mvnd -T1 clean verify

这个命令相当于:

  • 使用 mvnd daemon
  • 但关闭模块并行
  • 先验证 daemon 模式下的构建等价性

如果 mvnd -T1 clean verifymvn -T1 clean verify 都通过,再试:

1
mvnd clean verify

或者显式指定线程:

1
2
mvnd -T1C clean verify
mvnd -T4 clean verify

推荐迁移验证顺序:

1
2
3
mvn -T1 clean verify
mvnd -T1 clean verify
mvnd clean verify

如果第三步失败,问题多半不是 mvnd 不能构建,而是你的项目对并行构建不友好。

十、项目级 mvnd 配置

mvnd 支持配置文件。

读取顺序从高到低大致是:

1
2
3
4
MVND_PROPERTIES_PATH 或 mvnd.propertiesPath 指定的文件
项目目录:[PROJECT_HOME]/.mvn/mvnd.properties
用户目录:[USER_HOME]/.m2/mvnd.properties
安装目录:[MVND_HOME]/conf/mvnd.properties

也就是说,项目级配置优先级高于用户级配置。

推荐在项目中创建:

1
.mvn/mvnd.properties

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 默认构建线程。迁移初期建议先设置为 1,稳定后再改成 1C 或删除该配置使用 mvnd 默认值。
mvnd.threads=1

# daemon 最大堆内存,根据项目规模调整。
mvnd.maxHeapSize=3G

# daemon 初始堆内存。
mvnd.minHeapSize=256M

# daemon 空闲多久后退出。
mvnd.idleTimeout=3 hours

# 默认 builder。官方默认是 smart。
mvnd.builder=smart

# 如果构建日志需要更接近普通 mvn 输出,可以打开 noBuffering。
mvnd.noBuffering=false

如果是本地个人配置,可以写:

1
~/.m2/mvnd.properties

例如你想固定 JDK:

1
2
java.home=/Library/Java/JavaVirtualMachines/temurin-21.jdk/Contents/Home
mvnd.maxHeapSize=4G

注意:

多 JDK 环境里,切换 JAVA_HOMEjava.home 后,建议执行 mvnd --stop 停掉旧 daemon,再重新构建。

否则你以为换了 JDK,实际后台 daemon 可能还是旧的。

十一、daemon 管理命令

查看当前 daemon:

1
mvnd --status

停止所有 daemon:

1
mvnd --stop

查看帮助:

1
mvnd --help

什么时候建议 mvnd --stop

  • 切换 JDK 后
  • 修改 ~/.m2/settings.xml
  • 修改 .mvn/extensions.xml
  • 修改 .mvn/mvnd.properties
  • 升级 mvnd 后
  • 构建状态明显异常时
  • 本地内存紧张时

这招简单粗暴,但好用。

十二、maven.config、jvm.config、mvnd.properties 怎么分工

很多项目已经有:

1
2
.mvn/maven.config
.mvn/jvm.config

迁移到 mvnd 后,建议这样理解:

1. .mvn/maven.config

放 Maven 命令参数。

例如:

1
2
3
-DskipTests=false
-Dfile.encoding=UTF-8
-Pdev

这类参数仍然属于 Maven 构建参数。

2. .mvn/jvm.config

传统 mvn 用它配置 Maven JVM 启动参数。

例如:

1
2
-Xmx2G
-Dfile.encoding=UTF-8

mvnd 的 Maven 运行在 daemon 中,daemon JVM 参数更推荐放到 mvnd.properties

3. .mvn/mvnd.properties

mvnd daemon 相关参数。

例如:

1
2
3
mvnd.maxHeapSize=4G
mvnd.jvmArgs=-Dfile.encoding=UTF-8 -Duser.language=zh -Duser.country=CN
mvnd.threads=1C

迁移时建议把和 daemon 内存、daemon JVM 相关的配置逐步迁移到 mvnd.properties,不要假设所有 MAVEN_OPTS
.mvn/jvm.configmvnd 下都能以完全相同方式生效。

十三、从 Maven 3.9+ 迁移到 mvnd 的标准步骤

第 1 步:记录 Maven 基准

1
2
mvn -v
time mvn -T1 clean verify

记录:

  • Maven 版本
  • JDK 版本
  • 构建耗时
  • 测试是否通过
  • 产物是否正常生成

第 2 步:安装 mvnd 1.x

macOS 建议:

1
brew install mvndaemon/homebrew-mvnd/mvnd@1

或者:

1
sdk install mvnd

确认:

1
mvnd --version

第 3 步:串行模式验证

1
time mvnd -T1 clean verify

如果失败,先不要考虑并行。先看:

  • JDK 是否一致
  • settings 是否一致
  • profile 是否一致
  • 私服依赖是否能下载
  • 插件是否兼容
  • 本地路径是否写死

第 4 步:比较构建产物

至少检查:

1
2
3
4
target/*.jar
target/surefire-reports
target/generated-sources
本地仓库安装结果

对于 Spring Boot 项目,可以再跑:

1
java -jar target/xxx.jar --spring.profiles.active=local

或者启动核心模块的单元测试 / 集成测试。

第 5 步:打开并行构建

1
time mvnd clean verify

如果失败,改成:

1
2
3
mvnd -T1C clean verify
mvnd -T2 clean verify
mvnd -T4 clean verify

逐步找到项目能稳定承受的并行度。

第 6 步:写入项目配置

迁移初期可以保守一点:

1
2
3
mvnd.threads=1
mvnd.maxHeapSize=3G
mvnd.idleTimeout=3 hours

稳定后再改成:

1
2
mvnd.threads=1C
mvnd.maxHeapSize=4G

或者删除 mvnd.threads,使用 mvnd 默认策略。

第 7 步:团队推广

建议在 README 中写清楚:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
## 本地推荐构建方式

安装 Maven Daemon 1.x 后,可以使用:

```bash
mvnd clean verify
```

如果遇到构建异常,先执行:

```bash
mvnd --stop
mvnd -T1 clean verify
```

第 8 步:CI 灰度接入

先不要把所有流水线都切到 mvnd

建议顺序:

1
2
3
4
开发分支 CI
PR / MR 构建
主干分支普通验证
主干分支发布前验证

正式发布阶段要更保守。

如果公司构建链路对可重复性要求很高,release 阶段可以仍然保留:

1
mvn clean deploy

而日常验证使用:

1
mvnd clean verify

十四、CI 中如何使用 mvnd

CI 使用 mvnd 要看机器模型。

1. 短生命周期容器

比如每个 job 都是全新容器,只跑一个 Maven 命令:

1
mvnd -B clean verify

这种情况下,daemon 跨 job 复用价值不大,但仍可能受益于:

  • native client
  • 默认并行
  • 同一个 job 内多个 Maven 命令复用 daemon

如果一个 job 中会执行多次 Maven:

1
2
3
mvnd -B -DskipTests clean package
mvnd -B test
mvnd -B verify

收益会更明显。

2. 长生命周期构建机

如果 Jenkins agent 或自建 runner 是长期存在的机器,mvnd 的 daemon 复用价值更明显。

但也要注意:

  • 不同 job 不要互相污染配置
  • 切换 JDK 后要停 daemon
  • runner 内存要足够
  • 构建后可以按需执行 mvnd --stop

保守写法:

1
2
mvnd -B clean verify
mvnd --stop

更激进写法:

1
mvnd -B clean verify

是否停 daemon,要看你的 CI 机器是否专用、是否多项目共享、是否经常切 JDK。

3. CI 推荐脚本

1
2
3
4
5
6
7
8
9
10
11
#!/usr/bin/env bash
set -e

echo "Java version:"
java -version

echo "Maven Daemon version:"
mvnd --version

echo "Run build:"
mvnd -B -T1C clean verify

如果是迁移初期:

1
mvnd -B -T1 clean verify

稳定后:

1
mvnd -B clean verify

十五、和 Maven Wrapper 怎么配合

mvnwmvnd 不是一类东西。

Maven Wrapper 的作用是:

项目里带一套 wrapper 脚本,保证团队成员不用预先安装 Maven,也能使用项目指定版本的 Maven 构建。

典型文件:

1
2
3
mvnw
mvnw.cmd
.mvn/wrapper/maven-wrapper.properties

执行:

1
./mvnw clean verify

如果本机没有对应 Maven 版本,Wrapper 会下载并使用项目指定版本。

mvnd 的作用是:

用 daemon 机制加速 Maven 构建。

所以二者关系是:

工具 作用
mvnw 固定 Maven 版本,降低环境差异
mvnd 加速构建执行

团队最佳实践可以是:

  • README 中保留 ./mvnw clean verify 作为标准、最稳构建方式
  • 本地开发推荐安装 mvnd 1.x 后使用 mvnd clean verify
  • CI 可根据稳定性选择 ./mvnwmvnd

如果你的团队非常强调“所有人完全一致”,那就继续以 mvnw 为标准入口,mvnd 作为开发者本地加速工具。

十六、和 Maven Build Cache Extension 怎么配合

mvnd 和 Maven Build Cache Extension 解决的是不同层面的问题。

mvnd 解决:

1
2
3
4
5
Maven 启动慢
JVM 重复启动
插件类重复加载
JIT 优化无法复用
多模块默认并行

Build Cache Extension 解决:

1
2
3
4
未变化模块重复编译
未变化模块重复测试
未变化模块重复打包
CI 和本地无法共享构建结果

组合起来:

flowchart LR
    A[执行 mvnd] --> B[复用 Maven daemon]
    B --> C[启动 Maven 构建]
    C --> D[Build Cache 判断模块输入]
    D --> E{缓存命中?}
    E -->|命中| F[恢复模块产物]
    E -->|未命中| G[正常构建模块]
    G --> H[写入缓存]
    F --> I[构建完成]
    H --> I

建议顺序:

  1. 先单独迁移 mvnd
  2. 再接 Maven Build Cache Extension
  3. 最后在 CI 中配置远程缓存

不要一口气全上,否则构建快是快了,出问题时也更难定位。

十七、和 Maven 4 mvnsh 的关系

Maven 4 引入了 mvnsh,也就是 Maven Shell。

它的思路是:

打开一个 Maven Shell,让 Maven 进程在 shell 生命周期内保持存活,后续在 shell 里执行 Maven 命令就不用每次重新启动 Java 和
Maven。

示例:

1
mvnsh

进入后:

1
2
3
maven mvnsh> mvn verify
maven mvnsh> mvn test
maven mvnsh> exit

但是注意:

  • mvnsh 从 Maven 4.0.0 开始可用
  • 官方说明它属于技术预览能力
  • Maven 4 当前仍是预览线,不建议生产构建直接切换

所以当前 Maven 3.9+ 项目更现实的选择是:

1
mvnd 1.x

未来 Maven 4 稳定后,可以再评估:

1
2
3
Maven 4 + mvnsh
Maven Daemon 2.x
Maven Build Cache Extension

十八、哪些项目迁移 mvnd 最容易翻车

1. 插件不支持并行

如果某些插件不是线程安全的,默认并行可能出问题。

解决方式:

1
mvnd -T1 clean verify

或者项目配置:

1
mvnd.threads=1

确认稳定后再逐步提高线程数。

2. 测试共享全局资源

例如多个模块测试同时使用:

  • 同一个本地端口
  • 同一个临时目录
  • 同一个 H2 数据库文件
  • 同一个 Redis database
  • 同一个 Kafka topic
  • 同一个 Docker container name

普通串行构建时可能没事,并行后就炸。

解决方式:

  • 给测试资源加随机后缀
  • 使用 Testcontainers 时避免固定容器名
  • 使用随机端口
  • 测试库隔离
  • 不支持并行的模块暂时串行

3. 构建过程写源码目录

有些项目会把生成文件写到:

1
2
src/main/generated
src/main/java/generated

这类构建行为本身就比较危险。

建议生成目录放到:

1
target/generated-sources

这样更符合 Maven 生命周期,也更容易配合缓存和清理。

4. 多 JDK 切换频繁

例如你本地同时有:

1
2
3
4
JDK 8
JDK 11
JDK 17
JDK 21

切换项目后,如果 daemon 没停,可能会出现“命令行显示换了 JDK,但 daemon 还是旧 JDK”的问题。

解决方式:

1
2
mvnd --stop
mvnd --version

必要时在项目 .mvn/mvnd.properties 中固定:

1
java.home=/path/to/jdk

5. CI 构建机多项目共享

如果同一台机器上不同项目、不同 JDK、不同 settings 混用,需要谨慎。

建议:

  • 每个项目明确 JDK
  • 每个 job 打印 mvnd --version
  • 关键 job 构建结束执行 mvnd --stop
  • 必要时用 -Dmvnd.daemonStorage=... 隔离 daemon 存储

mvnd.daemonStorage 是特殊属性,只能通过命令行系统属性指定:

1
mvnd -Dmvnd.daemonStorage=/data/mvnd/finance clean verify

十九、Spring Boot 项目推荐用法

本地开发:

1
mvnd -T1C clean test

只构建某个模块和依赖:

1
mvnd -pl finance-adapter -am clean test

跳过测试快速打包:

1
mvnd -DskipTests clean package

运行单个测试:

1
mvnd -Dtest=SettlementServiceTest test

运行某个 profile:

1
mvnd clean verify -Pdev

如果是 Spring Boot 3 + JDK 21,可以在用户级配置中:

1
2
3
java.home=/Library/Java/JavaVirtualMachines/temurin-21.jdk/Contents/Home
mvnd.maxHeapSize=4G
mvnd.threads=1C

如果项目还在迁移初期:

1
2
mvnd.threads=1
mvnd.maxHeapSize=3G

二十、Dubbo / gRPC / Protobuf 项目注意事项

如果项目里有 Protobuf 代码生成,例如:

1
src/main/proto

迁移 mvnd 一般没问题,但要注意:

  • 代码生成插件是否线程安全
  • 多模块是否同时写同一个 generated 目录
  • proto 生成结果是否只写到当前模块 target
  • 是否依赖系统级 protoc
  • CI 和本地 protoc 版本是否一致

建议先:

1
2
mvnd -T1 clean generate-sources
mvnd -T1 clean verify

再试:

1
mvnd clean verify

如果并行失败,先不要怪 mvnd,大概率是生成目录、插件配置或模块依赖关系本身不够干净。

二十一、常见问题

Q1:mvnd 会不会改变 Maven 构建结果?

理论上不应该。

mvnd 目标是更快地执行 Maven 构建,而不是改变 Maven 构建语义。

但因为它默认并行,所以迁移时最容易出现的问题不是 Maven 行为变了,而是项目中原本存在的并行不安全问题暴露出来了。

所以先用:

1
mvnd -T1 clean verify

验证等价性。

Q2:mvnd 可以完全替代 mvn 吗?

本地开发和普通 CI 验证,大多数情况下可以。

但正式发布链路建议保守:

  • 继续用 mvn clean deploy
  • 或者在充分验证后使用 mvnd -T1 clean deploy

关键看团队对构建确定性的要求。

Q3:mvnd 和 Maven Wrapper 哪个更推荐?

不是二选一。

  • 新人、CI、标准构建文档:保留 ./mvnw
  • 老开发本地提效:使用 mvnd

最稳的团队写法是:

1
2
标准构建:./mvnw clean verify
推荐本地快速构建:mvnd clean verify

Q4:mvnd 为什么第一次没有明显变快?

正常。

第一次可能需要:

  • 启动 daemon
  • 加载 Maven
  • 加载插件
  • 下载依赖
  • JIT 预热

第二次、第三次才更容易看到收益。

建议测试:

1
2
3
time mvnd clean verify
time mvnd clean verify
time mvnd verify

最后一个不带 clean 的命令通常更接近日常开发反馈速度。

Q5:mvnd 默认并行导致测试失败怎么办?

先串行:

1
mvnd -T1 clean verify

项目级配置:

1
mvnd.threads=1

然后逐步排查:

  • 端口冲突
  • 临时目录冲突
  • 数据库冲突
  • 测试执行顺序依赖
  • 插件线程安全
  • 多模块写同一个文件

Q6:切换 JDK 后 mvnd 还是旧版本怎么办?

执行:

1
2
mvnd --stop
mvnd --version

如果还不对,检查:

1
2
3
JAVA_HOME
~/.m2/mvnd.properties
.mvn/mvnd.properties

Q7:Windows 上启动报 VCRUNTIME140.dll 缺失?

安装 Microsoft Visual C++ Redistributable。

这是 native executable 在 Windows 上常见的运行时依赖问题。

Q8:为什么 CI 上 mvnd 收益不明显?

如果每次 CI 都是全新容器,并且只执行一次 Maven 命令,daemon 跨构建复用不了,收益自然有限。

可以考虑:

  • 一个 job 内合并多个 Maven 命令
  • 使用 mvnd 默认并行
  • 配合 Maven Build Cache Extension
  • 缓存 Maven 本地仓库
  • 缓存 build cache 目录或使用远程 build cache

二十二、推荐团队落地方案

阶段一:个人本地试用

1
2
3
mvnd --version
mvnd -T1 clean verify
mvnd clean verify

目标:

  • 确认能跑
  • 确认构建结果一致
  • 找出并行问题

阶段二:项目级配置

新增:

1
.mvn/mvnd.properties

初期:

1
2
3
mvnd.threads=1
mvnd.maxHeapSize=3G
mvnd.idleTimeout=3 hours

稳定后:

1
2
3
mvnd.threads=1C
mvnd.maxHeapSize=4G
mvnd.idleTimeout=3 hours

阶段三:README 增加说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
## Maven Daemon

本项目支持使用 Maven Daemon 加速本地构建。

标准构建:

```bash
./mvnw clean verify
```

推荐本地快速构建:

```bash
mvnd clean verify
```

如果遇到 JDK 切换或 daemon 状态异常:

```bash
mvnd --stop
mvnd --version
```

阶段四:CI 灰度

先从非发布流水线开始:

1
mvnd -B -T1 clean verify

稳定后:

1
mvnd -B -T1C clean verify

最后:

1
mvnd -B clean verify

阶段五:组合 Build Cache

mvnd 稳定后,再引入:

1
Maven Build Cache Extension

最终目标:

1
2
3
mvnd 减少启动和插件加载成本
Build Cache 减少未变化模块重复构建
CI 缓存减少依赖下载和构建结果重复计算

二十三、一份推荐的 .mvn/mvnd.properties

适合迁移初期:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 迁移初期先串行,确保 mvnd 与 mvn 构建结果一致。
mvnd.threads=1

# 根据项目规模调整,普通 Spring Boot 多模块项目可以从 3G 起。
mvnd.maxHeapSize=3G
mvnd.minHeapSize=256M

# 空闲 daemon 自动退出时间。
mvnd.idleTimeout=3 hours

# 默认 builder。
mvnd.builder=smart

# 额外 JVM 参数。
mvnd.jvmArgs=-Dfile.encoding=UTF-8

适合稳定后:

1
2
3
4
5
6
7
8
# 使用类似 Maven -T1C 的并行度。
mvnd.threads=1C

mvnd.maxHeapSize=4G
mvnd.minHeapSize=512M
mvnd.idleTimeout=3 hours
mvnd.builder=smart
mvnd.jvmArgs=-Dfile.encoding=UTF-8

如果你希望完全使用 mvnd 默认并行策略,可以不配置 mvnd.threads

二十四、迁移验证脚本

可以写一个本地验证脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/env bash
set -e

echo "== Java =="
java -version

echo "== Maven =="
mvn -v

echo "== Maven Daemon =="
mvnd --version

echo "== Baseline: mvn serial =="
time mvn -T1 clean verify

echo "== mvnd serial =="
time mvnd -T1 clean verify

echo "== mvnd default =="
time mvnd clean verify

如果三个都通过,基本可以进入团队试用阶段。

二十五、迁移结论

如果你的项目已经是 Maven 3.9.x,迁移到 mvnd 1.x 的成本通常不高。

最稳的迁移方式不是一上来替换所有命令,而是:

1
2
3
4
先 mvnd -T1 跑通
再 mvnd 默认并行
再接入团队本地开发
最后灰度 CI

对于 Spring Boot 多模块项目,mvnd 的价值很明显:

  • 本地开发反馈更快
  • 多模块构建更容易吃满 CPU
  • 连续执行测试和打包时更省启动成本
  • 和 Build Cache Extension 配合后,CI 和本地构建都有更大优化空间

但也要记住:

mvnd 是加速器,不是构建质量修复器。

如果项目本身有测试隔离差、插件不线程安全、构建写死路径、模块边界混乱的问题,mvnd 只会更快地把这些问题暴露出来。这个角度看,它其实也是一面挺诚实的镜子。

参考资料

启示录

Maven 的慢,很多时候不是某一个点慢,而是很多小成本每次都重复发生。

mvnd 的意义就在于:把这些重复成本从“每次都来一遍”,变成“尽量复用已经热起来的环境”。

对个人开发来说,它减少等待;对团队来说,它推动你正视并行构建、测试隔离、JDK 管理、CI 稳定性这些更底层的工程问题。

工具只是入口,真正的收益来自构建体系变得更清晰。

富贵岂由人,时会高志须酬。

能成功于千载者,必以近察远。


Maven Daemon mvnd 深入实战:从 Maven 3.9+ 平滑迁移到 mvnd
https://allendericdalexander.github.io/2026/06/12/scm/mvnd-maven-daemon-migration-guide/
作者
AtLuoFu
发布于
2026年6月12日
许可协议