本章介绍Maven生命周期的概念,生命周期之所以重要,是因为我们每运行一次maven指令,本质上都是在运行生命周期的某个phase。实际使用Maven,都是命令,一种方式是直接的在cmd上执行输入的maven指令,另外一种是通过UI方式,点击某个菜单,它在底层去运行相关的指令。理解生命周期对于理解命令大有帮助。
本章主要分为四个部分
- 基本的概念
- 内置生命周期
- 自定义生命周期,编写HelloWorld插件
- 扩展内置的生命周期
1、概念
在介绍上述四个部分之前,需要理解lifeCycle(生命周期),plugin(插件)的概念。
1.1 lifecycle & phase
引用原著中的定义:
A Maven build lifecycle consists of a set of well-defined phases, Each phase groups a set of goals defined by Maven plugins and the lifecycle defines the order of execution
这段话有三个含义。
第一个含义,lifecycle与phase之间的关系,lifecycle是由一组预定义的phase组成的。
第二个含义,phase与plugin之间的关系,phase与goals是一对多的关系,plugin是由一到多个goal组成的。
第三个含义,lifecycle定义了phase的执行顺序。
1.2 plugin & goal
引用原著中对plugin的定义:
A Maven plugin is a collection of goals where each goal is responsible for performing a specific action
它是goals的集合,类似于Controller与methods之间的关系。
1.3 phase 与 goal的关系
类比Web应用,后端Controller提供了一组接口,它类似于goal的作用,而前端提供了一组按钮,它类似于phase的作用。
当我们点击按钮时,真正运行的是后台Controller中的代码和JS的代码。与此类似,当我们运行mvn指令时,真正运行的是plugin中goal的代码,controller与按钮之间建立关系是通过ajax请求建立的,phase与goal建立关系是通过子标签建立的。
当按钮没有与后台建立关系,自然点击按钮则不会有任何效果。phase也是一样的,若phase与goal之间没有建立关系,执行指令没有任何效果。
区别在于前端按钮与接口的关系通常是一对一,而且有什么参数之类的。而maven中phase与goal之间的关系是1对多的。
理解这些之后,再介绍内置的maven生命周期会很容易理解。
2、内置生命周期
2.1 clean
2.1.1 图片
2.1.2 phases
clean生命周期有三个phase, pre-clean, clean, post-clean。类似于spring拦截器的那个方法。
pre-clean在清理之前运行
clean运行清理的逻辑。
post-clean在清理之后运行
执行mvn help:describe -Dcmd=clean,查看clean的phase。结果如下:
'clean' is a phase within the 'clean' lifecycle, which has the following phases: * pre-clean: Not defined * clean: org.apache.maven.plugins:maven-clean-plugin:2.5:clean * post-clean: Not defined
可以清晰的看到,pre-clean, post-clean没有goal与之关联。clean(phase)与2.5版本的maven-clean-plugin的clean(goal)关联。
2.1.3 插件
执行mvn help:describe -Dplugin=clean,查看clean插件。结果如下:
Name: Apache Maven Clean Plugin Description: The Maven Clean Plugin is a plugin that removes files generated at build-time in a project's directory. Group Id: org.apache.maven.plugins Artifact Id: maven-clean-plugin Version: 3.1.0 Goal Prefix: clean This plugin has 2 goals: clean:clean Description: Goal which cleans the build.This attempts to clean a project's working directory of the files that were generated at build-time. By default, it discovers and deletes the directories configured in project.build.directory,project.build.outputDirectory, project.build.testOutputDirectory, and project.reporting.outputDirectory. Files outside the default may also be included in the deletion by configuring the filesets tag. clean:help Description: Display help information on maven-clean-plugin. Call mvn clean:help -Ddetail=true -Dgoal=<goal-name> to display parameter details. For more information, run 'mvn help:describe [...] -Ddetail'
可以看到clean插件有两个goal,分别是clean和help,运行它们的命令分别是mvn clean:clean, mvn clean:help。
clean插件的clean删除以下四个目录
- project.build.directory:编译的根目录,通常是target
- project.build.outputDirectory:编译之后的字节码目录,通常是target/classes
- project.build.testOutputDirectory:测试代码编译之后的字节码目录,通常是target/test-classes
- project.reporting.outputDirectory: 项目报告的存放目录,通常是target/site。
2.1.4 指令(mvn clean)
因为clean名称很重复,所以clean生命周期时,比较容易混淆。Lifecycle,phase,pluginName, goal它们四个的名称都是clean。
问题1:执行mvn clean究竟是在执行什么?
答:它在执行clean生命周期,会依次执行pre-clean,clean,post-clean,由于只有clean有对应的goal,所以本质上只执行了clean插件的clean。
问题2:指令执行的方式有哪几种?
答:有三种,执行生命周期,例如mvn clean。执行生命周期的某个phase,例如mvn clean:clean。执行插件的某个goal,例如mvn clean:help。
2.2 default
2.2.1 phases
default有23个,它大致分为五个阶段。准备,编译资源,编译测试代码并运行测试代码,打包,部署。
执行mvn help:describe -Dcmd=validate,显示的结果如下:
It is a part of the lifecycle for the POM packaging 'jar'. This lifecycle includes the following phases: * validate: Not defined * initialize: Not defined * generate-sources: Not defined * process-sources: Not defined * generate-resources: Not defined * process-resources: org.apache.maven.plugins:maven-resources- plugin:2.6:resources * compile: org.apache.maven.plugins:maven-compiler-plugin:3.1:compile * process-classes: Not defined * generate-test-sources: Not defined * process-test-sources: Not defined * generate-test-resources: Not defined * process-test-resources: org.apache.maven.plugins:maven-resources- plugin:2.6:testResources * test-compile: org.apache.maven.plugins:maven-compiler- plugin:3.1:testCompile * process-test-classes: Not defined * test: org.apache.maven.plugins:maven-surefire-plugin:2.12.4:test * prepare-package: Not defined * package: org.apache.maven.plugins:maven-jar-plugin:2.4:jar * pre-integration-test: Not defined * integration-test: Not defined * post-integration-test: Not defined * verify: Not defined * install: org.apache.maven.plugins:maven-install-plugin:2.4:install * deploy: org.apache.maven.plugins:maven-deploy-plugin:2.7:deploy
上述列出了23个phase,以及它们对应的goal。
2.2.1.1 准备
它包含两个phase
validate,校验pom文件的正确性以及完整性。它没有与之对应的goal,执行mvn default:validate没有任何效果。通常是IDE去校验pom文件的正确性。
initialize,初始化目录结构,获取必要的属性。它也没有对应的goal。
可以执行mvn validate -X > result.txt,去查看命令的运行日志。二者都没有任何效果。
2.2.1.2 编译源代码
它包含六个phase
generate-sources,生成源代码。没有对应的goal
process-sources,处理源代码。没有对应的goal
generate-resources,生成配置文件。没有对应的goal
process-resources,处理配置文件,创建编译输出目录,将resources配置文件拷贝到对应的目录下。有对应的goal。
compile: 编译源代码,并拷贝到对应的输出目录。有对应的goal,
process-classes:处理编译源代码之后的字节文件,没有对应的goal。
执行mvn compile -X >result.txt,去查看命令的运行日志,在target目录下去查看,发现只生成了classes和配置文件。
2.2.1.3 编译测试代码
与编译源代码的phase功能和数量基本相同,名称中多了test。
generate-test-resources:生成测试代码。没有对应的goal
process-test-resources:处理测试代码。没有对应的goal
generate-test-resources:生成测试配置文件。没有对应的goal
process-test-resources:处理测试配置文件,创建编译输出目录,若已存在不创建,将test目录下的resources配置文件拷贝到对应的目录下。有对应的goal。
test-compile:编译测试代码,并拷贝到对应的输出目录。有对应的goal。
process-test-classes:处理并编译测试代码之后的字节文件,没有对应的goal。
执行mvn test-compile -x > result.txt,去查看命令的运行日志,在target目录下去查看,发现只生成了测试代码的字节码和配置文件(test-classes目录)。
2.2.1.4 打包
它有三个phase
pre-package:在打包之前运行
package:打包的逻辑,它对应maven-jar-plugins,执行此命令,会生成jar包。
post-package:在打包之后运行。
执行mvn package -x > result.txt,查看运行的日志。在target目录下去查看,会发现生成了jar包。
Jar包默认的名字是name + version。可以通过配置build | finalName标签设置Jar的名称。
2.2.1.5 集成测试
2.2.1.6 部署
部署有三个phase
verify:校验package是否正确,没有对应的goal
install:在本地仓库部署,部署完成之后,可以在本地版本库查看到对应的目录,它对应的插件是maven-install-plugin。
deploy:在远程仓库部署,部署完成之后,可以在远程仓库上查看。它对应的插件是maven-deploy-plugin。使用前需要配置远程仓库。
执行git install -X > result.txt。查看日志,在本地版本库可以查看到相应的目录结构,例如项目group Id为com.test,artifactId为sample,也可以在本地版本库中找到com/test/sample目录结构,查看目录下的内容。
2.3 site
2.3.1 图片
2.3.2 phases
site是用于生成项目报告的,它有pre-site,site, post-site, site-deploy四个phase。
pre-site在生成报告之前执行
site执行报告的逻辑
post-site在生成报告之后执行
site-deploy将生成的报告拷贝到项目Jar或war包中。
执行mvn help:describe -Dcmd=site,运行结果如下:
'site' is a phase within the 'site' lifecycle, which has the following phases: * pre-site: Not defined * site: org.apache.maven.plugins:maven-site-plugin:3.3:site * post-site: Not defined * site-deploy: org.apache.maven.plugins:maven-site-plugin:3.3:deploy
只有site,site-deploy有对应的goal,它们存在于maven-site-plugin中,版本是3.3。
2.3.3 插件
执行mvn help:describe -Dplugin=site,运行结果会发现它有九个goal。
- attach-descriptor:添加site.xml配置文件,
- deploy:把生成的报告部署到远程仓库。
- effective-site:显示site.xml文件的内容。
- help:显示site插件的用法
- jar:把生成的报告打包到jar中
- run:启动web服务,它可以查看生成的报告。
- site:执行生成报告的逻辑过程
- stage:
- stage-deploy:
2.3.4 指令
执行mvn site相关的指令有两类,第一类是插件:goal,例如执行mvn site:help。第二类是生命周期:phase,这种情况下生命周期可以省略,例如执行mvn site:site,mvn pre-site。
pre-site,post-site没有对应的goal,没有任何结果。
3、生命周期与phase
建立生命周期与phase之间的关系,是通过maven-core-version.jar中的META-INF/plexus/components.xml定义的,它的格式如下:
<component> <role>org.apache.maven.lifecycle.Lifecycle</role> <implementation>org.apache.maven.lifecycle.Lifecycle</implementation> <role-hint>default</role-hint> <configuration> <id>default</id> <phases> <!-- 1到多个phase --> <phase>validate</phase> </phases> </configuration> </component>
role指定component的类型,以及它暴露在外的接口类,必须是org.apache.maven.lifecycle.Lifecycle。
implementation指定接口的具体实现类。
role和role-hint是联合主键,唯一标识一个component元素,当role相同时,必须指定role-hint,通常与Lifecycle同名。
id指定Lifecycle的名称
phases指定一个到多个phase,例如default生命周期指定23个phase。
3.1 示例
<component> <role>org.apache.maven.lifecycle.Lifecycle</role> <implementation>org.apache.maven.lifecycle.Lifecycle</implementation> <role-hint>clean</role-hint> <configuration> <id>clean</id> <phases> <phase>pre-clean</phase> <phase>clean</phase> <phase>post-clean</phase> </phases> <default-phases> <clean> org.apache.maven.plugins:maven-clean-plugin:2.5:clean </clean> </default-phases> </configuration> </component>
default-phases用于建立phase与goal之间的关系。查看default生命周期时看到没有goal与之关联。建立default生命周期phase与goal关系的步骤如下:
- 第一步,在项目中配置pom.xml,在build中定义插件XX_plugin。
- 第二步,XX_plugin中的components.xml中定义了phase与goal之间的关系,不同的plugin,phase与goal之间的关系是不同的。
4、HelloWorld插件示例
演示Hello World插件编写的过程,并验证。具体步骤如下:
第一步:创建Maven plugin项目,与创建普通的Maven项目步骤相同,区别在于选择archetype时,选择maven-archetype-mojo。
第二步:创建src/main/resources资源文件夹,在下面创建META-INF文件夹,创建components.xml,它用于定义插件与goal之间的关系。示例如下:
<?xml version="1.0" encoding="UTF-8"?> <component-set> <components> <component> <role>org.apache.maven.lifecycle.Lifecycle</role> <role-hint>HelloWorld</role-hint> <implementation>org.apache.maven.lifecycle.Lifecycle</implementation> <configuration> <id>hello_world</id> <phases> <phase>hello_world</phase> </phases> <default-phases> <hello_world> org.example:HelloWorld:hello_world </hello_world> </default-phases> </configuration> </component> </components> </component-set>
它与其他生命周期的格式基本相同,default-phases下的格式为<goal></goal>,其中的值为groupId:artifactId:goal文档注释。
第三步,创建HelloWorldMojo对象,继承AbstractMojo, 实现execute方法,本示例中只是打印一句”Hello Maven World”, 它需要在文档注解中添加@goal。代码如下:
/** * @goal hello_world * @requiresProject false */ public class HelloWorldMojo extends AbstractMojo { public void execute() throws MojoExecutionException, MojoFailureException { System.out.println("Hello Maven World"); } }
第四步,打包,执行mvn clean install。它会安装在你本地的maven仓库中
第五步,创建任意maven项目,引入插件,代码如下:
<build> <plugins> <plugin> <groupId>org.example</groupId> <artifactId>HelloWorld</artifactId> <version>1.0-SNAPSHOT</version> <extensions>true</extensions> </plugin> <plugins> </build>
第六步,验证,执行mvn hello_world,结果如下:
[INFO] org.apache.maven.cli.event.ExecutionEventLogger - --- HelloWorld:1.0-SNAPSHOT:hello_world (default-hello_world) @ jackson-demo --- Hello Maven World [INFO] org.apache.maven.cli.event.ExecutionEventLogger - ------------------------------------------------------------------------
可以看到插件已正常执行成功。
关键点
1. XXMojo与@goal主键建立关系,@goal注解与goal建立关系
2. 项目的role定义的是org.apache.maven.lifecycle.Lifecycle,所以可以直接执行mvn goal, 否则它就只是一个普通的插件,执行时格式为mvn pluginId:goal。pluginId格式为groupId:artifactId
3. plugin与goal是一对多的关系。goal与@goal注解是一对多的关系,多个时使用逗号分隔。@goal与XXMojo是一对一的关系。
厘清生命周期,phase,plugin,goal四者之间的关系是本章的核心知识点。