4、plugins

Maven 的生命周期是抽象的,这意味着生命周期以及生命周期中每个phase(阶段)本身不做任何实际的工作,实际的任务都是依靠插件来完成。每个阶段都可以绑定一个或多个插件目标,Maven 为大多数构建步骤编写并绑定了默认插件。当用户有特殊需要的时候,可以自己配置插件来定制构建行为,甚至可以使用自己编写的 Maven 插件。

插件目标(goal)

一个插件往往能够完成多个任务(目标goal)。比如,maven-dependency-plugin 插件能够基于项目依赖做很多事情,例如:它能够分析项目依赖来帮助找出潜在的无用依赖dependency:analyze,它能够列出项目的依赖树来帮助分析依赖来源dependency:tree,它能够列出项目所有已解析的依赖dependency:list等等。

这是一种通用的写法,冒号前面是插件前缀,冒号后面是该插件的目标。类似地,还可以写出compiler:compile(这是maven-compiler-plugincompile目标)和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 生命周期中的阶段与插件目标相互绑定,用以完成实际的构建任务。插件目标可能绑定到零个或多个构建阶段。未绑定到任何阶段的插件目标可以通过直接调用从而在生命周期之外执行。执行顺序取决于调用插件目标和阶段的顺序。例如,下面的命令中的cleanpackage参数是构建阶段,而dependency:copy-dependencies是插件的目标。

mvn clean dependency:copy-dependencies package

内置插件绑定

为了能让用户几乎不用任何配置就能构建 Maven 项目,Maven 为一些主要的生命周期阶段绑定了很多插件目标,当用户通过命令行调用这些内置绑定好的阶段时,对应的插件目标就会执行相应的任务。Maven 在$MAVEN_HOME/lib/maven-core-version.jar/META-INF/plexus/components.xml中定义了三个声明周期以及绑定

clean lifecycle的绑定

<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

site lifecycle的绑定

<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

default lifecycle的绑定

<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 文件中单独定义,因为每个打包类型都不同,例如pomjarmaven-pluginwar等等。

pom打包绑定

$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>

jar打包绑定

$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.pluginsorg.codehaus.mojo2个位置查找插件,如果需要在命令行调用(代码中引入则不需要)第三方的插件,就需要在$MAVEN_HOME/conf/settings.xml中配置,例如:

<pluginGroups>
    <pluginGroup>top.ygang</pluginGroup>
</pluginGroups>

简单实现

pom.xml

创建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提供了两种方式定义插件类

方式一,实现Mojo接口

实现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;
    }
}
方式二,继承AbstractMojo抽象类(推荐)

一般来说,我们并不会直接通过实现接口的方式来定义插件,而是会采用继承maven提供的抽象类org.apache.maven.plugin.AbstractMojo来定义我们的插件类。

这个抽象类实现了Mojo接口,同时这个抽象类还实现了setLoggetLog方法,只留下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

image-20230612150052613

绑定在其他项目生命周期

随便找一个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即可看到

image-20230612151501918

常用注解

@Mojo注解

@Mojo(name = "showtime",defaultPhase = LifecyclePhase.PRE_CLEAN)

@Parameter注解

@Parameter的属性值,可以使用命令行mvn -Dparam=value进行传参,也可以在pluginconfigration中传参,不同数据类型传参格式不同

1、String、boolean、数字类型同理
@Parameter
private String myValue;
<plugin>
    <groupId>top.ygang</groupId>
    <artifactId>mydemo-maven-plugin</artifactId>
    <version>1.0</version>
    <configuration>
        <myValue>hello</myValue>
    </configuration>
</plugin>
2、File类型

如果路径是相对的(不是以/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>
3、Enum
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>
4、Array
@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>
5、Collections
@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>
6、Map
@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>
7、Properties
@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>
8、自定义类型
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>