Maven 的生命周期是抽象的,这意味着生命周期以及生命周期中每个phase(阶段)本身不做任何实际的工作,实际的任务都是依靠插件来完成。每个阶段都可以绑定一个或多个插件目标,Maven 为大多数构建步骤编写并绑定了默认插件。当用户有特殊需要的时候,可以自己配置插件来定制构建行为,甚至可以使用自己编写的 Maven 插件。
一个插件往往能够完成多个任务(目标goal)。比如,maven-dependency-plugin
插件能够基于项目依赖做很多事情,例如:它能够分析项目依赖来帮助找出潜在的无用依赖dependency:analyze
,它能够列出项目的依赖树来帮助分析依赖来源dependency:tree
,它能够列出项目所有已解析的依赖dependency:list
等等。
这是一种通用的写法,冒号前面是插件前缀,冒号后面是该插件的目标。类似地,还可以写出compiler:compile
(这是maven-compiler-plugin
的compile
目标)和surefire:test
(这是maven-surefire-plugin
的 test 目标)。
调用插件的目标,例如大部分插件都有的help目标,用来查看插件的说明以及goal说明
mvn dependency:help
# 如果是第三方的插件,可以使用mvn [groupId]:[artifactId]:[version]:[goal]来调用
# 命令中插件版本是可以省略的,maven会自动找到这个插件最新的版本运行,不过最好我们不要省略版本号,每个版本的插件功能可能不一样,为了保证任何情况下运行效果的一致性,强烈建议指定版本号。
mvn org.apache.maven.plugins:maven-war-plugin:3.2.3:help
mvn org.springframework.boot:spring-boot-maven-plugin:2.2.0.RELEASE:help
Maven 生命周期中的阶段与插件目标相互绑定,用以完成实际的构建任务。插件目标可能绑定到零个或多个构建阶段。未绑定到任何阶段的插件目标可以通过直接调用从而在生命周期之外执行。执行顺序取决于调用插件目标和阶段的顺序。例如,下面的命令中的clean
和package
参数是构建阶段,而dependency:copy-dependencies
是插件的目标。
mvn clean dependency:copy-dependencies package
为了能让用户几乎不用任何配置就能构建 Maven 项目,Maven 为一些主要的生命周期阶段绑定了很多插件目标,当用户通过命令行调用这些内置绑定好的阶段时,对应的插件目标就会执行相应的任务。Maven 在$MAVEN_HOME/lib/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>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>
生命周期阶段 | 插件:目标 | 执行任务 |
---|---|---|
pre-clean | ||
clean | maven-clean-plugin:clean | 删除项目的输出目录 |
post-clean |
<component>
<role>org.apache.maven.lifecycle.Lifecycle</role>
<implementation>org.apache.maven.lifecycle.Lifecycle</implementation>
<role-hint>site</role-hint>
<configuration>
<id>site</id>
<phases>
<phase>pre-site</phase>
<phase>site</phase>
<phase>post-site</phase>
<phase>site-deploy</phase>
</phases>
<default-phases>
<site>org.apache.maven.plugins:maven-site-plugin:3.3:site</site>
<site-deploy>org.apache.maven.plugins:maven-site-plugin:3.3:deploy</site-deploy>
</default-phases>
</configuration>
</component>
生命周期阶段 | 插件:目标 |
---|---|
pre-site | |
site | maven-site-plugin:site |
post-site | |
site-deploy | maven-site-plugin:deploy |
<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>
<phase>validate</phase>
<phase>initialize</phase>
<phase>generate-sources</phase>
<phase>process-sources</phase>
<phase>generate-resources</phase>
<phase>process-resources</phase>
<phase>compile</phase>
<phase>process-classes</phase>
<phase>generate-test-sources</phase>
<phase>process-test-sources</phase>
<phase>generate-test-resources</phase>
<phase>process-test-resources</phase>
<phase>test-compile</phase>
<phase>process-test-classes</phase>
<phase>test</phase>
<phase>prepare-package</phase>
<phase>package</phase>
<phase>pre-integration-test</phase>
<phase>integration-test</phase>
<phase>post-integration-test</phase>
<phase>verify</phase>
<phase>install</phase>
<phase>deploy</phase>
</phases>
</configuration>
</component>
生命周期阶段 | 插件:目标 | 执行任务 |
---|---|---|
process-resources | maven-resources-plugin:resources | 复制主资源文件至主输出目录 |
compile | maven-compiler-plugin:compile | 编译主代码至主输出目录 |
process-test-resources | maven-resources-plugin:testResources | 复制测试资源文件至测试输出目录 |
test-compile | maven-compiler-plugin:testCompile | 编译测试代码至测试输出目录 |
test | maven-surefile-plugin:test | 执行测试用例 |
package | maven-jar-plugin:jar | 创建项目jar包 |
install | maven-install-plugin:install | 将输出构件安装到本地仓库 |
deploy | maven-deploy-plugin:deploy | 将输出的构件部署到远程仓库 |
default 生命周期的插件绑定在 $MAVEN_HOME/lib/maven-core-version.jar/META-INF/plexus/default-bindings.xml
文件中单独定义,因为每个打包类型都不同,例如pom
、jar
、maven-plugin
、war
等等。
在 $MAVEN_HOME/lib/maven-core-version.jar/META-INF/plexus/default-bindings.xml
文件中关于 pom
打包类型的默认绑定如下:
<component>
<role>org.apache.maven.lifecycle.mapping.LifecycleMapping</role>
<role-hint>pom</role-hint>
<implementation>org.apache.maven.lifecycle.mapping.DefaultLifecycleMapping</implementation>
<configuration>
<lifecycles>
<lifecycle>
<id>default</id>
<!-- START SNIPPET: pom-lifecycle -->
<phases>
<install>org.apache.maven.plugins:maven-install-plugin:2.4:install</install>
<deploy>org.apache.maven.plugins:maven-deploy-plugin:2.7:deploy</deploy>
</phases>
<!-- END SNIPPET: pom-lifecycle -->
</lifecycle>
</lifecycles>
</configuration>
</component>
在 $MAVEN_HOME/lib/maven-core-version.jar/META-INF/plexus/default-bindings.xml
文件中关于 jar
打包类型的默认绑定如下:
<component>
<role>org.apache.maven.lifecycle.mapping.LifecycleMapping</role>
<role-hint>jar</role-hint>
<implementation>org.apache.maven.lifecycle.mapping.DefaultLifecycleMapping</implementation>
<configuration>
<lifecycles>
<lifecycle>
<id>default</id>
<!-- START SNIPPET: jar-lifecycle -->
<phases>
<process-resources>org.apache.maven.plugins:maven-resources-plugin:2.6:resources</process-resources>
<compile>org.apache.maven.plugins:maven-compiler-plugin:3.1:compile</compile>
<process-test-resources>org.apache.maven.plugins:maven-resources-plugin:2.6:testResources</process-test-resources>
<test-compile>org.apache.maven.plugins:maven-compiler-plugin:3.1:testCompile</test-compile>
<test>org.apache.maven.plugins:maven-surefire-plugin:2.12.4:test</test>
<package>org.apache.maven.plugins:maven-jar-plugin:2.4:jar</package>
<install>org.apache.maven.plugins:maven-install-plugin:2.4:install</install>
<deploy>org.apache.maven.plugins:maven-deploy-plugin:2.7:deploy</deploy>
</phases>
<!-- END SNIPPET: jar-lifecycle -->
</lifecycle>
</lifecycles>
</configuration>
</component>
我们之前在调用一个插件的goal的时候,使用的命令是这样的:mvn [groupId]:[artifactId]:[version]:[goal]
,比较麻烦,maven也为了我们使用插件方便,提供了插件前缀来帮我们解决这个问题。
我们在命名项目名称也就是artifactId
的时候,如果满足xxx-maven-plugin
这样的格式,那么maven默认就会认为xxx
就是插件前缀,我们在调用goal的时候,可以使用xxx:goal
的格式来进行调用
注意:maven默认会在仓库org.apache.maven.plugins
和 org.codehaus.mojo
2个位置查找插件,如果需要在命令行调用(代码中引入则不需要)第三方的插件,就需要在$MAVEN_HOME/conf/settings.xml
中配置,例如:
<pluginGroups>
<pluginGroup>top.ygang</pluginGroup>
</pluginGroups>
创建maven项目,并修改pom.xml
文件
<?xml version="1.0" encoding="UTF-8"?>
<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">
<modelVersion>4.0.0</modelVersion>
<groupId>top.ygang</groupId>
<artifactId>mydemo-maven-plugin</artifactId>
<version>1.0</version>
<!-- 更改打包方式为maven插件 -->
<packaging>maven-plugin</packaging>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!-- 引入插件开发需要的相关基础类 -->
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>3.0</version>
</dependency>
<!-- 引入插件开发需要的相关注解 -->
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<version>3.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
maven插件是需要对外提供各种实际应用能力的,在这个步骤中,我们将定义我们这个插件对外提供的实际能力,这样其他项目想要使用我们插件的时候,就可以选择对应的goal
来执行了。
Mojo是 Maven plain Old Java Object
的缩写,Mojo 映射到一个插件目标,插件由一个或多个 Mojo 组成。
maven提供了两种方式定义插件类
实现org.apache.maven.plugin.Mojo
接口,并添加@Mojo
注解
@Mojo(name = "showtime")
public class ShowTimeMojo implements Mojo {
/**
* 核心方法,当使用mvn命令调用插件的目标的时候,最后具体调用的就是这个方法
* @throws MojoExecutionException
* @throws MojoFailureException
*/
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
}
/**
* 注入一个标准的Maven日志记录器,允许这个Mojo向用户传递事件和反馈
* @param log
*/
@Override
public void setLog(Log log) {
}
/**
* 获取注入的日志记录器
* @return
*/
@Override
public Log getLog() {
return null;
}
}
一般来说,我们并不会直接通过实现接口的方式来定义插件,而是会采用继承maven提供的抽象类org.apache.maven.plugin.AbstractMojo
来定义我们的插件类。
这个抽象类实现了Mojo
接口,同时这个抽象类还实现了setLog
和getLog
方法,只留下execute
方法给开发人员去实现。这个类中Log默认可以向控制台输出日志信息,maven中自带的插件都继承这个类,一般情况下我们开发插件目标可以直接继承这个类,然后实现execute()
方法就可以了。
需要在类上面使用@Mojo
注解。这样maven在执行我们的插件的时候,才能够找到我们的入口类。
@Mojo(name = "showtime")
public class ShowTimeMojo extends AbstractMojo {
/**
* 核心方法,当使用mvn命令调用插件的目标的时候,最后具体调用的就是这个方法
* @throws MojoExecutionException
* @throws MojoFailureException
*/
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
String currentTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
getLog().info("MyMvnPlugin is running , current time is " + currentTime);
}
}
执行命令打包并发布插件到本地仓库,打包完成后我们就可以在我们本地仓库中找到我们的插件了
mvn clean install
由于我们已经将插件发送到仓库了,所以我们可以在控制台使用命令来执行插件
mvn top.ygang:mymvnplugin:1.0:showtime
# 或者使用插件前缀
mvn mydemo:showtime
随便找一个maven项目,在pom.xml
文件中将自定义插件的showtime
这个goal绑定到pre-clean
阶段
<build>
<plugins>
<plugin>
<groupId>top.ygang</groupId>
<artifactId>mydemo-maven-plugin</artifactId>
<version>1.0</version>
<executions>
<execution>
<id>clean</id>
<phase>pre-clean</phase>
<goals>
<goal>showtime</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
执行命令mvn clean
即可看到
name
:指定插件的goaldefaultPhase
:指定当前goal绑定的phase@Mojo(name = "showtime",defaultPhase = LifecyclePhase.PRE_CLEAN)
property
:属性名称,默认为字段变量名defaultValue
:属性默认值,可以使用pom.xml
中的所有变量(包括用户定义变量),例如${basedir}
@Parameter
的属性值,可以使用命令行mvn -Dparam=value
进行传参,也可以在plugin
的configration
中传参,不同数据类型传参格式不同
@Parameter
private String myValue;
<plugin>
<groupId>top.ygang</groupId>
<artifactId>mydemo-maven-plugin</artifactId>
<version>1.0</version>
<configuration>
<myValue>hello</myValue>
</configuration>
</plugin>
如果路径是相对的(不是以/
或C:
之类的驱动器号开头),则路径是相对于包含POM的目录的。
@Parameter
private File myValue;
<plugin>
<groupId>top.ygang</groupId>
<artifactId>mydemo-maven-plugin</artifactId>
<version>1.0</version>
<configuration>
<myValue>c:\temp</myValue>
</configuration>
</plugin>
public enum Color {
GREEN,
RED,
BLUE
}
@Parameter
private Color myValue;
<plugin>
<groupId>top.ygang</groupId>
<artifactId>mydemo-maven-plugin</artifactId>
<version>1.0</version>
<configuration>
<myValue>BLUE</myValue>
</configuration>
</plugin>
@Parameter
private String[] myValue;
<plugin>
<groupId>top.ygang</groupId>
<artifactId>mydemo-maven-plugin</artifactId>
<version>1.0</version>
<configuration>
<myValue>
<param>value1</param>
<param>value2</param>
</myValue>
</configuration>
</plugin>
@Parameter
private List myValue;
<plugin>
<groupId>top.ygang</groupId>
<artifactId>mydemo-maven-plugin</artifactId>
<version>1.0</version>
<configuration>
<myValue>
<param>value1</param>
<param>value2</param>
</myValue>
</configuration>
</plugin>
@Parameter
private Map myValue;
<plugin>
<groupId>top.ygang</groupId>
<artifactId>mydemo-maven-plugin</artifactId>
<version>1.0</version>
<configuration>
<myValue>
<key1>value1</key1>
<key2>value2</key2>
</myValue>
</configuration>
</plugin>
@Parameter
private Properties myValue;
<plugin>
<groupId>top.ygang</groupId>
<artifactId>mydemo-maven-plugin</artifactId>
<version>1.0</version>
<configuration>
<myValue>
<property>
<name>propertyName1</name>
<value>propertyValue1</value>
<property>
<property>
<name>propertyName2</name>
<value>propertyValue2</value>
<property>
</myValue>
</configuration>
</plugin>
public class MyClazz {
private String field1;
private String field2;
}
@Parameter
private MyClazz myValue;
<plugin>
<groupId>top.ygang</groupId>
<artifactId>mydemo-maven-plugin</artifactId>
<version>1.0</version>
<configuration>
<myValue>
<field1>value1</field1>
<field2>value2</field2>
</myValue>
</configuration>
</plugin>