2.2 插件是生命周期的执行者


文档摘要

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

2.2 插件是生命周期的执行者

本节摘要:插件由一个或多个 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。你没写,等于接受了默认编制。

图:goal 与阶段的绑定机制

图: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,各自绑定不同阶段、注入不同配置,默认绑定之外的一切定制行为都从这张表进。

实战判断:三种典型场景怎么选型

  • 想在某阶段追加行为:写 executions 绑定,别用直接调用(直接调用不会进流水线,别人构建时不会执行);
  • 想覆盖默认插件的行为:显式声明同名插件并写 configuration,例如给 compiler 插件配 release 参数(第 5 章插件兼容一节会用到);
  • 一次性诊断:直接调用完整 goal 名,不污染 POM。

⚠️ 常见坑:插件不写版本号。Maven 对未声明版本的插件会发出警告并自动解析"最新",不同时间、不同机器解析结果可能不同,构建从此不可复现。团队规范里插件版本与依赖版本同等级别要求锁定,第 5.4 节会用 enforcer 把这条规矩变成硬门禁。

战报小结

  • 插件由 goal 组成,goal 既可以按 packaging 的默认绑定表随阶段执行,也可以用完整名直接调用;
  • executions 是定制绑定的唯一入口:阶段、goal、执行 id、专属配置四件套;
  • 自研插件门槛不高:maven-plugin 打包加 AbstractMojo 子类加注解,百行内可交付一个可用的检查哨兵;
  • 插件版本必须锁:不锁版本的插件是构建不可复现的头号漏洞之一。

插件军团编制已明。下一节回到配置战场:同一份代码要进测试、预发、生产三个环境,Profile 与资源过滤如何配合完成"一套代码、三副面孔"。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U