Maven 源码阅读 —— 核心构建流程解析
理解 Maven 源码从"一个 mvn 命令发生了什么"开始。本文沿核心调用链拆解:命令行入口 → 构建环境初始化 → 项目模型构建 → 生命周期执行 → 插件调用,最终落到构建结果。
阅读建议:配合源码仓库
maven/maven(主分支 3.x)对照阅读,关注cli、core、model、plugin等模块。
总体调用链
mvn clean install
│
├─ 1. cli/MavenCli#main 命令行入口
│ └─ doMain → execute
├─ 2. core/Maven#execute 核心执行入口
│ ├─ 读取用户请求(goals/phases)
│ ├─ 加载全局/用户 settings
│ └─ 构建 DefaultMavenExecutionRequest
├─ 3. 项目模型构建
│ ├─ ProjectBuilder#build 解析 POM 为 MavenProject
│ └─ 处理继承/聚合/dependencyManagement
├─ 4. 生命周期执行
│ ├─ LifecycleStarter#execute
│ ├─ 计算需要执行的阶段序列
│ └─ 解析各阶段绑定的插件目标
├─ 5. 插件执行
│ ├─ DefaultMavenPluginManager#getConfiguredMojo
│ └─ MojoExecutor#execute 调用插件目标
└─ 6. 结果收集与输出1. 命令行入口:MavenCli
入口类 org.apache.maven.cli.MavenCli:
java
public class MavenCli {
public static void main(String[] args) {
// 1. 解析命令行参数(-P、-D、-pl、-am 等)
// 2. 读取 MAVEN_HOME 与全局配置
// 3. 构造 MavenCli 实例执行
int result = cli.doMain(args, workingDirectory, System.out, System.err);
System.exit(result);
}
}关键职责:
- 参数解析:
CLIManager解析-P/-D/-pl等选项,包装为CLIRequest - 环境初始化:定位
maven.home、读取conf/settings.xml与用户~/.m2/settings.xml - 执行:把参数与配置封装成
DefaultMavenExecutionRequest,交给Maven#execute
2. 核心执行入口:Maven#execute
org.apache.maven.Maven 接口的默认实现 DefaultMaven:
java
public class DefaultMaven implements Maven {
public MavenExecutionResult execute(MavenExecutionRequest request) {
// 1. 构建执行上下文
// 2. 解析要执行的目标:生命周期阶段 or 插件目标
// 3. 交给 LifecycleStarter 执行
// 4. 收集结果
}
}ExecutionRequest 的核心内容
| 元素 | 来源 | 作用 |
|---|---|---|
| goals | 命令行 mvn package deploy | 要执行的生命周期阶段 |
| selectedProjects | -pl 指定模块 | 多模块部分构建 |
| alsoMake | -am | 连带构建依赖模块 |
| profiles | -P 指定 | 激活的 Profile |
| systemProperties | -D 指定 | 系统属性 |
目标分类:阶段还是插件目标?
Maven 需要区分命令行参数是生命周期阶段(clean、install)还是插件目标(dependency:tree):
- 含
:的形式(dependency:tree)→ 插件目标 - 纯单词(
install)→ 生命周期阶段
这在 LifecycleStarter 中根据前缀与生命周期映射表判断。
3. 项目模型构建
在真正执行前,Maven 要把 POM 解析成内存模型 MavenProject。
POM 解析链
MavenModelBuilder(ProjectBuilder)
├─ 读取 pom.xml → Model(原始模型)
├─ 递归解析父 POM → ModelMerger 合并
├─ 应用 dependencyManagement
├─ 应用 Profile(激活的)
├─ 应用超级 POM(默认值)
└─ 得到 EffectiveModel关键组件
| 组件 | 作用 |
|---|---|
ProjectBuilder | 构建项目模型入口 |
ModelMerger | 父子 POM 合并(继承语义) |
DefaultModelProcessor | 定位 POM 文件、加载 profile |
MavenProject | 项目模型运行时对象,供生命周期与插件使用 |
合并规则:子 POM 覆盖父 POM 的同名元素(如 <dependencies> 是追加合并,<version> 是覆盖合并),不同元素合并方式在 ModelMerger 中逐字段定义。
4. 生命周期执行:LifecycleStarter
org.apache.maven.lifecycle.internal.LifecycleStarter#execute 是核心编排器:
java
public class LifecycleStarter {
public void execute(MavenSession session) {
// 1. 计算执行计划:把请求的阶段与默认生命周期映射
// 2. 建立项目构建顺序(多模块拓扑排序)
// 3. 逐个执行
}
}阶段 → 目标 的解析
请求:install
│
▼
LifecycleMapping(jar 打包的映射表)
│ 每阶段对应插件目标:
│ compile → compiler:compile
│ test → surefire:test
│ package → jar:jar
│ install → install:install
▼
构建 BuildPlan:待执行目标列表(含用户自定义 execution 绑定)多模块执行顺序
- 模块按依赖关系做拓扑排序
- 默认顺序执行;
-T 4时按拓扑并行 -pl x -am只构建 x 及其依赖;-pl x -amd构建依赖 x 的模块
5. 插件执行:MojoExecutor
真正执行插件的是 MojoExecutor:
MojoExecutor#execute
├─ DefaultMavenPluginManager#getConfiguredMojo
│ ├─ 从仓库解析插件 jar
│ ├─ 实例化 Mojo(插件类)
│ └─ 注入参数(配置合并:POM config + 命令行属性)
└─ mojo.execute() 执行插件目标逻辑Mojo 参数注入
插件类的 @Parameter 字段从以下来源取值(优先级从高到低):
- 命令行
-D系统属性 - execution 内
<configuration> - 插件级
<configuration> - 插件参数默认值
java
@Mojo(name = "compile", defaultPhase = LifecyclePhase.COMPILE)
public class CompilerMojo extends AbstractMojo {
@Parameter(defaultValue = "${project.build.sourceDirectory}")
private File sourceDirectory;
}6. 构建结果与输出
执行完成后 MavenExecutionResult 收集结果:
- 成功/失败状态与异常
- 构建摘要(BUILD SUCCESS / BUILD FAILURE)
- 每个模块的执行结果
异常分类:LifecycleExecutionException(阶段执行失败)、PluginExecutionException(插件失败)、ProjectBuildingException(POM 解析失败)。
阅读源码的入口建议
| 目标 | 源码位置 |
|---|---|
| 命令入口 | maven-cli/src/main/java/org/apache/maven/cli/MavenCli.java |
| 核心执行 | maven-core/src/main/java/org/apache/maven/Maven.java |
| 生命周期编排 | maven-core/.../lifecycle/internal/LifecycleStarter.java |
| 阶段映射 | maven-core/.../lifecycle/internal/DefaultLifecycleMappingDelegate.java |
| 插件调用 | maven-core/.../plugin/internal/DefaultMavenPluginManager.java |
| 项目构建 | maven-core/.../project/DefaultProjectBuilder.java |
常见问题
- 为什么
mvn test会先编译? 阶段是顺序执行的前缀链路,test 之前的所有阶段(含 compile)都会执行。 - 插件参数从哪里来的? 按"命令行属性 > execution 配置 > 插件配置 > 默认值"的优先级合并注入。
- 怎么调试 Maven 自身?
MAVEN_OPTS=-Xdebug -Xrunjdwp:...或直接用 IDE 启动 MavenCli 并打断点。