Maven 编写插件

Maven 的任何行为都是由插件完成的,包括项目的清理、绵编译、测试以及打包等操作都有其对应的 Maven 插件。每个插件拥有一个或者多个目标,用户可以直接从命令行运行这些插件目标,或者选择将目标绑定到 Maven 的生命周期。

大量的 Maven 插件可以从 Aapche 获得,当你发现自己有特殊需要的时候,首先应该搜索一下看是否已经有现成的插件可供使用。在一些非常情况下(几率低于1%),你有非常特殊的需求,并且无法找到现成的插件可供使用,那么就只能自己编写 Maven 插件了。编写 Maven 插件并不是特别复杂,本章将详细介绍如何一步步编写能够满足自己需要的 Maven 插件。

1. 编写 Maven 插件的一般步骤

为了能让读者对编写 Maven 插件的方法和过程有一个总体的认识,下面先简要介绍下编写 Maven 插件的主要步骤。

  1. 创建一个 maven-plugin 项目:插件本身也是 Maven 项目,特殊的地方在于它的 packagng 必须是 maven-plugin。

  2. 为插件编写目标:每个插件都必须包含一个或者多个目标, Maven 称之为 Mojo(与 POJO 对应,后者指 Plain Old Java Object,这里指 Maven Old Java Object)。编写插件的时候必须提供一个或者多个继承自 AbstractMojo的类。

  3. 为目标提供配置点:大部分 Maven 插件及其日标都是可配置的,因此在编写 Mojo 的时候需要注意提供可配置的参数

  4. 编写代码实现目标行为:根据实际的需要实现 Mojo

  5. 错误处理及日志:当 Mojo 发生异常时,根据情况控制 Maven 的运行状态。在代码中编写必要的日志以便为用户提供足够的信息。

  6. 测试插件:编写自动化的测试代码测试行为,然后再实际运行插件以验证其行为。

2. 案例:编写一个用于代码行统计的 Maven 插件

为了便于大家实践,下面将详细演示如何实际编写一个简单的用于代码行统计的 Maven 插件。使用该插件,用户可以了解到到 Maven 项目中各个源代码目录下文件的数量,以及它们加起来共有多少代码行。

要创建一个 Maven 插件项目,首先使用 maven-archetype-plugin 命令:

mvn archetype:generate

然后选择:

maven-archetype-plugin (An archetype which contains a sample Maven plugin.)

输入 Maven 坐标等信息之后,一个 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>com.github.binarylei.maven</groupId>
<artifactId>maven-count-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>maven-plugin</packaging> <properties>
<maven.version>3.0</maven.version>
</properties> <dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>${maven.version}</version>
</dependency>
</dependencies>
</project>

Maven 插件项目的 POM 有两个特殊的地方:

  1. 它的 packaging 必须为 maven-plugin,这种特殊的打包类型能控制 Maven 为其在生命周期阶段绑定插件处理相关的目标,例如在 compile 阶段, Maven 需要为插件项目构建个特殊插件描述符文件。

  2. 从上述代码中可以看到一个 artifactId 为 maven-plugin-api 的依赖,该依赖中包含了插件开发所必需的类,例如稍后会看到的 AbstractMojo。

插件项目创建好之后,下一步是为插件编写目标。创建一个 CountMojo 类:

package com.github.binarylei.maven;

