Maven自定义插件

2014/03/10 Maven

Maven自定义插件

maven 实质上是一个插件执行框架,所有的工作都是通过插件完成的。包括我们日常使用到的类似 install、clean、deploy、compiler…这些命令,其实底层都是一个一个的 maven 插件。Maven自定义插件帮助自己更好的理解插件的目标和生命周期阶段之间的关系。

maven 插件的命名规范

在写一个项目之前,第一件事就是确定一个名称。maven 插件也不例外。它有着自己的一套命名规范。但是规范很简单,一句话就可以概括,官方插件命名的格式为 maven-xxx-plugin,非官方的插件命名为 xxx-maven-plugin 。是不是觉得很眼熟,没错,spring boot starter 的命名也有类似的规范。

创建工程,指定打包方式

创建一个Maven工程即可,要在pom文件中指定打包方式为:maven-plugin

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

引入依赖

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

完整的XML配置


    <groupId>com.maven</groupId>
    <artifactId>hello-maven-plugin</artifactId>
    <version>1.0-SNAPSHOT</version>
    <!-- 打包方式必须指定为maven-plugin -->
    <packaging>maven-plugin</packaging>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.apache.maven</groupId>
            <artifactId>maven-plugin-api</artifactId>
            <version>3.5.2</version>
        </dependency>
        <!-- 插件注解 -->
        <dependency>
            <groupId>org.apache.maven.plugin-tools</groupId>
            <artifactId>maven-plugin-annotations</artifactId>
            <version>3.5.2</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-plugin-plugin</artifactId>
                <version>3.5.2</version>
            </plugin>
        </plugins>
    </build>

注意: build使用maven-plugin-plugin

创建 Mojo 类

Mojo 类是一个 Maven 插件的核心类。Mojo 这个单词的意思是:Maven Old Java Object。 首先生成的类继承了 AbstractMojo 这个抽象类,这里是 maven 插件的规范要求,maven 插件必须要继承 AbstractMojo 并实现他的 execute 方法。 抽象类AbstractMojo包含了实现mojo所需的基础代码,我们仅仅需要实现execute方法就可以了。execute方法可以抛出两种异常:

  • MojoExecutionException:表示意外错误发生,控制台会显示BUILD ERROR消息
  • MojoFailureException:表示非意外的错误发生,例如编译是白。控制台会显示BUILD FAILURE消息 常用注解
  • @goal 这个 tag 的作用是指定插件的命名,比如我们常用的 mvn clean,这个 clean 就是他的 @goal 。
  • @phase 是绑定插件执行的生成周期,比如你绑定在 clean 这个周期,那你在执行 clean 的时候会自动触发你的插件。
  • @parameter 用来指定插件的参数。 ```java @Mojo(name = “hello”) public class HelloMojo extends AbstractMojo {

    @Parameter(name = “name”, defaultValue = “word”) private String name;

    @Override public void execute() throws MojoExecutionException, MojoFailureException { getLog().info(“This is hello maven plugin “+name); }

}

首先,我们的类必须继承 AbstractMojo 并实现他的 execute 方法,而 execute 方法其实就是这个插件的入口类。
示例代码中有两个很重要的注解,一个是 @Mojo ,它主要用来定义插件相关的信息相当于上面说的 @goal ,其中 name 属性用来指定这个插件名称,同 clean 类似。
另外一个重要注解 @Parameter ,则是用来指定插件运行时使用的参数,其中 name 是参数名,defaultValue 顾名思义是默认值,也就是在用户没有设置的时候使用的值。

## 使用插件
首先上面我们的代码写完了,必须要 Install 一下,否则别的项目无法直接依赖,如果你还想给其它人使用,那还需上传到 maven 仓库。
依赖插件
```xml
    <build>
        <plugins>
            <plugin>
                <groupId>com.maven</groupId>
                <artifactId>hello-maven-plugin</artifactId>
                <version>1.0-SNAPSHOT</version>
            </plugin>
        </plugins>
    </build>

在我们想使用插件的项目中,添加如上配置,其中 plugin 中使用我们插件。

启动插件

如果上面配置的都正确,那么在 idea 右侧的 Maven 中,你配置的项目的 Plugins 下会多了一个 hello(具体根据你插件项目的名称),而 hello 里面会有一个 hello:hello,其中这个 demo 对应你插件项目的名称,而 hello 对应你插件的名称也就是 @Mojo 中的 name 。 或者使用命令调用

mvn com.maven:hello-maven-plugin:1.0-SNAPSHOT:hello

上面调用插件的mvn命令参数非常冗长,缩短的方式有几种:

  • 如果希望执行本地仓库中,插件的最新版本,则version可以省略
  • 可以为插件分配快捷前缀,如果插件命名遵循了 ${prefix}-maven-plugin格式则prefix自动为前缀。调用命令可以简化为 mvn com.maven:hello:hello
  • 可以将组标识添加到Maven的settings.xml的pluginGroups,这样就可以进一步省略组标识: ```xml
com.maven
调用命令简化为 mvn hello:hello
好的,我们双击一下,hello:hello ,会输出如下日志:
```text
[INFO] Scanning for projects...
[INFO] 
[INFO] ---------------------------< com.demo:demo >----------------------------
[INFO] Building demo 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- hello-maven-plugin:1.0-SNAPSHOT:hello (default-cli) @ demo ---
[INFO] This is hello maven plugin word
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  1.534 s
[INFO] Finished at: 2023-02-02T15:56:18+08:00
[INFO] ------------------------------------------------------------------------

