Maven 工程手册:从有效模型到可复现构建
同一个 Java 仓库,在开发者机器上执行 mvn package 可以通过,换到 CI 却下载了另一个父 POM;有人改了 dependencyManagement,以为依赖已经进入 classpath;另一个人直接执行某个插件 goal,得到的 JAR 又和 package 阶段不同。它们看起来是三个问题,根因却相同:团队只记住了命令,没有看见 Maven 真正执行的模型。
Maven 不会只读取当前 pom.xml。它会合并 Super POM、父 POM、当前 POM、激活的 profile、用户与全局 settings,再解析依赖、插件和仓库,最终形成 effective model。随后,生命周期阶段触发 packaging 与插件绑定,Reactor 决定多模块顺序。要把 Maven 用成团队工程能力,必须能回答三个问题:最终模型从哪里来、某个产物由哪个 goal 生成、这次构建使用了哪些外部输入。
先判断是否适用
当团队需要用统一入口恢复依赖、编译和测试 JVM 项目,并希望用 POM、生命周期与 Reactor 固化构建模型时,Maven 是合适的选择。有效模型、依赖仲裁、BOM、插件绑定、客户端仓库配置、离线恢复和 Enforcer 共同决定构建是否可解释,不能只靠一条 mvn package 维持项目。
Spring Boot、Jakarta EE 等框架插件只负责把框架任务接入 Maven,业务开发规则仍应回到对应框架手册。Nexus、Artifactory 的服务端高可用、制品发布、签名和审批属于制品与 CI/CD 系统;Maven 项目只管理客户端消费、构建产物和交接证据。跨语言任务图、affected 计算或远端构建缓存成为核心诉求时,应转向专门的 Monorepo 与构建加速工具,而不是继续扩张父 POM。
运行 Wrapper 前确认 JDK 与命令来源
准备一台非生产开发机、一个可删除的练习目录和一个 JDK。先记录命令实际解析到了哪里:
Get-Command java,mvn -ErrorAction SilentlyContinue | Format-Table Name,Source
java -version
mvn --versionPOSIX 环境使用:
command -v java
command -v mvn || true
java -version
mvn --versionmvn --version 同时给出 Maven home、Java home、默认编码和操作系统。它比单独的 java -version 更接近构建事实,因为 Maven 运行 JVM 可能与 IDE Project SDK、编译目标 JDK 不同。下方配置使用保留域名 example.invalid 和占位凭证,不能替换成真实内网地址后提交。
先确认版本与兼容边界
先读取 .mvn/wrapper/maven-wrapper.properties,再执行 ./mvnw --version,确认项目声明的 Maven 分发版本、实际 Maven home 与运行 Java home 一致。随后对照 Maven 版本历史与 Java 要求:选择 3.9.16 时运行要求是 JDK 8 或更高;进入 Maven 4 之前还要确认该版本是否已经从 RC 进入 GA,以及运行 JDK 是否满足 Java 17 要求。
Maven 4 不能只凭核心命令可启动就升级。先从 Maven 4 变化说明 列出受影响的插件、core extension、POM 和配置能力,再在独立分支比较 effective model、依赖树、测试与产物。官方页面仍显示预发布状态时,只能作为兼容试验,不能替换稳定构建基线。
Maven Wrapper 支持 only-script 等引导形式,也支持为 Maven 分发包和 Wrapper JAR 配置 SHA-256。仓库基线应来自 Wrapper properties 与团队升级记录;插件有独立发布节奏,“Maven 能运行”不等于所有插件都支持同一 Maven 或 JDK。
已有项目优先执行 Wrapper
如果仓库已经包含 mvnw、mvnw.cmd 和 .mvn/wrapper/maven-wrapper.properties,开发者通常不需要全局安装目标 Maven。先审查配置,再执行:
Get-Content .mvn\wrapper\maven-wrapper.properties
.\mvnw.cmd --versioncat .mvn/wrapper/maven-wrapper.properties
./mvnw --version第一次运行可能下载 Maven 分发包。应确认 distributionUrl 指向批准来源,并配置 distributionSha256Sum;否则 Wrapper 只固定了版本字符串,没有固定下载内容。Wrapper 脚本和配置必须提交,不能让 CI 临时生成。
新项目生成 Wrapper
新仓库需要一次可审计的 Maven 安装来生成 Wrapper。以下命令演示如何固定明确版本;执行前先用版本历史确认它仍符合团队支持策略:
mvn wrapper:wrapper -Dmaven=3.9.16 -Dtype=only-script
./mvnw --versionWindows 使用 mvnw.cmd --version。生成后检查 .mvn/wrapper/maven-wrapper.properties,从 Apache 下载页或组织批准清单取得分发包 SHA-256,填入 distributionSha256Sum,再在一台没有目标 Maven 的干净机器验证。
全局安装只用于引导或诊断
Apache Maven 官方支持下载二进制包后加入 PATH,也列出了 Homebrew、SDKMAN!、APT、DNF、YUM、Chocolatey 与 Scoop 等入口。系统包仓库可能落后于项目基线,因此全局安装完成后必须执行 mvn --version,不能把“安装成功”当成“版本正确”。日常项目命令统一改用 Wrapper。
卸载全局 Maven 时按原安装入口移除,并再次检查命令路径;不要手工删除一个目录后留下旧 PATH。Wrapper 下载缓存位于用户目录,是否清理由磁盘治理策略决定,不要在共享构建机上直接递归删除整个 .m2。
先建立一个可复制项目
下面的项目故意不依赖业务框架。它只验证 Maven 能读取模型、编译源码、执行 test 阶段并生成 JAR。创建目录:
maven-verify-lab/
├─ pom.xml
└─ src/
└─ main/
└─ java/
└─ lab/
└─ App.javapom.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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>example.lab</groupId>
<artifactId>maven-verify-lab</artifactId>
<version>1.0.0-SNAPSHOT</version>
<properties>
<maven.compiler.release>17</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-clean-plugin</artifactId>
<version>3.5.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.5.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.15.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.4</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.5.0</version>
</plugin>
</plugins>
</build>
</project>这里显式固定了命令真正经过的 Clean、Resources、Compiler、Surefire 与 JAR Plugin。Surefire 3.6.0-M1 属于预发布版本,示例选择官方归档的 3.5.4;采用这些版本前仍要分别检查插件的 System Requirements、发布状态和项目兼容性。示例版本进入长期项目后必须纳入插件升级流程,不能脱离 Wrapper、JDK 和产物验证单独更新。
src/main/java/lab/App.java:
package lab;
public final class App {
private App() {}
public static void main(String[] args) {
System.out.println("maven-model-ok");
}
}在目录根执行:
./mvnw clean verify
jar tf target/maven-verify-lab-1.0.0-SNAPSHOT.jar
java -cp target/maven-verify-lab-1.0.0-SNAPSHOT.jar lab.AppWindows 将第一行换成 .\mvnw.cmd clean verify。预期日志以 BUILD SUCCESS 结束,JAR 列表包含 lab/App.class,最后输出 maven-model-ok。verify 比 package 多走集成验证后的阶段,适合作为团队权威入口;这个最小项目没有测试源码,因此“test 阶段通过”不等于已有测试覆盖。
清理实验只删除练习目录中的 target:
./mvnw clean如果需要完全退出实验,删除整个 maven-verify-lab 目录。不要为了重跑而删除用户级整个本地仓库;那会丢掉诊断证据并放大网络压力。
POM 不是最终模型
一个最小 POM 也会继承 Super POM 的默认值。实际项目还可能继承企业 parent,导入 BOM,被 profile 改写属性,并由命令行 -D 或 settings 激活不同仓库。排障时先生成有效模型:
./mvnw help:effective-pom -Dverbose > effective-pom.xml
./mvnw help:effective-settings > effective-settings.xml
./mvnw help:active-profileseffective-pom.xml 是诊断产物,不应默认提交。它可能包含内部仓库路径或构建环境信息,分享前要脱敏。-Dverbose 会补充元素来源,适合追查某个插件版本或属性来自哪个 parent。
有效模型的形成顺序可以这样理解:
父子继承和聚合不是一回事。parent 决定模型继承,<modules> 决定当前 Reactor 聚合哪些模块;一个 POM 可以只做 parent、只做 aggregator,也可以同时承担两者。把它们混为一谈,会在拆仓、独立发布和 relativePath 上留下隐式耦合。
依赖解析:声明范围不等于最终版本
Maven 传递依赖发生冲突时,默认采用“最近定义”原则;路径深度相同则先声明者优先。这个结果不是“最高版本自动胜出”。先用证据看树:
./mvnw dependency:tree
./mvnw dependency:tree -Dverbose
./mvnw dependency:tree -Dincludes=org.example:example-lib依赖 scope 还决定它进入哪些 classpath、是否传递以及是否参与打包。compile、provided、runtime、test 不是环境标签,而是 classpath 契约。把容器提供的 API 错写为 compile,可能把重复实现带入产物;把运行时驱动错写为 test,编译能过、启动却找不到类。
可选依赖与 exclusion 也不是通用修复器。optional 表示下游不会自动继承,需要下游显式选择;exclusion 针对某条依赖路径排除传递项。排除后必须证明运行时仍有兼容实现,不能只以依赖树“变干净”为验收。
dependencyManagement 与 BOM
dependencyManagement 管理版本和默认属性,但不会自动把依赖加入当前模块。真正进入 classpath 的依赖仍需出现在 <dependencies>。这是 Maven 团队最常见的模型误判之一。
父 POM 可以集中管理:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>example-bom</artifactId>
<version>${example-bom.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>BOM import 只在 dependencyManagement 中生效。它把一组版本约束引入模型,不下载所有库,也不保证这些版本在你的组合场景下一定兼容。评审 BOM 升级时要看 effective POM 和完整依赖树,运行代表性测试,并检查是否出现同深度仲裁变化。
pluginManagement 也遵循相似边界:它为插件提供默认版本和配置,但通常需要模块在 <plugins> 中实际声明后才执行。依赖管理和插件管理必须分开治理,因为插件是在构建机上执行的代码,权限常常高于业务依赖。
生命周期、phase 与 goal
Maven 内置 clean、default 和 site 三套生命周期。最常用的 default 生命周期按顺序经过 validate、compile、test、package、verify、install、deploy 等阶段。执行后面的 phase 会连带执行前面的 phase:
./mvnw verify这不是“执行一个 verify 函数”,而是让当前 packaging 的默认绑定和 POM 中绑定到这些阶段的插件 goals 依次运行。jar、war、pom 等 packaging 会改变默认绑定;插件 execution 还能把额外 goal 绑定到某个 phase。
直接执行 goal 的语义不同:
./mvnw dependency:tree
./mvnw help:effective-pom它们调用具体插件 goal,不会自动替代完整生命周期。团队 CI 应优先调用一个权威 phase,例如 verify;诊断 goal 用于取证,不应偷偷成为另一套产物入口。
要知道实际执行了什么,使用普通或调试日志,并查看插件的 help:
./mvnw help:describe -Dplugin=org.apache.maven.plugins:maven-jar-plugin -Ddetail
./mvnw -X verify-X 可能输出路径、仓库、代理和配置,日志进入工单前必须脱敏。不要长期把 debug 日志当 CI 默认产物。
多模块 Reactor:构建顺序不是目录顺序
Reactor 收集模块、排序并按依赖关系构建。官方文档列出的排序因素包括模块间项目依赖、插件声明、插件依赖和构建扩展;<modules> 中的文本顺序不是唯一依据。
常用选择参数:
./mvnw -pl :service-api -am verify
./mvnw -pl :service-api -amd verify
./mvnw -rf :service-api verify-pl 选择项目。-am 同时构建其所需上游模块。-amd 同时构建依赖它的下游模块。
-rf 从失败模块恢复 Reactor,但不会自动证明之前模块产物仍与当前源码一致。
本地为了提速使用 -pl ... -am 可以接受,合并门禁仍需定期执行完整 Reactor。只跑受影响模块时,选择依据、遗漏风险和完整构建频率必须写进团队契约;真正跨语言的大仓任务图和远端缓存属于 31 家族。
并行构建可以用 -T,但并非所有插件和测试都线程安全:
./mvnw -T 1C verify上线并行前要比较串行与并行产物、测试和失败稳定性。共享临时目录、固定端口、修改全局状态的测试通常先暴露问题。
settings、mirror、server 与 profile
POM 是项目可共享模型,settings.xml 是机器或用户侧执行环境。用户 settings 通常位于 ~/.m2/settings.xml,全局 settings 位于 Maven 安装目录。可提交到项目的应是无 secret 模板或说明,不是真实用户文件。
一个受控镜像与凭证示意:
<settings xmlns="http://maven.apache.org/SETTINGS/1.2.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.2.0 https://maven.apache.org/xsd/settings-1.2.0.xsd">
<mirrors>
<mirror>
<id>approved-mirror</id>
<url>https://packages.example.invalid/maven-public/</url>
<mirrorOf>*</mirrorOf>
</mirror>
</mirrors>
<servers>
<server>
<id>approved-mirror</id>
<username>${env.MAVEN_REPO_USER}</username>
<password>${env.MAVEN_REPO_TOKEN}</password>
</server>
</servers>
</settings>server.id 必须匹配 Maven 实际连接的 repository 或 mirror id。401 并不总是密码错,也可能是 id 不匹配导致凭证根本没被选中。mirrorOf=* 会接管所有仓库请求,企业镜像必须能代理所需来源;随意加多个 mirror 不会形成直觉中的优先级列表。
profile 应只表达确有必要的构建差异,并且可以通过 help:active-profiles 解释。不要用 profile 隐藏业务环境配置,更不要让开发、测试、生产靠默认激活猜测。profile 改变依赖、插件或产物时,它就是产物身份的一部分,CI 必须显式记录。
Maven 4 的项目级 settings 与配置能力正在演进;稳定线项目不要提前复制 RC 示例并假设 Maven 3 等价。跨主版本试验应在独立分支记录 effective model diff。
本地仓库、离线与可复现性
默认本地仓库位于 ~/.m2/repository,既保存下载内容,也保存本地 install 产生的项目产物和解析状态。它不是一个可以无条件跨机器复制的透明目录,更不是团队制品库。
离线命令:
./mvnw -o verify离线成功只证明当前本地仓库已有本次构建需要的依赖、插件和元数据,不证明另一台干净机器可恢复。团队至少做两类验证:
干净或隔离本地仓库从批准源恢复。预热后的离线构建不访问网络并生成等价产物。
使用隔离仓库可以避免污染个人缓存:
./mvnw -Dmaven.repo.local=./.m2-lab verify
./mvnw -o -Dmaven.repo.local=./.m2-lab verify实验后只删除仓库内的 .m2-lab。如果第二次离线失败,先看缺的是业务依赖、父 POM、插件还是插件传递依赖,不要把错误概括成“缓存没下全”。
-U 会要求检查缺失发布版和更新 snapshot,不能作为所有失败的固定答案;它改变网络访问和解析行为。删除单个损坏坐标比清空整个仓库更可控,删除前保存 _remote.repositories、.lastUpdated 和校验失败日志作为证据。
Enforcer:把团队假设变成失败
Maven Enforcer Plugin 可以在生命周期早期检查 Maven/JDK 范围、依赖收敛、禁止依赖和其他规则。它不是漏洞扫描器,也不会自动判断所有二进制兼容问题,但适合阻断已知错误基线。
典型策略包括:
要求通过批准的 Maven 与 JDK 范围运行。检查 dependency convergence,暴露同一坐标出现多个版本。禁止已知不可接受的依赖或重复类风险入口。
要求插件和依赖版本显式,减少默认绑定漂移。
规则应绑定到 validate,让构建尽早失败。启用前先在代表仓库统计现有违规,逐项修正或建立有 owner、原因和到期日的例外。不要长期 -Denforcer.skip=true,那相当于把门禁变成提示。
项目接入与常用操作
一个可维护 Maven 仓库通常保留:
mvnw / mvnw.cmd # 团队权威入口
.mvn/wrapper/maven-wrapper.properties # Maven 分发与校验
.mvn/maven.config # 经评审的共享 CLI 参数
pom.xml # 项目模型
settings.example.xml # 可选,无 secret 的环境模板
docs/build.md # JDK、镜像、验证与升级说明常用命令要按问题选择:
./mvnw --version
./mvnw verify
./mvnw help:effective-pom -Dverbose
./mvnw help:effective-settings
./mvnw help:active-profiles
./mvnw dependency:tree
./mvnw dependency:analyze
./mvnw -pl :module -am verifydependency:analyze 基于字节码静态分析,会对反射、服务加载等场景产生误判;结果需要结合运行路径解释。任何“清理未使用依赖”的 PR 都要经过完整测试。
构建产物不能只看文件存在。至少记录坐标、文件名、大小、hash、JAR 内容、manifest、测试报告和构建版本:
sha256sum target/*.jar
jar tf target/*.jarWindows 可用 Get-FileHash target\*.jar -Algorithm SHA256。若要求可复现字节级产物,还要治理时间戳、文件顺序、插件版本、JDK 和编码;同样源码“功能可运行”不等于 hash 必然一致。
代理、CA、权限与凭证
Maven 代理可在用户 settings.xml 的 <proxies> 配置,企业 CA 应进入运行 Maven 的 JDK truststore,或由批准的 JVM 参数指向受控 truststore。不能用关闭 TLS 校验或切换 HTTP 作为长期修复。
排障时分四层:DNS/路由是否可达,代理是否被选中,TLS 链是否被运行 JVM 信任,仓库是否接受当前身份。浏览器可访问不代表 Maven 可访问,因为浏览器与 JDK 可能使用不同代理和证书库。
仓库消费凭证应只读、最小 scope、可轮换,并由用户密钥设施或 CI secret 注入。Wrapper 分发下载凭证与依赖仓库凭证也应分离。官方 Wrapper 支持 MVNW_USERNAME / MVNW_PASSWORD,但环境变量仍可能被子进程、调试输出或错误采集暴露;优先使用短期凭证并限制日志。
Maven 的密码加密主要降低 settings 明文暴露,不等同于硬件密钥或集中 secret 服务。能运行构建的账号通常也能解密使用,威胁模型必须写清。任何凭证误入 Git 或日志,都应立即撤销和轮换,不能只删除文件。
插件供应链:构建时执行的代码
依赖通常进入应用 classpath,插件则直接在构建进程中执行,可以读源码、环境变量、用户目录和网络。构建扩展更早进入 Maven 核心执行路径,风险更高。
团队应做到:
插件版本显式固定,不依赖 Super POM 或前缀解析碰巧选中的版本。插件与 pluginRepositories 只来自批准源,镜像规则覆盖插件解析。新插件评审维护状态、许可证、传递依赖、网络行为和写文件范围。
CI 安装阶段只提供完成当前任务所需的最小凭证,发布凭证延后到隔离 job。Wrapper 分发和插件产物都保留校验与来源证据。
不要把 groupId:artifactId:goal 的一次成功当作安全证明。真正验收是干净环境、受控源、最小权限下执行权威生命周期,并对产物和副作用取证。
常见失败:从现象回到模型
本机成功,CI 提示依赖或父 POM不存在
本机 verify 通过,CI 报 Non-resolvable parent POM 或依赖无法解析。
比较 ./mvnw --version、effective settings、active profiles 和 dependency:tree;用隔离本地仓库重跑。
常见原因:本机曾 install 未发布模块,用户 settings 激活了额外仓库,CI mirror 不包含目标源,或 parent 的 relativePath 只在某个目录结构成立。
让 Reactor 包含所需模块,或把批准产物发布到受控仓库;统一 settings 注入与 mirror 规则。
空仓库从批准源执行 Wrapper verify,工作区无额外文件,依赖树一致。
版本明明写了,运行时却加载另一个版本
POM 中声明了版本,dependency:tree 显示被另一条路径仲裁,运行时出现 NoSuchMethodError。
查看冲突坐标的完整树和 effective POM,区分直接声明、dependencyManagement、BOM 与传递路径。
在最接近治理边界的 parent/BOM 明确管理版本,并升级不兼容依赖;不要靠随机调整声明顺序长期压住冲突。
依赖树收敛、Enforcer 通过、代表性运行测试通过。
插件 goal 找不到或版本漂移
prefix:goal 在一台机器可用,另一台报前缀无法解析,或执行了不同插件版本。
查看 effective POM、pluginGroups、pluginRepositories 与 debug 日志中的插件坐标。
在 POM 中使用完整插件坐标并固定版本;确保 mirror 覆盖插件来源。
隔离本地仓库执行权威 phase,不依赖用户 settings 的额外 pluginGroup。
401/403 与镜像错路由
公共依赖也被送往内网镜像,或服务器返回未授权。
检查 effective settings 中 mirror 的 id、mirrorOf、URL,以及 server id 是否匹配。
修正镜像匹配表达式和凭证作用域,撤销可能发往错误主机的凭证。
使用只读账号解析一个批准坐标,日志不含 secret,目标仓库审计能看到预期身份。
PKIX path building failed 或代理超时
浏览器可下载,Maven 失败。
从 mvn --version 确认运行 Java home,检查代理最终配置和该 JDK truststore。
由安全 owner 下发完整 CA 链并配置运行 JVM;修正 nonProxyHosts,不要关闭校验。
Wrapper 分发、父 POM、依赖和插件四类下载都成功。
Reactor 恢复后产物混入旧模块
-rf 从失败模块恢复后通过,但最终集成结果与完整构建不同。
检查上游模块是否在源码变化后重新构建,是否从本地仓库取到旧 snapshot。
在无法证明上游未变时执行完整 clean verify;不要把恢复命令直接作为发布证据。
完整 Reactor 在干净工作区通过,产物 hash 和测试报告重新生成。
CI 等价入口
CI 不应另写一套 Maven 逻辑。最小步骤是记录 JDK 与 Wrapper 版本、注入只读 settings、执行同一 verify,最后归档测试和产物证据:
steps:
- name: versions
run: |
java -version
./mvnw --version
- name: verify
run: ./mvnw --batch-mode --no-transfer-progress verifyWindows runner 调用 mvnw.cmd。CI 缓存键至少包含操作系统、CPU/JDK 基线、Wrapper 配置和所有会改变有效模型的 POM 摘要。缓存只提升速度,删除后仍须能从批准源恢复。
如果 CI 通过 -s 指定 settings,路径和注入方式应是流水线模板的一部分;不要让 runner 主目录里长期残留跨项目共享凭证。发布 job 与普通验证 job 分离,只有发布 job 在审批后获得写权限。
升级、回滚与团队落地
升级 Maven 不是只改 distributionUrl。先建立旧基线,再升级 Wrapper,最后比较:
./mvnw --version 与运行 JDK。effective POM、effective settings 和 active profiles。完整依赖树、插件版本与校验规则。
单模块与完整 Reactor 的测试、产物和耗时。警告、弃用、扩展兼容和 CI 行为。
Maven 3 到 Maven 4 尤其要检查插件与 core extension。官方仍把 Maven 4 标为未 GA,且运行 JDK 提升到 17;编译目标可以通过 compiler/toolchain 与运行 JVM 分离,但这不消除插件兼容风险。
回滚应恢复 Wrapper 脚本、properties、.mvn 配置、POM/parent/BOM 和 CI 镜像的同一提交。不要只把本机 mvn 降级,因为团队入口是 Wrapper。回滚后清理本次试验的隔离缓存,再从批准源重建旧基线。
团队最低基线包括:
每个仓库有构建 owner 和升级窗口。Wrapper 与分发校验进入代码评审,禁止 CI 使用裸 mvn。parent、BOM、依赖和插件升级分别展示 effective model 与树变化。
verify 是本机与 CI 的权威入口,跳过测试和 Enforcer 需要显式审批。settings 只由受控模板生成,镜像、CA 与只读凭证有 owner 和轮换周期。构建失败工单包含版本、模型、树、Reactor、仓库和产物证据,而不是只截最后一行。
effective model 会让“源码没改”仍然变更构建
父 POM、BOM、远端 snapshot、settings profile 和命令行属性都可能改变模型。现象是同一提交在两台机器得到不同插件或依赖。判断标准不是 git diff 为空,而是 effective POM、active profiles、依赖树和仓库来源一致。治理上应禁止关键 parent/BOM 使用漂移版本,并把外部输入纳入构建记录。
本地仓库混合了缓存与本地产物身份
开发者执行 install 后,另一个项目可能解析到从未发布的 snapshot;CI 干净环境则失败。遇到“只在我机器成功”,优先用隔离仓库重跑。团队共享的正确边界是远端受控制品库,不是复制某位开发者的 .m2。
mirror 规则可能把凭证和请求送错边界
过宽 mirrorOf、重复仓库 id 或 URL 改写会让依赖和插件走向意外主机。判断时同时看 effective settings 与仓库审计日志。任何凭证发错主机都按泄露处理:撤销、轮换、修正规则,再验证最小只读权限。
插件默认绑定会随 Maven 与 packaging 基线变化
项目没有显式插件版本时,构建依赖 Super POM 和生命周期默认值。升级后可能出现编译参数、测试发现或 JAR manifest 变化。架构结论是关键插件必须固定版本,并把产物内容和测试发现纳入升级对比,不能只看 BUILD SUCCESS。
并行与恢复会复用不该复用的状态
-T、-rf 和模块选择能提速,也会暴露非线程安全插件、固定端口测试和旧本地产物。发布证据应来自可重复的完整构建;局部命令是开发提效入口,不是默认发布入口。差异一旦出现,先回到串行、干净 Reactor 建立对照,再定位具体插件或测试状态。
Maven 4 迁移同时改变运行时与扩展边界
Maven 4 需要 Java 17 运行,而项目可能仍编译到更低 release。core extension、旧插件和加密工具的兼容性不能从普通依赖树推断。应建立独立兼容分支,在旧/新 Maven 下对比模型、树、测试和产物;旧 Wrapper 保留到门禁完成,不能原地覆盖后再寻找回滚方法。
仓库提交了 mvnw、mvnw.cmd 和 Wrapper 配置,并校验分发来源与 SHA-256。./mvnw --version 记录了 Maven home、运行 JDK、编码和操作系统事实。已解释 effective POM、effective settings 和 active profiles 的来源。
依赖树无无法解释的仲裁;dependencyManagement 与实际依赖声明边界清楚。parent、aggregator、BOM、pluginManagement 和 Reactor 职责没有混写。本机与 CI 调用同一 verify 入口,隔离本地仓库可从批准源恢复。
mirror、server id、代理和 CA 已验证,仓库及日志无真实凭证。插件与扩展有固定版本、批准来源、最小权限和升级审查。并行、模块选择和 -rf 不被当作未经完整验证的发布证据。
升级记录包含模型、依赖、插件、测试、产物和回滚 Wrapper。Maven 4 仍按 RC 试验对待,没有误写为稳定 GA 基线。构建 owner、例外到期、凭证轮换和退出路径明确。
升级前检查入口
安装来源或运行 JDK 是否变化:查看 安装说明 和 版本历史,确认目标 Maven 的发布状态、最低 Java 要求和旧版本支持边界。
Wrapper 是否能在干净机器恢复:查看 Maven Wrapper,核对引导类型、distributionUrl、SHA-256、认证和内部镜像接入方式。
effective model 为什么变化:用 POM 模型说明 与 依赖机制 复核继承、聚合、仲裁、scope、BOM 和 exclusions。
phase 与产物为什么变化:查看 构建生命周期 和 多模块 Reactor,确认 packaging、goal 绑定与模块排序没有漂移。
镜像、代理或凭证为什么失效:查看 Settings Reference,逐项核对 mirror、server id、profile、proxy 与本地仓库配置。
核心插件是否支持目标 Maven/JDK:分别检查 Compiler Plugin、JAR Plugin、Surefire Plugin 和 Enforcer Plugin 的 System Requirements 与行为变化。
是否准备进入 Maven 4:以 Maven 4 变化说明 为入口,确认 GA 状态、Java 17 运行要求、插件与 extension 兼容,再决定试验、升级或保持 Maven 3。