import org.apache.maven.model.Resource;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException; import java.io.File;
import java.io.IOException;
import java.util.List; /**
* 统计代码的 Maven 插件
* @author: leigang
* @version: 2018-03-25
* @goal count
*/
public class CountMojo extends AbstractMojo {
private static final String[] INCLUDES_DEFALUT = {"java", "xml", "properties"}; /**
* @parameter expression="${project.basedir}"
* @required
* @readonly
*/
private File basedir; /**
* @parameter expression="${project.build.sourceDirectory}"
* @required
* @readonly
*/
private File sourceDirectory; /**
* @parameter expression="${project.build.testSourceDirectory}"
* @required
* @readonly
*/
private File testSourceDirectory; /**
* @parameter expression="${project.build.resources}"
* @required
* @readonly
*/
private List<Resource> resources; /**
* @parameter expression="${project.build.testResources}"
* @required
* @readonly
*/
private List<Resource> testResources; /**
* The file types which will be included for counting
* @parameter
*/
private String[] includes; public void execute() throws MojoExecutionException, MojoFailureException {
System.out.println("my maven plugin"); if (includes == null || includes.length == 0) {
includes = INCLUDES_DEFALUT;
} try {
countDir(sourceDirectory);
countDir(testSourceDirectory);
for (Resource resource : resources) {
countDir(new File(resource.getDirectory()));
}
for (Resource resource : testResources) {
countDir(new File(resource.getDirectory()));
}
} catch (IOException e){
throw new MojoExecutionException("Unable to count lines of code.", e);
}
}
}
  1. 每个插件目标类,或者说 Mojo,都必须继承 AbstractMojo 并实现 execute() 方法,只有这样 Maven 才能识别该插件目标,并执行 execute() 方法中的行为。

  2. 由于历史原因,上述 CountMojo 类使用了 Java1.4 风格的标注(将标注写在注释中),这里要关注的是 @goal,任何一个 Mojo 都必须使用该标注写明自己的目标名称,有了目标定义之后,我们才能在项目中配置该插件目标,或者在命令行调用之。例如

    mvn com.github.binarylei.maven:maven-plugin:1.0.0-SNAPSHOT:count

创建一个 Mojo 所必要的工作就是这三项:继承 AbstractMojo、实现 execute() 方法、提供 @goal 标注。

下一步是为插件提供配置点。我们希望该插件默认统计所有 Java、XML,以及 properties 文件,但是允许用户配置包含哪些类型的文件。includes 字段就是用来为用户提供该配置点的,它的类型为 String 数组,并且使用了 @parameter 参数表示用户可以在使用该插件的时候在 POM 中配置该字段。

<plugin>
<groupId>org.github.binarylei.maven</groupId>
<artifactId>maven-count-plugin</artifactId>
<version>1.0.0-SNAPSHOT</version>
<configuration>
<includes>
<include>java</include>
<include>sql</include>
</includes>
</configuration>
</plugin>

上述配置了 CountMojo 统计 Java 和 SQL 文件,而不是默认的 Java、XML 和 Properties

CountMojo 类中还包含了 basedir、 sourcedirectory、 testsource Directory 等字段,它们都使用了 @parameter 标注,但同时关键字 expression 表示从系统属性读取这几个字段的值。${project.basedir}, ${project.build.sourceDirectory} , ${project.build.testSourceDirectory} 等表达式读者应该已经熟悉,它们分别表示了项目的基础目录、主代码目录和测试代码目录。@readonly 标注表示不允许用户对其进行配置,因为对于一个项目来说,这几个目录位置都是固定的。

了解这些简单的配置点之后,下一步就该实现插件的具体行为了。CountMojo 类的 execute() 方法中大家能看到这样一些信息:如果用户没有配置 includes 则就是用默认的统计包含配置,然后再分别统计项目主代码目录、测试代码目录、主资源目录,以及测试资源目录。这里涉及一个 countDir() 方法,代码如下:

private void countDir(File dir) throws IOException {
if (! dir.exists()) {
return;
} List<File> collected = new ArrayList<File>();
collectFiles(collected, dir); int lines = 0;
for (File souceFile : collected) {
lines += countLine(souceFile);
} String path = dir.getAbsolutePath().substring(basedir.getAbsolutePath().length());
getLog().info(path + ":" + lines + " lines of code in " + collected.size() + " files");
} private void collectFiles(List<File> collected, File file) {
if (file.isFile()) {
for (String include : includes) {
if (file.getName().endsWith("." + include)) {
collected.add(file);
break;
}
}
} else {
for (File sub : file.listFiles()) {
collectFiles(collected, sub);
}
}
} private int countLine(File file) throws IOException {
BufferedReader reader = new BufferedReader(new FileReader(file));
int line = 0; try {
while (reader.ready()) {
reader.readLine();
line++;
}
} finally {
reader.close();
}
return line;
}