配置参数

可能你还记得,我们在写 helloMojo 的时候还指定了一个 name 属性,并且为它指定了一个 Parameter,这个如何使用。只要在依赖的插件下面添加 configuration 标签就可以了。

    <build>
        <plugins>
            <plugin>
                <groupId>com.maven</groupId>
                <artifactId>hello-maven-plugin</artifactId>
                <version>1.0-SNAPSHOT</version>
                <configuration>
                    <name>yangjingjing</name>
                </configuration>
            </plugin>
        </plugins>
    </build>

其中 configuration 标签内的标签,对应你定义的参数名称。

附到生命周期

在目标项目中增加如下配置:

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

则在构建生命周期的compile阶段,hello目标自动执行。

Maven插件工具注解

import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.plugin.descriptor.PluginDescriptor;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Execute;
import org.apache.maven.plugins.annotations.InstantiationStrategy;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import org.apache.maven.settings.Settings;
 
       // 此Mojo对应的目标的名称
@Mojo( name = "<goal-name>",
       aggregator = <false|true>, 
       configurator = "<role hint>",
       // 执行策略
       executionStrategy = "<once-per-session|always>",
       inheritByDefault = <true|false>,
       // 实例化策略
       instantiationStrategy = InstantiationStrategy.<strategy>,
       // 如果用户没有在POM中明确设置此Mojo绑定到的phase,那么绑定一个MojoExecution到那个phase
       defaultPhase = LifecyclePhase.<phase>,
       requiresDependencyResolution = ResolutionScope.<scope>,
       requiresDependencyCollection = ResolutionScope.<scope>,
       // 提示此Mojo需要被直接调用(而非绑定到生命周期阶段)
       requiresDirectInvocation = <false|true>,
       // 提示此Mojo不能在离线模式下运行
       requiresOnline = <false|true>,
       // 提示此Mojo必须在一个Maven项目内运行
       requiresProject = <true|false>,
       // 提示此Mojo是否线程安全,线程安全的Mojo支持在并行构建中被并发的调用
       threadSafe = <false|true> ) // (since Maven 3.0)
 
// 何时执行此Mojo
@Execute( goal = "<goal-name>",           // 如果提供goal,则隔离执行此Mojo
          phase = LifecyclePhase.<phase>, // 在此生命周期阶段自动执行此Mojo
          lifecycle = "<lifecycle-id>" )  // 在此生命周期中执行此Mojo
public class MyMojo
    extends AbstractMojo
{
    
    @Parameter( name = "parameter",
                // 在POM中可使用别名来配置参数
                alias = "myAlias",
                property = "a.property",
                defaultValue = "an expression, possibly with ${variables}",
                readonly = <false|true>,
                required = <false|true> )
    private String parameter;
 
    @Component( role = MyComponentExtension.class,
                hint = "..." )
    private MyComponent component;
 
 
    @Parameter( defaultValue = "${session}", readonly = true )
    private MavenSession session;
 
    @Parameter( defaultValue = "${project}", readonly = true )
    private MavenProject project;
 
    @Parameter( defaultValue = "${mojoExecution}", readonly = true )
    private MojoExecution mojo;
 
    @Parameter( defaultValue = "${plugin}", readonly = true )
    private PluginDescriptor plugin;
 
    @Parameter( defaultValue = "${settings}", readonly = true )
    private Settings settings;
 
    @Parameter( defaultValue = "${project.basedir}", readonly = true )
    private File basedir;
 
    @Parameter( defaultValue = "${project.build.directory}", readonly = true )
    private File target;
 
    public void execute()
    {
    }
}

单元测试

用于测试单个Mojo的功能,并仿冒Maven的其它部分。

使用PlexusTestCase 你可以像编写普通Junit测试用例那样,进行Mojo单元测试。但是,大部分情况下Mojo都需要被注入一个Maven项目的引用,可以 extends PlexusTestCase达成变量注入的目的。

maven-plugin-testing-harness 此工具专门用于测试 org.apache.maven.reporting.AbstractMavenReport#execute()

远程调试

要调试Maven插件,可以使用远程调试的方式。

你需要用 mvnDebug而非mvn来调用Maven,此命令默认会监听8000端口,等待Debugger的连接。

mvnDebug cc.gmem.yun.maven.plugins:maven-archetype-plugin:3.1.1:create-from-project \
    -Darchetype.properties=archetype.properties -Darchetype.postPhase=package -Darchetype.partialArchetype=true
 
# Preparing to execute Maven in debug mode
# Listening for transport dt_socket at address: 8000

本地调试

配合Intellij IDEA等开发工具,可以很容易实现本地调试。

在你的插件项目中,创建一个Maven的运行配置,设置环境变量:

MAVEN_DEBUG_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000"

然后Maven的Command line调用你的插件的Mojo,配置好了点击调试按钮即可。

Search

    微信好友

    博士的沙漏

    Table of Contents