2.2 插件是生命周期的执行者 本节摘要:插件由一个或多个 goal( mojo )组成,goal 可绑定到生命周期阶段随流水线执行,也可用完整坐标直接调用。jar 与 war 等不同 packaging 携带不同的默认绑定表。本节讲清绑定机制,并手写一个最小检查类插件走完"编写、安装、绑定、执行"全流程。 一个编制问题:兵从哪来 作战室例会上有人问:"我们 POM 里根本没写 compiler 插件,编译是谁干的?" 答案藏在 packaging 里。
本节摘要:插件由一个或多个 goal( mojo )组成,goal 可绑定到生命周期阶段随流水线执行,也可用完整坐标直接调用。jar 与 war 等不同 packaging 携带不同的默认绑定表。本节讲清绑定机制,并手写一个最小检查类插件走完"编写、安装、绑定、执行"全流程。
作战室例会上有人问:"我们 POM 里根本没写 compiler 插件,编译是谁干的?" 答案藏在 packaging 里。Maven 按 packaging 给每个生命周期阶段预置了一张默认绑定表:jar 项目在 compile 阶段绑 maven-compiler-plugin 的 compile goal,在 test 阶段绑 maven-surefire-plugin 的 test goal,在 package 阶段绑 maven-jar-plugin 的 jar goal;war 项目则在 package 阶段换成 maven-war-plugin 的 war goal。你没写,等于接受了默认编制。

两种出场方式对应两类插件:构建型插件(compiler、surefire、jar)走绑定,保证每次构建必经;工具型插件(dependency、help、enforcer 的部分用法)走直接调用,需要时才拉上战场。第 1.4 节的 dependency:tree 就是直接调用的典型案例。
需求很实际:risk-engine 的部署目录要求 jar 旁必须有一份版本说明,格式固定。与其每次手工写,不如做一个检查插件,在 verify 阶段校验说明文件是否存在并打印版本摘要。最小实现:
<!-- 插件项目本身的 POM 关键段:打包类型必须是 maven-plugin --> <groupId>com.shop.risk</groupId> <artifactId>version-check-maven-plugin</artifactId> <version>1.0.0</version> <packaging>maven-plugin</packaging> <dependencies> <!-- 插件开发依赖:注解与抽象基类来自这里 --> <dependency> <groupId>org.apache.maven</groupId> <artifactId>maven-plugin-api</artifactId> <version>3.8.6</version> </dependency> <dependency> <groupId>org.apache.maven.plugin-tools</groupId> <artifactId>maven-plugin-annotations</artifactId> <version>3.6.4</version> </dependency> </dependencies>
// 检查 mojo:绑定 verify 阶段的版本哨兵 import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; @Mojo(name = "check", threadSafe = true) public class VersionCheckMojo extends AbstractMojo { // 参数由使用方 POM 注入,默认值兜底 @Parameter(defaultValue = "${project.version}", required = true) private String projectVersion; @Parameter(defaultValue = "false") private boolean failOnError; @Override public void execute() throws MojoExecutionException { getLog().info("版本哨兵:当前构建版本 " + projectVersion); if (projectVersion.endsWith("-SNAPSHOT") && failOnError) { // 快照版本禁止通过校验:发布场景的硬闸 throw new MojoExecutionException("发布构建禁止 SNAPSHOT 版本"); } getLog().info("版本检查通过"); } }
开发流程四步:mvn install 把插件装进本地仓库;在使用方 POM 声明插件并绑定阶段;跑一次构建观察日志;按需加参数。使用方声明如下:
<build> <plugins> <plugin> <groupId>com.shop.risk</groupId> <artifactId>version-check-maven-plugin</artifactId> <version>1.0.0</version> <executions> <!-- executions 是绑定的核心:goal 挂到哪个阶段、叫什么执行 id --> <execution> <id>verify-version</id> <phase>verify</phase> <goals> <goal>check</goal> </goals> <configuration> <failOnError>true</failOnError> </configuration> </execution> </executions> </plugin> </plugins> </build>
执行 mvn verify,日志里会出现 version-check-maven-plugin:1.0.0:check (verify-version) 一行——格式与 2.1 节的日志拆解法完全一致。executions 的语义务必吃透:一个插件可有多条 execution,各自绑定不同阶段、注入不同配置,默认绑定之外的一切定制行为都从这张表进。
⚠️ 常见坑:插件不写版本号。Maven 对未声明版本的插件会发出警告并自动解析"最新",不同时间、不同机器解析结果可能不同,构建从此不可复现。团队规范里插件版本与依赖版本同等级别要求锁定,第 5.4 节会用 enforcer 把这条规矩变成硬门禁。
插件军团编制已明。下一节回到配置战场:同一份代码要进测试、预发、生产三个环境,Profile 与资源过滤如何配合完成"一套代码、三副面孔"。