使用 mvn clean install 命令将该插件项目构建并安装到本地仓库后,就能使用它统计 Maven 项目的代码了。如下所示:

mvn com.github.binarylei.maven:maven-plugin:1.0.0-SNAPSHOT:count

输出结果如下:

[INFO] \src\main\java:132 lines of code in 1 files
[INFO] \src\test\java:0 lines of code in 0 files
[INFO] \src\main\resources:0 lines of code in 0 files

如果嫌命令行太长太复杂,可以将该插侏的 groupId 添加到 settings.xml 中。如下所示:

<settings>
<pluginGroups>
<pluginGroup>com.github.binarylei.maven</pluginGroup>
</pluginGroups>
</settings>

现在 Maven 命令行就可以简化成

mvn loc: count

3. Mojo 标注

每个 Mojo 都必须使用 @Goal 标注来注明其目标名称,否则 Maven 将无法识别该目标。Mojo 的标注不仅限于 @Coal,以下是一些可以用来控制 Mjo 行为的标注。

  1. @goal

    这是唯一必须声明的标注,当用户使用命令行调用插件,或者在 POM 中配置插件的时候,都需要使用该日标名称。

  2. @phase

    默认将该目标绑定至 Default 生命周期的某个阶段,这样在配置使用该插件目标的时候就不需要声明 phase。例如, maven-surefire-plugin的 test 目标就带有 @phase test标注。

  3. @requiresDependencyResolution scope

    表示在运行该 Mojo 之前必须解析所有指定范围的依赖。例如, maven-surefire-plugin 的 test 目标带有 @requiresDependencyResolution test 标注,表示在执行测试之前,所有测试范围的依赖必须得到解析。这里可用的依赖范围有 compile、test和 runtime,默认值为 runtime

  4. @requiresProject true/false

    表示该目标是否必须在一个 Maven 项日中运行,默认为 true。大部分插件目标都需要依赖一个项目才能执行,但有一些例外。例如 maven-help-plugin 的 system 目标,它用来显示系统属性和环境变量信息,不需要实际项目,因此使用了 @requiresProject false 标注。另外,maven-archetype-plugin 的 generate 目标也是一个很好的例子。

  5. @requiresDirectInvocation true/false

    当值为 true 的时候,该目标就只能通过命令行直接调用,如果试图在 POM 中将其绑定到生命周期阶段, Maven 就会报错,默认值为 false。如果你希望编写的插件只能在命令行独立运行,就应当使用该标注。

  6. @requiresonline true/false

    表示是否要求 Maven 必须是在线状态,默认值是 false

  7. @requiresReport true/false

    表示是否要求项目报告已经生成,默认值是 false

  8. @aggregator

    当 Mojo 在多模块项目上运行时,使用该标注表示该目标只会在顶层模块运行。例如 maven-javadoc-plugin 的使用了 @aggregator 标注,它不会为多模块项目的每个模块生成 Javadoc,而是在顶层项目生成一个已经聚合的 Javadoc 文档。

  9. @execute goal=""

    在运行该目标之前先让 Maven 运行另外一个目标,如果是本插件的目标,则直接使用目标名称,否则使用 "prefix:goal" 的形式,即注明目标前缀。例如, maven-pmd-plugin 是一个使用 PMD 来分析项目源码的工具,它包含 pmd 和 check 等目标,其中 pmd 用来生成报告,而 check 用来验证报告。由于 check 是依赖于 pmd 生成的内容的,因此可以看到它使用了标注 @execute goal="pmd"。

  10. @execute phase=""

    在运行该目标之前让 Maven 先运行一个并行的生命周期,到指定的阶段为止。例如 maven-dependency-plugin 的 analyze 使用了标注 @execute phase="test-compile",因此当用户在命令行执行 dependency:analyze 的时候, Maven 会首先执行 default 生命周期所有至 test-compile 的阶段。

  11. @execute lifecycle="" phase=""

    在运行该日标之前让 Maven 先运行一个自定义的生命周期,到指定的阶段为止。例如 maven-surefire-report-plugin 这个用来生成测试报告的插件,它有一个 report 目标,标注了 @execute phase="test" lifecycle=" surefire",表示运行这个自定义的 surefire 声明周期至 test 阶段。自定义生命周期的配置文件位于 sre/main/resources/META-INF/maven/lifecycle.xml。代码如下:

