介绍

本指南旨在帮助用户为 Maven 开发 Java 插件。

重要通知:插件命名约定和 Apache Maven 商标

您通常会为您的插件命名<yourplugin>-maven-plugin

强烈建议不要调用它maven-<yourplugin>-plugin(注意“Maven”在插件名称的开头),因为它是Apache Maven 团队使用 groupId维护的官方 Apache Maven 插件的保留命名模式。使用这种命名模式是对 Apache Maven 商标的侵犯。org.apache.maven.plugins

你的第一个插件

在本节中,我们将构建一个简单的插件,其目标是不带任何参数,并且在运行时仅在屏幕上显示一条消息。在此过程中,我们将介绍设置项目以创建插件的基础知识、定义目标代码的 Java mojo 的最小内容,以及执行 mojo 的几种方法。

你的第一个魔力

简而言之,Java mojo 仅由代表一个插件目标的单个类组成。不需要像 EJB 这样的多个类,尽管包含许多类似 mojo 的插件可能会使用 mojo 的抽象超类来整合所有 mojo 共有的代码。

在处理源代码树以查找 mojos 时,查找带有Java 5 注释或 " " javadoc 注释的类。任何带有此注解的类都包含在插件配置文件中。 plugin-tools@Mojogoal

一个简单的魔力

下面列出的是一个没有参数的简单 mojo 类。这与 mojo 一样简单。列出之后是对源的各个部分的描述。

package sample.plugin;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Mojo;

/**
 * Says "Hi" to the user.
 *
 */
@Mojo( name = "sayhi")
public class GreetingMojo extends AbstractMojo
{
    public void execute() throws MojoExecutionException
    {
        getLog().info( "Hello, world." );
    }
}
  • 除了方法之外,该类org.apache.maven.plugin.AbstractMojo提供了实现 mojo 所需的大部分基础设施execute
  • 注释 " @Mojo" 是必需的,它控制着 mojo 的执行方式和时间。
  • execute方法可以抛出两个异常:
    • org.apache.maven.plugin.MojoExecutionException如果出现意外问题。抛出此异常会导致显示“BUILD ERROR”消息。
    • org.apache.maven.plugin.MojoFailureException如果出现预期的问题(例如编译失败)。抛出此异常会导致显示“BUILD FAILURE”消息。
  • getLog方法(定义在 中AbstractMojo)返回一个类似 log4j 的记录器对象,它允许插件在“调试”、“信息”、“警告”和“错误”级别创建消息。此记录器是向用户显示信息的公认方式。请查看检索 Mojo Logger部分以获取有关其正确使用的提示。

Mojo API 规范描述了所有 Mojo 注释。

项目定义

一旦为插件编写了 mojo,就该构建插件了。要正确执行此操作,项目的描述符需要正确设置许多设置:

groupId 这是插件的组 ID,应该与 mojos 使用的包的公共前缀匹配
artifactId 这是插件的名称
version 这是插件的版本
packaging 这应该设置为“ maven-plugin
dependencies 必须向 Maven 插件工具 API 声明依赖项以解析“ AbstractMojo”和相关类

下面列出的是示例 mojo 项目的 pom 的说明,其参数设置如上表所述:

<project>
  <modelVersion>4.0.0</modelVersion>

  <groupId>sample.plugin</groupId>
  <artifactId>hello-maven-plugin</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>maven-plugin</packaging>

  <name>Sample Parameter-less Maven Plugin</name>

  <dependencies>
    <dependency>
      <groupId>org.apache.maven</groupId>
      <artifactId>maven-plugin-api</artifactId>
      <version>3.0</version>
      <scope>provided</scope>
    </dependency>

    <!-- dependencies to annotations -->
    <dependency>
      <groupId>org.apache.maven.plugin-tools</groupId>
      <artifactId>maven-plugin-annotations</artifactId>
      <version>3.4</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>
</project>

构建插件

maven-plugin与打包定义的标准构建生命周期绑定的插件目标很少:

compile 为插件编译 Java 代码
process-classes 提取数据以构建插件描述符
test 运行插件的单元测试
package 构建插件 jar
install 在本地存储库中安装插件 jar
deploy 将插件 jar 部署到远程存储库

有关更多详细信息,您可以查看包装的详细绑定maven-plugin

执行你的第一个 Mojo

执行新插件最直接的方法是直接在命令行中指定插件目标。为此,您需要hello-maven-plugin在项目中配置插件:

...
  <build>
    <plugins>
      <plugin>
        <groupId>sample.plugin</groupId>
        <artifactId>hello-maven-plugin</artifactId>
        <version>1.0-SNAPSHOT</version>
      </plugin>
    </plugins>
  </build>
...

并且,您需要以以下形式指定完全限定的目标:

mvn groupId:artifactId:version:goal

例如,要运行示例插件中的简单 mojo,您可以mvn sample.plugin:hello-maven-plugin:1.0-SNAPSHOT:sayhi在命令行中输入“”。

提示version运行独立目标不需要。

缩短命令行

有几种方法可以减少所需的输入量:

  • 如果您需要运行安装在本地存储库中的插件的最新版本,则可以省略其版本号。所以只需使用“ mvn sample.plugin:hello-maven-plugin:sayhi”来运行你的插件。
  • 您可以为插件分配一个缩短的前缀,例如mvn hello:sayhi. 如果您遵循使用约定${prefix}-maven-plugin(或者maven-${prefix}-plugin如果插件是 Apache Maven 项目的一部分),这将自动完成。您也可以通过额外的配置来分配一个 - 更多信息请参见Plugin Prefix Mapping 简介
  • 最后,您还可以将插件的 groupId 添加到默认搜索的 groupId 列表中。为此,您需要将以下内容添加到您的${user.home}/.m2/settings.xml文件中:
    <pluginGroups>
      <pluginGroup>sample.plugin</pluginGroup>
    </pluginGroups>

此时,您可以使用 " mvn hello:sayhi" 运行 mojo。

将 Mojo 附加到构建生命周期

您还可以配置插件以将特定目标附加到构建生命周期的特定阶段。这是一个例子:

  <build>
    <plugins>
      <plugin>
        <groupId>sample.plugin</groupId>
        <artifactId>hello-maven-plugin</artifactId>
        <version>1.0-SNAPSHOT</version>
        <executions>
          <execution>
            <phase>compile</phase>
            <goals>
              <goal>sayhi</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>

这会导致在编译 Java 代码时执行简单的 mojo。有关将 mojo 绑定到生命周期中的各个阶段的更多信息,请参阅构建生命周期文档。

Mojo 原型

要创建一个新的插件项目,您可以使用 Mojo原型和以下命令行:

mvn archetype:generate \
  -DgroupId=sample.plugin \
  -DartifactId=hello-maven-plugin \
  -DarchetypeGroupId=org.apache.maven.archetypes \
  -DarchetypeArtifactId=maven-archetype-plugin

参数

如果没有参数,mojo 不太可能非常有用。参数提供了一些非常重要的功能:

  • 它提供了钩子,允许用户调整插件的操作以满足他们的需要。
  • 它提供了一种从 POM 中轻松提取元素值的方法,而无需导航对象。

在 Mojo 中定义参数

定义参数就像在 mojo 中创建实例变量并添加适当的注释一样简单。下面列出的是简单 mojo 的参数示例:

    /**
     * The greeting to display.
     */
    @Parameter( property = "sayhi.greeting", defaultValue = "Hello World!" )
    private String greeting;

注释之前的部分是参数的描述。注释将parameter变量标识为 mojo 参数。注释的defaultValue参数定义了变量的默认值。该值可以包括引用项目的表达式,例如“ ${project.version}”(更多可以在“参数表达式”文档中找到)。通过引用用户通过选项设置的系统属性,该property参数可用于允许从命令行配置 mojo 参数。-D

在项目中配置参数

为插件配置参数值是在pom.xml文件中的 Maven 项目中完成的,作为在项目中定义插件的一部分。配置插件的示例:

<plugin>
  <groupId>sample.plugin</groupId>
  <artifactId>hello-maven-plugin</artifactId>
  <version>1.0-SNAPSHOT</version>
  <configuration>
    <greeting>Welcome</greeting>
  </configuration>
</plugin>

在配置部分,元素名称(“ greeting”)是参数名称,元素的内容(“ Welcome”)是要分配给参数的值。

注意:更多细节可以在配置插件指南中找到。

具有一个值的参数类型

下面列出了各种类型的简单变量,它们可以用作你的 mojos 中的参数,以及关于如何解释 POM 中的值的任何规则。

布尔值

这包括类型化的变量booleanBoolean. 读取配置时,文本“ true”导致参数设置为 true,所有其他文本导致参数设置为 false。例子:

    /**
     * My boolean.
     */
    @Parameter
    private boolean myBoolean;
<myBoolean>true</myBoolean>
整数

byte这包括类型为 、ByteintIntegerlongLongshort和的变量Short。读取配置时,XML 文件中的文本会使用相应类Integer.parseInt()的方法或方法转换为整数值。valueOf()这意味着字符串必须是有效的十进制整数值,仅由数字 0 到 9 组成-,前面有一个可选的负值。例子:

    /**
     * My Integer.
     */
    @Parameter
    private Integer myInteger;
<myInteger>10</myInteger>
浮点数字

double这包括类型为 、Doublefloat和的变量FloatvalueOf()读取配置时,使用相应类的方法将 XML 文件中的文本转换为二进制形式。这意味着字符串可以采用 Java 语言规范第 3.10.2 节中指定的任何格式。一些有效值样本是1.06.02E+23

    /**
     * My Double.
     */
    @Parameter
    private Double myDouble;
<myDouble>1.0</myDouble>
日期

这包括类型化的变量Date。读取配置时,XML 文件中的文本使用以下日期格式之一进行转换:“ yyyy-MM-dd HH:mm:ss.S a”(示例日期为“2005-10-06 2:22:55.1 PM”)或“ yyyy-MM-dd HH:mm:ssa”(示例日期为“2005-10-06 下午 2:22:55”)。请注意,解析是使用DateFormat.parse()which 允许格式化的一些宽大处理完成的。如果该方法可以解析指定的日期和时间,即使它与上面的模式不完全匹配,它也会这样做。例子:

    /**
     * My Date.
     */
    @Parameter
    private Date myDate;
<myDate>2005-10-06 2:22:55.1 PM</myDate>
文件和目录

这包括类型化的变量File。读取配置时,XML 文件中的文本用作所需文件或目录的路径。如果路径是相对的(不以 开头/或类似的驱动器C:号),则路径相对于包含 POM 的目录。例子:

    /**
     * My File.
     */
    @Parameter
    private File myFile;
<myFile>c:\temp</myFile>
网址

