Maven的生命周期与自定义插件实现方法
作者:码畜c 发布时间:2022-04-06 20:13:48
Maven 的生命周期
maven 将项目的生命周期(Lifecycle)抽象为了三种,每种生命周期中又包含了多个阶段(Phase)。也就是说生命周期与阶段的关系是一对多的。
生命周期的种类 与 不同生命周期的所有阶段都可以在maven提供的实现自定义插件需要用到的依赖中的enum里查看到:
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<version>3.6.0</version>
<scope>provided</scope>
</dependency>
public enum LifecyclePhase {
/**
* 1. Clean lifecycle : 用于构建前,清除一些资源
*/
// 执行clean前要完成的事
PRE_CLEAN("pre-clean"),
// 移除上一次构建的文件
CLEAN("clean"),
// 执行clean后要完成的事
POST_CLEAN("post-clean"),
/**
* 2. Default (build) lifecycle : 用于构建应用
*/
VALIDATE("validate"),
INITIALIZE("initialize"),
// 生成源代码,含在编译阶段中的生成的源代码,比如通过APT技术生成的代码。
// APT是在javac程序在编译期间扫描指定注解,并配合自定义的继承了AbstractProcessor的能够处理特定注解处理器类,生成源代码文件的一种技术。
// 当我们使用maven配置生成处理器并生成了源代码时,maven会在target/generated-sources下特殊分类展示这些源代码文件。仅是方便查看,
// 生成的源代码文件依旧在指定的生成目录下,并不是在target/generated-sources目录下。
GENERATE_SOURCES("generate-sources"),
// 处理源代码
PROCESS_SOURCES("process-sources"),
// 生成资源文件
GENERATE_RESOURCES("generate-resources"),
// 处理资源文件,例:复制到目标目录下:src/main/resources/* --> target/classes,为打包做好准备
PROCESS_RESOURCES("process-resources"),
// 编译源代码
COMPILE("compile"),
// 处理编译后的类文件
PROCESS_CLASSES("process-classes"),
GENERATE_TEST_SOURCES("generate-test-sources"),
PROCESS_TEST_SOURCES("process-test-sources"),
GENERATE_TEST_RESOURCES("generate-test-resources"),
PROCESS_TEST_RESOURCES("process-test-resources"),
TEST_COMPILE("test-compile"),
PROCESS_TEST_CLASSES("process-test-classes"),
TEST("test"),
PREPARE_PACKAGE("prepare-package"),
// 打包:将编译后的代码打包成指定格式的文件包,比如JAR、WAR或者EAR文件
PACKAGE("package"),
PRE_INTEGRATION_TEST("pre-integration-test"),
INTEGRATION_TEST("integration-test"),
POST_INTEGRATION_TEST("post-integration-test"),
VERIFY("verify"),
// 安装:打包后的项目安装到本地仓库,后期可以使用本地依赖
INSTALL("install"),
// 部署:将打包后的项目推送到远程仓库
DEPLOY("deploy"),
/**
* 3. Site lifecycle(很少用) : 将 POM 可以包含的各种项目信息,例如:项目描述,开发者信息等(pom中可以使用的描述标签)。通过 Maven 提供的 maven-site-plugin 插件让 Maven 生成一个 Web
* 站点(生成一组静态文件)。并能推送到远程。
*/
PRE_SITE("pre-site"),
SITE("site"),
POST_SITE("post-site"),
SITE_DEPLOY("site-deploy"),
NONE("");
private final String id;
LifecyclePhase(String id) {
this.id = id;
}
public String id() {
return this.id;
}
}
每个阶段的执行依赖前面的所有阶段的执行结果,比如:mvn clean,会先执行pre-clean。mvn post-clean,会先执行pre-clean & clean。
常用命令的区别
clean:删除项目路径下的target文件,但不会删除本地的maven仓库已经install生成的jar文件
compile:无论之前是否编译过,都会在项目路径下生成一个target目录,该目录下含有一个classes文件夹,编译生成的class文件都放在这里。
package:基于compile命令的功能,同时会在target目录下生成项目的jar或war包。可能存在打包编译失败的问题:A项目依赖B项目,打包A项目,发现引入B项目,继而打包B项目。根据B项目打包出的Jar或War包只存在B项目的target目录下,所以当打包完B项目继续编译A项目时就会报错,因为找不到所依赖的B项目的打包结果。本质原因就在于,B项目的打包结果没有在本地仓库中,maven找不到所需B项目依赖。需要通过install命令解决,也说明一个问题:打包一个项目时,先install没有在本地仓库中的依赖。
install:基于package命令的功能,同时会在本地maven仓库生成jar文件。
组合使用:
mvn clean package
mvn clean install
关于maven命令,个人理解就是在执行命令关联的插件中的某些goal。
什么是 Maven plugin
Maven Plugin本质上也是一个Maven project,打包后也就是一个jar包,帮助我们完成某些工作。
当我们通过idea自己定义一个插件maven项目时,可以选择archetype(原型)选项为:org.apache.maven.archetypes:maven-archetype-plugin
。idea 会根据此选项帮助我们快速搭建出一个插件项目的结构。注意观察:org.apache.maven.archetypes:maven-archetype-plugin,不就是一个maven 坐标嘛。
是的,可以在本地的maven仓库根据这个坐标,找到对应的jar包。而jar包中的内容,就是idea要给我们生成的项目骨架。
Maven插件的类型也分为两种:
Build plugin : 用于构建项目工程,在<build>
标签中配置的<plugin>
Reporting plugin : 用于生成网站内容(site),在<reporting>
标签中配置的<plugin>
Build类型Maven plugin与生命周期的关系
上面说到,插件会帮助我们完成一些工作,那么完成什么样的工作?什么时间节点运行?
以maven的生命周期为例:
所有生命周期的所有阶段的工作都是由maven自带的Build类型插件完成的。也就是说,maven定义了一套接口,具体实现由插件完成。
根据生命周期的多个阶段的执行顺序,序执行具体的实现插件。
对于我们自己实现的Build类型插件,也必须要挂在到某个阶段上。也就是说,maven的Build插件是需要绑定到生命周期中的某个阶段上触发执行的。
那么在引出一个问题:一个Build插件工程只能为一个生命周期阶段服务嘛?并不是。一个工程中可以编写服务于不同阶段的功能代码,每一个实现可以称之为一个goal。而且多个goal是可以挂到同个phase上的。
那么可以整理出这样一种关系:Lifecycle 1:N -> Phase 1:N -> Goal
实现一个简单的自定义插件
如果能理解上面的描述,那么下面的插件代码其实就很好理解了,甚至不需要太多的解释描述。
一、创建一个Maven plugin工程,我们不讨论过旧的基于java注释的配置方式,而是基于注解配置的方式。pom如下:
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>maven-plugin</artifactId>
<groupId>life.cqq</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>custom-plugin</artifactId>
<packaging>maven-plugin</packaging>
<name>custom-plugin</name>
<dependencies>
<!-- Maven插件类库 -->
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>3.5.0</version>
</dependency>
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<version>3.6.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 配置插件用的插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<version>3.5.2</version>
<configuration>
<!-- 插件执行命令前缀 -> mvn prefix:mojoName,例: mvn customPlugin:customMojo -->
<goalPrefix>customPlugin</goalPrefix>
<skipErrorNoDescriptorsFound>true</skipErrorNoDescriptorsFound>
</configuration>
</plugin>
</plugins>
</build>
</project>
注意:<packaging>maven-plugin</packaging>
二、 创建一个goal插件类:
package life.cqq;
import java.util.List;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
@Mojo(name = "customMojo", defaultPhase = LifecyclePhase.PACKAGE)
public class CustomMojo extends AbstractMojo {
@Parameter(defaultValue = "${project.build.directory}", property = "args", required = true)
private String args;
@Parameter(property = "options", required = true)
private List<String> options;
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
System.out.println("Custom plugin exec");
System.out.println(args);
System.out.println(options);
}
}
继承AbstractMojo
添加@Mojo注解,指定goal名称 & 生效阶段
Parameter注解:引入插件的项目中,对于引入插件配置的
<configuration><configuration/>
节点中的子节点配置项(在后面的测试工程中会体现出来)重写execute方法
一个简单的插件工程就算完成了,是不是很简单。
三、创建测试工程
先将插件项目install在本地,测试工程pom如下:
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>maven-plugin</artifactId>
<groupId>life.cqq</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>using-plugin</artifactId>
<packaging>jar</packaging>
<name>using-plugin</name>
<build>
<plugins>
<!-- 引入自定义插件 -->
<plugin>
<groupId>life.cqq</groupId>
<artifactId>custom-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- 插件参数配置 -->
<configuration>
<args>1</args>
<options>
<!-- 这里主要想体现一个问题:集合元素中子元素的标签名称是比较宽泛的。但根据一般的命名规范来说,都会将子元素统一更名为:option -->
<option1>O1</option1>
<option2>OP2</option2>
<option3>OPT3</option3>
</options>
</configuration>
<!-- 配置需要执行插件中的那些goal -->
<executions>
<execution>
<!-- 执行id -->
<id>exeCustomMojoId</id>
<!-- 生效阶段,不指定则取插件类的@Mojo注解中的defaultPhase。此处专门设定了一个与defaultPhase不同的阶段值 -->
<phase>clean</phase>
<!-- 需要运作的Goal -->
<goals>
<goal>customMojo</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
测试工程不需要查看项目代码的执行情况,专注于maven的打印日志即可。
执行goal挂载的phase:
D:\development\idea\workspace\personal\maven-plugin\using-plugin>mvn clean
[INFO] Scanning for projects...
[INFO]
[INFO] -----------------------< life.cqq:using-plugin >------------------------
[INFO] Building using-plugin 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ using-plugin ---
[INFO] Deleting D:\development\idea\workspace\personal\maven-plugin\using-plugin\target
[INFO]
[INFO] --- custom-plugin:1.0-SNAPSHOT:customMojo (exeCustomMojoId) @ using-plugin ---
Custom plugin exec
1
[OP1, OP2, OP3]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.228 s
[INFO] Finished at: 2022-09-03T17:32:48+08:00
[INFO] ------------------------------------------------------------------------
直接调用插件中的goal:
D:\development\idea\workspace\personal\maven-plugin\using-plugin>mvn customPlugin:customMojo
[INFO] Scanning for projects...
[INFO]
[INFO] -----------------------< life.cqq:using-plugin >------------------------
[INFO] Building using-plugin 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- custom-plugin:1.0-SNAPSHOT:customMojo (default-cli) @ using-plugin ---
Custom plugin exec
1
[OP1, OP2, OP3]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.207 s
[INFO] Finished at: 2022-09-03T17:57:42+08:00
[INFO] ------------------------------------------------------------------------
在 [INFO] BUILD SUCCESS
上打印了我们在自定义插件中的输出内容。
来源:https://blog.csdn.net/qq_38074398/article/details/128238563
猜你喜欢
- 泛型的概述和优势泛型概述泛型:是JDK5中引入的特性,可以在编译阶段约束操作的数据类型,并进行检查。泛型的格式:<数据类型>;
- 安装hbase首先下载hbase的最新稳定版本 http://www.apache.org/dyn/closer.cgi/hbas
- 一个线程如何知道另一线程已经结束?Thread类提供了回答此问题的方法。有两种方法可以判定一个线程是否结束。第一,可以在线程中调用isAli
- 一、项目背景在实际工作中,会遇到业务比较集中的情况,随着时间推延,这部分业务关联的mysql表就会越来越大,十分臃肿。尽管在项目架构上做了读
- 本文实例讲述了Java实现批量导入excel表格数据到数据库中的方法。分享给大家供大家参考,具体如下:1、创建导入抽象类package co
- 导入redis的jar包<!-- redis --> <dependency>  
- 配置宝塔面板javaweb运行环境详解,若出现404nignx错误也可按此教程进行检查1.准备:(解析成功的域名,本地运行完好的项目,宝塔面
- 前言在我们开发过程中,由于主流的架构都是采用前后端分离的方式,我们作为后端开发者需要为前段持续地提供运行在容器中最新代码,虽然可
- 通过之前三篇关于Spring Boot异步任务实现的博文,我们分别学会了用@Async创建异步任务、为异步任务配置线程池、使用多个线程池隔离
- 判断参数是否为空并作为查询条件@Override public Page<DemandEntity>
- 已知两个链表list1和list,2,各自非降序排列,将它们合并成另外一个链表list3,并且依然有序,要求保留所有节点。实现过程中,lis
- SlidingDrawer效果想必大家也见到过,它就是1.5模拟器上进入应用程序列表的效果。下面是截图一、简介 SlidingDr
- synchronized原理在java中,每一个对象有且仅有一个同步锁。这也意味着,同步锁是依赖于对象而存在。当我们调用某对象的synchr
- 当前使用的IDEA版本是2020.1。随着IDEA版本的升级,有些插件不再支持,而有些插件变成了收费插件,这些插件将不再推荐。以下列举的,都
- throw抛出异常的方式比较直接:if(age < 0){throw new MyException("年龄不能为负数!&q
- 首先是.select在MP查询中,默认查询所有的字段,如果有需要也可以通过select方法进行指定字段。其中要注意的细节:wrapper.s
- 一、串口连接的打开与关闭串口,即COM口,在.NET中使用 SerialPort 类进行操作。串口开启与关闭,是涉及慢速硬件的IO操作,频繁
- 楔子近期公司程序被安全扫描出 远程主机允许明文身份验证 中风险漏洞,查了下修复方案,RabbitMQ官方提供了SSL连接方式,而且 Spri
- 当异常被抛出,通常方法的执行将作一个陡峭的非线性的转向。依赖于方法是怎样编码的,异常甚至可以导致方法过早返回。这在一些方法中是一个问题。例如
- 本文实例讲述了java生成xml格式文件的方法。分享给大家供大家参考,具体如下:这里演示利用Java生成xml格式文件Demo中所用到的ja