<lifecyles>
<lifecyle>
<id>surefire</id>
<phases>
<phase>
<id>test</id>
<configuration>
<testFailureIgnore>true</testFailureIgnore>
</configuration>
</phase>
</phases>
</lifecyle>
</lifecyles>

4. Mojo 参数

我们可以使用 @parameter 将 Mojo 的某个字段标注为可配置的参数,即 Mojo 参数。事实上几乎每个 Mojo 都有一个或者多个 Mojo 参数,通过配置这些参数, Maven 用户可以自定义插件的行为。

Maven 支持种类多样的 Mojo 参数,包括单值的 boolean、int、 float、 String、Date、File 和 URL,多值的数组、 Collection、Map、 Properties 等。

  1. boolean(包括 boolean 和 Boolean)

    /**
    * @parameter
    */
    private boolean sampleBoolean;

    对应的配置如下:

    <sampleboolean >true</sampleboolean>
  2. int(包括 Integer、long、Iong、 short、 Short、byte、Byte)

    /**
    * @parameter
    */
    private int sampleint;

    对应的配置如下:

    <sampleint >8</sampleint>
  3. float(包括 Float、 double、 Double)

    /**
    * @parameter
    */
    private float sampleFloat;

    对应的配置如下:

    <sampleFloat >8.0</sampleFloat>
  4. String(包括 StringBuffer、 char、 Character)

    /**
    * @parameter
    */
    private String sampleString;

    对应的配置如下:

    <sampleString >Hello World</sampleString>
  5. Date(格式为 yyyy-MM-dd HH:mm:ss.S a 或 yyyy-MM-dd HH:mm:ssa)

    /**
    * @parameter
    */
    private Date sampleDate;

    对应的配置如下:

    <sampleDate >2018-03-25 13:10:00</sampleDate>
  6. File

    /**
    * @parameter
    */
    private File sampleFile;

    对应的配置如下:

    <sampleFile >c:/tmp</sampleFile>
  7. URL

    /**
    * @parameter
    */
    private URL sampleURL;

    对应的配置如下:

    <sampleURL >http://www.baidu.com</sampleURL>
  8. 数组

    /**
    * @parameter
    */
    private String[] includes;

    对应的配置如下:

    <includes>
    <include>java</include>
    <include>sql</include>
    </includes>
  9. Collection(任何实现 Collection 接口的类,如 ArrayList 和 HashSet)

    /**
    * @parameter
    */
    private List includes;

    对应的配置如下:

    <includes>
    <include>java</include>
    </includes>
  10. Map

    /**
    * @parameter
    */
    private Map sampleMap;

    对应的配置如下:

    <sampleMap>
    <key1>java</key1>
    </sampleMap>
  11. Properties

    /**
    * @parameter
    */
    private Properties sampleProperties;

    对应的配置如下:

    <sampleProperties>
    <property>
    <name>key_1</name>
    <value>value_1</value>
    </property>
    <property>
    <name>key_2</name>
    <value>value_2</value>
    </property>
    </sampleProperties>

