Maven 源码阅读 —— 生命周期与插件执行机制
生命周期是 Maven 的"骨架"。本文从源码层面拆解 Phase / Goal / Lifecycle 三个核心模型的实现,以及阶段如何映射到插件目标并最终执行。
核心模型:Lifecycle / Phase / Goal
概念映射
Lifecycle(生命周期):clean / default / site
└── Phase(阶段):clean、validate、compile、package...
└── Goal(插件目标):compiler:compile、surefire:test...源码中三个模型的关系:
| 模型 | 源码类 | 说明 |
|---|---|---|
| Lifecycle | Lifecycle | 有序的 Phase 集合 |
| Phase | LifecyclePhase | 命名的阶段,绑定 Goal 列表 |
| Goal | MojoExecution | 插件目标的一次可执行实例 |
| Mojo | Mojo 接口 | 插件目标的实现类(@Mojo 注解) |
Lifecycle 定义源码
java
// org.apache.maven.lifecycle.Lifecycle
public class Lifecycle {
private String id; // "default" / "clean" / "site"
private List<LifecyclePhase> phases; // 有序阶段列表
private List<String> defaultPhases; // 阶段 id 列表
}phases 的顺序即执行顺序:执行某个阶段时,先执行其之前的所有阶段。
阶段映射表:Phase → Goal
Maven 默认(jar 打包)阶段与插件目标的绑定定义在生命周期映射插件中:
maven-core 中的 lifecycle mapping 数据(部分):
process-resources → maven-resources-plugin:resources
compile → maven-compiler-plugin:compile
test-compile → maven-compiler-plugin:testCompile
test → maven-surefire-plugin:test
package → maven-jar-plugin:jar
install → maven-install-plugin:install
deploy → maven-deploy-plugin:deploy映射实现:DefaultLifecycleMappingDelegate
java
// org.apache.maven.lifecycle.internal.DefaultLifecycleMappingDelegate
public class DefaultLifecycleMappingDelegate implements LifecycleMappingDelegate {
public Map<Phase, List<MojoExecution>> calculateLifecycleMappings(...) {
// 1. 从打包类型(packaging=jar)取得映射表
// 2. 遍历目标阶段之前的每个阶段
// 3. 为每阶段解析绑定的插件目标
// 4. 合并用户 <executions> 中的自定义绑定
// 5. 返回 Map<Phase, List<MojoExecution>>
}
}打包类型决定映射
映射表按 packaging 区分:jar、war、pom 等各有不同的默认绑定。packaging 类型对应的映射由 LifecycleMapping 组件提供(maven-core 内置 jar/war 等,maven-war-plugin 等插件可提供扩展映射)。
执行计划:BuildPlan 的构建
LifecycleStarter 把用户请求(如 install)展开为完整的执行计划:
请求阶段:install
│
▼
计算 0..N 个阶段(install 之前所有阶段)
│
▼
每阶段查映射 → List<MojoExecution>
│
▼
合并用户 executions(自定义 phase/goal 绑定)
│
▼
生成有序执行列表(BuildPlan)java
// 关键数据结构
class BuildPlan {
List<ExecutionPlanItem> items; // 有序的可执行项
}
class ExecutionPlanItem {
LifecyclePhase phase; // 所属阶段
List<MojoExecution> mojoExecutions; // 该阶段要执行的目标
}多模块下的执行计划
- 每个模块独立生成 BuildPlan
- 模块按拓扑序执行:先构建依赖方,再构建被依赖方
-T并行时同一模块的 plan 内部仍顺序执行,不同模块并行
插件目标执行:MojoExecutor
执行计划生成后,MojoExecutor 逐个执行 MojoExecution:
java
// org.apache.maven.lifecycle.internal.MojoExecutor
public class MojoExecutor {
private MojoExecutionResult executeMojo(...) {
// 1. 获取 Mojo 实例(getConfiguredMojo)
// 2. 注入参数
// 3. 执行前置钩子
// 4. mojo.execute() —— 真正的插件逻辑
// 5. 记录结果
}
}Mojo 实例化:DefaultMavenPluginManager
java
public class DefaultMavenPluginManager implements MavenPluginManager {
public Mojo getConfiguredMojo(Class<? extends Mojo> mojoClass, MavenSession session, MojoExecution mojoExecution) {
// 1. 解析插件构件(从仓库下载插件 jar)
// 2. 创建插件 ClassLoader(依赖隔离)
// 3. 实例化 Mojo 对象
// 4. 注入参数(Mojo 配置)
// 5. 返回配置好的 Mojo
}
}参数注入机制
Mojo 字段通过 @Parameter 注解声明,注入源与优先级:
java
@Mojo(name = "compile", defaultPhase = LifecyclePhase.COMPILE)
public class CompilerMojo extends AbstractMojo {
// 注入优先级:命令行属性 > execution 配置 > 插件配置 > 默认值
@Parameter(property = "maven.compiler.release")
private String release;
}property指定可被命令行-Dmaven.compiler.release=21覆盖${project.build.sourceDirectory}形式的默认值通过表达式求值器(ExpressionEvaluator)注入- 复杂对象(List/Map)由配置转换器(ConfigurationConverter)从 XML 配置构建
异常与失败处理
| 异常 | 含义 | 触发点 |
|---|---|---|
| LifecycleExecutionException | 生命周期阶段执行失败 | LifecycleStarter |
| PluginExecutionException | 插件执行失败 | MojoExecutor |
| PluginConfigurationException | 插件配置解析失败 | getConfiguredMojo |
| MojoExecutionException | 插件业务逻辑抛出的异常 | Mojo.execute() |
失败时 Maven 停止当前模块后续阶段,输出失败摘要;多模块构建默认遇到失败即停(可用 -fae/-fn 调整容错)。
关键源码文件索引
| 类 | 路径 |
|---|---|
| LifecycleStarter | maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleStarter.java |
| MojoExecutor | maven-core/src/main/java/org/apache/maven/lifecycle/internal/MojoExecutor.java |
| DefaultLifecycleMappingDelegate | maven-core/src/main/java/org/apache/maven/lifecycle/internal/DefaultLifecycleMappingDelegate.java |
| DefaultMavenPluginManager | maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java |
| Lifecycle | maven-core/src/main/java/org/apache/maven/lifecycle/Lifecycle.java |
常见问题
- 自定义 execution 什么时候生效? 计算执行计划时,用户
<executions>绑定会合并进对应阶段的 MojoExecution 列表,先默认绑定后自定义绑定执行。 - 同一阶段多个目标执行顺序? 按声明顺序:默认映射目标在前,用户 executions 按其声明顺序。
- 插件类加载为什么要隔离? 不同插件可能依赖不同版本的库,Maven 为每个插件创建独立 ClassLoader(父委托给核心类加载器),避免类冲突。