这包括类型化的变量URL。读取配置时,使用 XML 文件中的文本作为 URL。格式必须遵循 RFC 2396 指南,并且看起来像任何 Web 浏览器 URL ( scheme://host:port/path/to/file)。在转换 URL 时,对 URL 的任何部分的内容都没有限制。

    /**
     * My URL.
     */
    @Parameter
    private URL myURL;
<myURL>http://maven.apache.org</myURL>
纯文本

char这包括类型为 、CharacterStringBuffer和的变量String。读取配置时,将 XML 文件中的文本用作要分配给参数的值。对于charCharacter参数,只使用文本的第一个字符。

枚举

也可以使用枚举类型参数。首先你需要定义你的枚举类型,然后你可以在参数定义中使用枚举类型:

    public enum Color {
      GREEN,
      RED,
      BLUE
    }

    /**
     * My Enum
     */
    @Parameter
    private Color myColor;

所以让我们看看你可以在你的 pom 配置中使用这样的枚举:

<myColor>GREEN</myColor>

您还可以将枚举类型中的元素用作 defaultValues,如下所示:

    public enum Color {
      GREEN,
      RED,
      BLUE
    }

    /**
     * My Enum
     */
    @Parameter(defaultValue = "GREEN")
    private Color myColor;

具有多个值的参数类型

下面列出了可以用作 mojo 中参数的各种类型的复合对象,以及有关如何解释 POM 中的值的任何规则。通常,为保存参数值而创建的对象的类(以及参数值中每个元素的类)确定如下(使用产生有效类的第一步):

  1. 如果 XML 元素包含implementation提示属性,则使用
  2. 如果 XML 标记包含 a .,请尝试将其作为完全限定的类名
  3. 尝试将 XML 标记(首字母大写)作为与正在配置的 mojo/object 相同的包中的类
  4. 对于数组,使用数组的组件类型(例如,String用于String[]参数);对于集合和地图,使用在 mojo 配置中为集合或地图指定的类;用于String集合中的条目和地图中的值

一旦定义了元素的类型,XML 文件中的文本就会被转换为适当的对象类型

数组

数组类型参数是通过多次指定参数来配置的。例子:

    /**
     * My Array.
     */
    @Parameter
    private String[] myArray;
<myArray>
  <param>value1</param>
  <param>value2</param>
</myArray>
收藏品

此类别涵盖任何实现java.util.CollectionArrayListor的类HashSet。这些参数是通过多次指定参数来配置的,就像一个数组一样。例子:

    /**
     * My List.
     */
    @Parameter
    private List myList;
<myList>
  <param>value1</param>
  <param>value2</param>
</myList>

有关各个集合元素映射的详细信息,请参阅映射列表

地图

此类别涵盖任何实现但未实现的java.util.Map类。这些参数是通过在参数配置的表单中包含 XML 标记来配置的。例子:HashMapjava.util.Properties <key>value</key>

    /**
     * My Map.
     */
    @Parameter
    private Map myMap;
<myMap>
  <key1>value1</key1>
  <key2>value2</key2>
</myMap>
特性

此类别涵盖了实现的任何地图java.util.Properties。这些参数是通过 <property><name>myName</name> <value>myValue</value> </property> 在参数配置的表单中包含 XML 标记来配置的。例子:

    /**
     * My Properties.
     */
    @Parameter
    private Properties myProperties;
<myProperties>
  <property>
    <name>propertyName1</name>
    <value>propertyValue1</value>
  <property>
  <property>
    <name>propertyName2</name>
    <value>propertyValue2</value>
  <property>
</myProperties>
其他项目类别

此类别涵盖任何不实现java.util.Mapjava.util.Collection或的类java.util.Dictionary。例子:

    /**
     * My Object.
     */
    @Parameter
    private MyObject myObject;
<myObject>
  <myField>test</myField>
</myObject>

有关用于配置此类参数的策略的详细信息,请参阅映射复杂对象。

使用二传手

您不限于使用私有字段映射,如果您试图使您的 Mojos 在 Maven 上下文之外可重复使用,这很好。使用上面的示例,我们可以使用下划线约定命名我们的私有字段,并提供配置映射机制可以使用的设置器。所以我们的 Mojo 看起来像下面这样:

public class MyQueryMojo
    extends AbstractMojo
{
    @Parameter(property="url")
    private String _url;

    @Parameter(property="timeout")
    private int _timeout;

    @Parameter(property="options")
    private String[] _options;

    public void setUrl( String url )
    {
        _url = url;
    }

    public void setTimeout( int timeout )
    {
        _timeout = timeout;
    }

    public void setOptions( String[] options )
    {
        _options = options;
    }

    public void execute()
        throws MojoExecutionException
    {
        ...
    }
}

请注意每个参数的属性名称规范,它告诉 Maven 当字段名称与插件配置中参数的预期名称不匹配时使用什么 setter 和 getter。

资源

  1. Mojo 文档:Mojo API、Mojo 注释
  2. Maven 插件测试工具:Mojos 的测试框架。
  3. Plexus:Maven 使用的 IoC 容器。
  4. Plexus Common Utilities:一组对 Mojo 开发有用的实用程序类。
  5. Commons IO:一组对文件/路径处理有用的实用程序类。
  6. 常见错误和陷阱:有问题的编码模式概述。