一个简单的 @parameter 标注就能让用户配置各种类型的 Mojo 字段,不过在此基础上用户还能为 @parameter 标注提供一些额外的属性,进一步自定义 Moio 参数。

  1. @parameter alias=""

    使用 alias,用户就可以为 Mojo 参数使用别名,当 Mojo 字段名称太长或者可读性不强时,这个别名就非常有用。例如

    /**
    * @parameter alias="uid"
    */
    private String uniqueIdentity;

    对应的配置如下:

    <uid>maven</uid>
  2. @parameter expression="${aSystemProperty}"

    使用系统属性表达式对Mj参数进行赋值,这是非常有用的特性。配置了@parameter 的 expression 之后,用户可以在命令行配置该 Mojo 参数。例如, maven-surefire-plugin 的 test 目标有如下源码:

    /**
    * @parameter expression="${maven.test.skip}"
    */
    private boolean skip;

    用户可以在 POM 中配置 skip 参数,同时也可以直接在命令行使用 -Dmaven.test.skip=true 来跳过测试。如果 Mojo 参数没有提供 expression,那就意味着该参数无法在命令行直接配置。还需要注意的是,Mojo 参数的名称和 expression 名称不一定相同。

  3. @parameter default-value="aValue/${anExpression}"

    如果用户没有配置该 Mojo 参数,就为其提供一个默认值。该值可以是一个简单字面量如 "true"、"hello" 或者 "1.5",也可以是一个表达式,以方便使用 POM 的某个元素。

    /**
    * @parameter defaultValue="true"
    */
    private boolean sampleBoolean;

除了 @parameter 标注外,还看到可以为 Mojo 参数使用 @readonly 和 @required 标注。

  1. @readonly

    表示该 Mojo 参数是只读的,如果使用了该标注,用户就无法对其进行配置。通常在应用 POM 元素内容的时候,我们不希望用户干涉。

  2. @required

    表示该 Mojo 参数是必须的,如果使用了该标注,但是用户没有配置该 Mojo 参数且其没有默认值, Maven 就会报错。

5. 错误处理和日志

如果大家看一下 Maven 的源码,会发现 AbstractMojo 实现了 Mojo 接口, execute() 方法正是在这个接口中定义的。具体代码如下:

public interface Mojo {
String ROLE = Mojo.class.getName(); void execute() throws MojoExecutionException, MojoFailureException; void setLog(Log var1); Log getLog();
}

这个方法可以抛出两种异常,分别是 MojoExecutionException 和 MojoFailureException。如果 Maven 执行插件目标的时候遇到 MojoFailureException,就会显示 “BUILD FAIL,URF” 的错误信息,这种异常表示 Mojo 在运行时发现了预期的错误。例如 maven-surefire-plugin 运行后若发现有失败的测试就会抛出该异常。

如果 Maven 执行插件目标的时候遇到 MojoExecutionException,就会显示 “BUILD ERROR” 的错误信息。这种异常表示 Maven 在运行时发现了未预期的错误。

上述两种异常能够在 Mojo 执行出错的时候提供一定的信息,但这往往是不够的,用户在编写插件的时候还应该提供足够的日志信息, AbstractMojo 提供了一个 getlog() 方法,用户可以使用该方法获得一个 Log 对象。该对象支持四种级别的日志方法,它们从低到高分别为:

  1. debug 调试级别的日志。 Maven 默认不会输出该级别的日志,不过用户可以在执行 mvn 命令的时候使用 -X 参数开启调试日志,该级别的日志是用来帮助程序员了解插件具体运行状态的,因此应该尽量详细。需要注意的是,不要指望你的用户会主动去看该级别的日志。

  2. info 消息级别的日志。 Maven 默认会输出该级别的日志,该级别的日志应该足够简洁,帮助用户了解插件重要的运行状态。例如, maven-compiler-plugin 会使用该级别的日志告诉用户源代码编译的目标目录。

  3. warn 警告级别的日志。当插件运行的时候遇到了一些问题或错误,不过这类问题不会导致运行失败的时候,就应该使用该级别的日志警告用户尽快修复。

  4. error 错误级别的日志。当插件运行的时候遇到了一些问题或错误,并且这类问题导致 Mojo 无法继续运行,就应该使用该级别的日志提供详细的错误信息。

上述每个级别的日志都提供了三个方法。以 debug 为例,它们分别为:

void debug (Charsequence content)
void debug (Charsequence content, Throwable error)
void debug (Throwable error)

用户在编写插件的时候,应该根据实际情况选择适应的方法。基本的原则是,如果有异常出现,就应该尽量使用适宜的日志方法将异常堆栈记录下来,方便将来的问题分析。

如果使用过 Log4j 之类的日志框架,就应该不会对 Maven 日志支持感到陌生,日志不是一个 Maven 插件的核心代码,但是为了方便使用和调试,完整的插件应该具备足够丰富的日志代码。

6. 測试 Maven 插件

Maven 社区有一个用来帮助插件集成测试的插件,它就是 maven-invoker-plugin。该插件能够用来在一组项目上执行 Maven,并检查每个项目的构建是否成功,最后,它还可以执行 Beanshell 或者 Groovy 脚本来验证项目构建的输出。Beanshell 和 Groovy 都是基于 JVM 平台的脚本语言,读者可以访问 http://www.beanshel.org/http://groovy.codehaus.org/ 以了解更多的信息。本章下面的内容会用到少许的 Groovy 代码,不过这些代码十分简单,很容易理解。

为了验证这一行为,先配置 maven-count-plugin 的 POM 使用 maven-invoker-plugin,代码如下所示:

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-invoker-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<projectsDirectory>src/it</projectsDirectory>
<goals>
<goal>install</goal>
</goals>
<postBuildHookScript>validate.groovy</postBuildHookScript>
</configuration>
<executions>
<execution>
<id>integration-test</id>
<goals>
<goal>install</goal>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>

maven-invoker-plugin 有三项配置:

  1. 首先 projectsDirectory 用来配置测试项目的目录,也就是说在 src/it 目录下存放要测试的 Maven项目源码;
  2. 其次 goals 表示在测试项目上要运行的 Maven 目标,这里的配置表示 maven-invoker-plugin 会在 src/it 目录下的各个 Maven 项目中运行 mvn install 命令;
  3. 最后 postBuildHookScript 表示在测试完成后要运行的验证脚本,这里是一个 groovy 文件。

maven-invoker-plugin 的两个目标 install 和 run 被绑定到了 integration-test 生命周期阶段。这里的 install 目标用来将当前的插件构建并安装到仓库中供测试项目使用,run 目标则会执行定义好的 mvn 命令并运行验证脚本。当然仅仅该配置还不够,src/it 目录下必须有一个或者多个供测试的 Maven 项目。

<?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>com.github.binarylei.maven</groupId>
<artifactId>app</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging> <dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies> <build>
<plugins>
<plugin>
<groupId>com.github.binarylei.maven</groupId>
<artifactId>maven-count-plugin</artifactId>
<version>1.0.0-SNAPSHOT</version>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>count</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

测试项目准备好了,现在要准备的是与该项目对应的验证脚本文件,即 validate.groovy ,它位于 src/it/app 目录下(即上述测试项目的根目录)。

def file = new  File(basedir, 'build.log')

def countMain = false
def countTest = false file.eachLine {
if (it =~ /java:11 lines of code in 1 files/)
countMain = true
if (it =~ /java:0 lines of code in 0 files/)
countTest = true
} if (!countMain)
throw new RuntimeException("incorrect src/main/java count info"); if (!countTest)
throw new RuntimeException("incorrect src/test/java count info");

这段 Groovy 代码做的事情很简单。它首先读取 app 项目目录下的 build.log 文件,当 mavel-invoker-plugin 构建測试项目的时候,会把 mvn 输出保存到项目下的 build.log 文件中。因此,可以解析该日志文件来验证 maven-count-plugin 是否输出了正确的代码行信息。

上述 Groovy代码首先假设没有找到正确的主代码统计信息和测试代码统计信息,然后它逐行遍历日志文件,紧接着使用正则表达式检査寻找要检查的内容(两个斜杠//中间的内容是正则表达式,而 =~ 表示寻找该正则表达式匹配的内容),如果找到期望的输出,就将 countMain 和 countTest 置为 true。最后,如果这两个变量的值有 false,就就抛出对应的异常信息。

Maven 会首先在测试项目 app 上运行 mvn install 命令,如果运行成功,则再执行 validate.groovy 脚本。只有脚本运行通过且没有异常,集成测试才算成功。现在在 maven-count-plugin 下运行 mvn clean install,就能看到如下的输出:

[INFO] --- maven-invoker-plugin:3.0.0:install (integration-test) @ maven-count-plugin ---
[INFO] Installing C:\Users\len\Desktop\java\maven-plugin\pom.xml to D:\Program Files\Maven\LocalRepository\com\github\binarylei\maven\maven-count-plugin\1.0.0-SNAPSHOT\maven-count-plugin-1.0.0-SNAPSHOT.pom
[INFO] Installing C:\Users\len\Desktop\java\maven-plugin\target\maven-count-plugin-1.0.0-SNAPSHOT.jar to D:\Program Files\Maven\LocalRepository\com\github\binarylei\maven\maven-count-plugin\1.0.0-SNAPSHOT\maven-count-plugin-1.0.0-SNAPSHOT.jar
[INFO]
[INFO] --- maven-invoker-plugin:3.0.0:run (integration-test) @ maven-count-plugin ---
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[INFO] Running Setup Jobs
[INFO] Building: app\pom.xml
[INFO] run script validate.groovy.groovy
[INFO] app\pom.xml ...................................... SUCCESS (9.6 s)

从输出中可以看到 maven-invoker-plugin 的 install 目标将当前项日目 maven-count-plugin 安装至本地仓库,然后它的 run 目标构建测试项目 app,并最后报告运行结果。至此,所有 Maven 插件集成测试的步骤就都完成了。

上述样例只涉及了 maven-invoker-plugin 的很少一部分配置点,用户还可以配置:

  1. debug(boolean) 是否在构建测试项目的时候开启 debug 输出。
  2. settingsFile(File) 执行集成测试所使用的 settings.xml,默认为本机环境 settings.xml
  3. localRepositoryPath(File) 执行集成测试所使用的本地仓库,默认就是本机环境仓库。
  4. preBuildHookScript(String) 构建测试项目之前运行的 Beanshell 或 Groovy 脚本。
  5. postBuildHookScript(String) 构建测试项目之后运行的 Beanshell 或 Groovy 脚本

要了解更多的配置点,或者查看更多的样例。读者可以访问 maven-invoker-plugin 的站点: http://maven.apache.org/plugins/maven-invoker-plugin/

13 Maven 编写插件的更多相关文章

  1. 自己动手编写Maven的插件

    Maven的插件机制是完全依赖Maven的生命周期的,因此理解生命周期至关重要.本文参考官方文档后使用archetype创建,手动创建太麻烦. 创建创建项目 选择maven-archetype-moj ...

  2. 再谈:jquery编写插件的方法

    版权声明:作者原创,转载请注明出处! 编写插件的两种方式: 1.类级别开发插件(1%) 2.对象级别开发(99%) 类级别的静态开发就是给jquery添加静态方法,三种方式 1.添加新的全局函数 2. ...

  3. jquery编写插件

    jquery编写插件的方法    版权声明:作者原创,转载请注明出处! 编写插件的两种方式: 1.类级别开发插件(1%) 2.对象级别开发(99%) 类级别的静态开发就是给jquery添加静态方法,三 ...

  4. Qt中如何 编写插件 加载插件 卸载插件

    Qt中如何 编写插件 加载插件 卸载插件是本文要介绍的内容.Qt提供了一个类QPluginLoader来加载静态库和动态库,在Qt中,Qt把动态库和静态库都看成是一个插件,使用QPluginLoade ...

  5. IDEA集成有道翻译插件/maven帮助插件/mybatis插件

    (一)IDEA集成有道翻译插件:https://www.cnblogs.com/a8457013/p/7814335.html 插件下载地址:http://plugins.jetbrains.com/ ...

  6. 两个jquery编写插件实例

    (1) 封装基于jq弹窗插件   相信码友们对于$.fn.extexd();$.extend()以及$.fn.custom和$.custom都有一定的了解:我阐述一下我自己对于$.fn.custom和 ...

  7. 【01】Maven依赖插件之maven-dependency-plugin

    一.插件目标(goal) 1.analyze:分析项目依赖,确定哪些是已使用已声明的,哪些是已使用未声明的,哪些是未使用已声明的 2.analyze-dep-mgt:分析项目依赖,列出已解析的依赖项与 ...

  8. jquery编写插件的方法

     版权声明:作者原创,转载请注明出处! 编写插件的两种方式: 1.类级别开发插件(1%) 2.对象级别开发(99%) 类级别的静态开发就是给jquery添加静态方法,三种方式 1.添加新的全局函数 2 ...

  9. Maven 的插件和生命周期的绑定

    一.Maven 的生命周期 Maven 的生命周期是对所有的构建过程进行抽象和统一.Maven 的生命周期是抽象的,这意味着生命周期本身不做任何实际的工作,生命周期只是定义了一系列的阶段,并确定这些阶 ...

随机推荐

  1. linux 之 压缩 / 解压

    压缩解压 tar 即可压缩也可以解压 c 压缩 如果没有z.j参数,则表示,只打包,不压缩. 就说, t 查看 z 以gzip方式压缩 相当于 gzip ?.. j 以bzip方式压缩 bzip2 ? ...

  2. 你该知道的 TValue

    你该知道的 TValue Represents a lightweight version of the Variant type. TValue is a data structure that c ...

  3. cnapckSurround c++builder Region 代码折叠快捷键

    C++Builder代码折叠 cnapckSurround c++builder Region 代码折叠快捷键,可以导入导出,IDE code edit,cnpack menu surround wi ...

  4. cv::circle《转》

    void circle(CV_IN_OUT Mat& img, Point center, int radius, const Scalar& color, int thickness ...

  5. WDA-参考路径

    1. SAP Netweave安装 https://wenku.baidu.com/view/b3ac371a227916888486d77c.html?sxts=1545717961793   2. ...

  6. mysql的collation-字符集

    utf8_general_ci               :排序规则 utf8 -- UTF-8 Unicode     :字符集 一.通过my.cnf文件增加(一劳永逸)两个参数:1.在[mysq ...

  7. scala private 和 private[this] 的区别

    class PackageStudy { private var a = 0 private[this] var b = 1 // 只能在类内部使用,对象都不能直接使用 def getb(): Int ...

  8. Linux就业技术指导(六):天津IDC机房项目实践

    一,天津IDC机房项目图片介绍 服务器DELL R720 二,远程控制卡配置方法 远程控制卡,在服务器没有装操作系统或者操作系统出问题了.用户可以通过连接到远程控制卡来连接服务器,就如同切换到我们的虚 ...

  9. ssh-keygen生成私钥和公钥

    ssh-keygen生成私钥和公钥 例: 用户名:root 服务器地址:192.168.1.10 生成:ssh-keygen -t rsa -b 4096 -C“root@192.168.1.10” ...

  10. Windows XP with SP3大客户免激活日文版

    原贴地址:http://www.humin.com.cn/ja_windows_xp_professional_with_service_pack_3_x86_dvd_vl_x14-74058-iso ...