当前位置: 首页 > news >正文

Maven插件开发

简介

Maven本身只是一套框架,它的功能基于全部依赖于插件来实现。因此,掌握插件开发深度定制Maven的必修课。

插件本身也是Maven构件,构件标识的命名约定是 <yourplugin>-maven-plugin。不要命名为 maven-<yourplugin>-plugin,这种命名模式由官方插件(组标识 org.apache.maven.plugins)保留。

每个Maven插件包含一或多个Mojo,每个Mojo实现了一个插件目标(goal),Mojo通常编写为Java类。插件就是一系列Mojo的集合。

插件必须在自己的JAR包的 META-INF/maven/plugin.xml中提供描述符信息,你不需要手工编写描述符,使用Maven插件工具注解即可自动生成。

入门

本章我们编写一个最简单的插件,它不需要参数,仅仅简单的打印一段消息。

第一个Mojo

可以将mojo理解为插件的一个目标(goal)的实现类。你需要使用@Mojo注解来声明一个类是mojo:

Java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

package sample.plugin;

import org.apache.maven.plugin.AbstractMojo;

import org.apache.maven.plugin.MojoExecutionException;

import org.apache.maven.plugins.annotations.Mojo;

@Mojo( name = "sayhi")

public class GreetingMojo extends AbstractMojo

{

    public void execute() throws MojoExecutionException

    {

        getLog().info( "Hello, world." );

    }

}

抽象类AbstractMojo包含了实现mojo所需的基础代码,我们仅仅需要实现execute方法就可以了。execute方法可以抛出两种异常:

  1. MojoExecutionException:表示意外错误发生,控制台会显示BUILD ERROR消息
  2. MojoFailureException:表示非意外的错误发生,例如编译是白。控制台会显示BUILD FAILURE消息

构建插件

插件本身也是Maven项目,因此你需要编写POM,示例:

XML

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

<project>

  <modelVersion>4.0.0</modelVersion>

  <groupId>sample.plugin</groupId>

  <artifactId>hello-maven-plugin</artifactId>

  <version>1.0-SNAPSHOT</version>

  <!-- 打包方式必须指定为maven-plugin -->

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

    </dependency>

    <!-- 插件注解 -->

    <dependency>

      <groupId>org.apache.maven.plugin-tools</groupId>

      <artifactId>maven-plugin-annotations</artifactId>

      <version>3.4</version>

      <scope>provided</scope>

    </dependency>

  </dependencies>

</project>

使用插件

另创建一个Maven项目,添加以下配置: 

XML

1

2

3

4

5

6

7

8

9

<build>

  <plugins>

    <plugin>

      <groupId>sample.plugin</groupId>

      <artifactId>hello-maven-plugin</artifactId>

      <version>1.0-SNAPSHOT</version>

    </plugin>

  </plugins>

</build>

然后调用我们的插件:

Shell

1

2

# mvn groupId:artifactId:version:goal

mvn sample.plugin:hello-maven-plugin:1.0-SNAPSHOT:sayhi

简化命令行

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

  1. 如果希望执行本地仓库中,插件的最新版本,则version可以省略
  2. 可以为插件分配快捷前缀,如果插件命名遵循了 ${prefix}-maven-plugin格式则prefix自动为前缀。调用命令可以简化为 mvn sample.plugin:hello:sayhi
  3. 可以将组标识添加到Maven的settings.xml的pluginGroups,这样就可以进一步省略组标识:
    ~/.m2/settings.xml

    XML

    1

    2

    3

    <pluginGroups>

        <pluginGroup>sample.plugin</pluginGroup>

    </pluginGroups>

    调用命令简化为 mvn hello:sayhi

附到生命周期

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

XML

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

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

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

Mojo原型

你可以从原型创建插件项目,避免手写样板代码: 

Shell

1

2

3

4

5

mvn archetype:generate \

    -DgroupId=sample.plugin \

    -DartifactId=hello-maven-plugin \

    -DarchetypeGroupId=org.apache.maven.archetypes \

    -DarchetypeArtifactId=maven-archetype-plugin

参数化Mojo

Mojo可以支持参数,目标项目的POM中可以声明传入的参数值,你也可以在命令行中以-D系统属性的风格传入参数值。

要定义Mojo参数,只需要为Mojo类的字段添加注解:

Java

1

2

@Parameter( property = "sayhi.greeting", defaultValue = "Hello World!" )

private String greeting;

上面的代码定义了一个参数greeting,并给定了默认值。此参数对应的系统属性为sayhi.greeting。

参数值可以使用 ${project.version}这样的表达式,来引用目标项目的属性。

POM配置参数值

XML

1

2

3

4

5

6

7

8

9

<plugin>

  <groupId>sample.plugin</groupId>

  <artifactId>hello-maven-plugin</artifactId>

  <version>1.0-SNAPSHOT</version>

  <configuration>

    <!-- 这里使用参数的字段名而非系统属性 -->

    <greeting>Welcome</greeting>

  </configuration>

</plugin>

插件参数表达式

在插件参数表达式中,你可以使用以下对象:

对象说明
session当前 MavenSession对象 
session.*
localRepositoryMavenSession.getLocalRepository()
reactorProjectsMavenSession.getProjects()
repositorySystemSessionMavenSession.getRepositorySession()
projectMavenSession.getCurrentProject()
project.*
pom.*
executedProjectMavenProject.getExecutionProject()
settingsMavenSession.getSettings()
settings.*
basedirMavenSession.getExecutionRootDirectory(),为空则取
System.getProperty( "user.dir" )
mojoExecution当前MojoExecution对象
mojo
mojo.*
pluginMojoExecution.getMojoDescriptor().getPluginDescriptor() 
plugin.*
*还可以直接使用系统属性、项目属性

类型转换规则

配置中的文本转换为Mojo参数字段的规则如下:

参数字段类型文本表示
布尔型true或者false
整数包括byte, Byte, int, Integer, long, Long, short, Short,支持负号
浮点数包括double, Double, float,  Float,支持科学计数法
java.util.Date

支持格式:

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:55PM

java.io.File文件系统路径,例如c:\temp
java.net.URLURL,例如https://gmem.cc

复杂参数

多值参数

Mojo字段可以使用数组、集合类:

Java

1

2

@Parameter

private String[] myArray;

对应POM配置示例: 

XML

1

2

3

4

<myArray>

    <param>value1</param>

    <param>value2</param>

</myArray>

映射类型的字段POM配置示例,参数名为myMap: 

XML

1

2

3

4

<myMap>

    <key1>value1</key1>

    <key2>value2</key2>

</myMap>

Properties类型的POM配置示例,参数名为myProperties:

XML

1

2

3

4

5

6

7

8

9

10

<myProperties>

  <property>

    <name>propertyName1</name>

    <value>propertyValue1</value>

  <property>

  <property>

    <name>propertyName2</name>

    <value>propertyValue2</value>

  <property>

</myProperties>

对象参数 

Mojo参数字段也可以是任意对象类型: 

Java

1

2

@Parameter

private MyObject myObject;

对应POM配置示例:  

XML

1

2

3

<myObject>

    <myField>test</myField>

</myObject>

测试和调试

单元测试

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

使用PlexusTestCase

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

maven-plugin-testing-harness

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

集成测试

在一个真实的Maven Build中测试Mojo,你需要提供一个测试用的Maven项目,插件需要安装到本地仓库。

maven-verifier

Maven验证器为你的JUnit测试用例提供以下功能:

  1. 启动Maven
  2. 根据构建的输出、日志信息进行断言
  3. 从src/test/resources目录抽取出一个Maven项目,并存放到临时工作目录中,针对此项目进行测试
  4. 操控本地仓库

Maven项目本身也在用此验证器进行集成测试,下面是一些说明如何使用该验证器的代码片段:

Java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

public class TrivialMavenVerifierTest extends TestCase {

    public void testMyPlugin() throws Exception {

        // 从/src/test/resources/中抽取测试用的Maven项目

        File testDir = ResourceExtractor.simpleExtractResources( getClass(), "/my-dummy-maven-project" );

        Verifier verifier;

        // 首选需要保证,此test项目产生的构件已经从本地仓库移除

        verifier = new Verifier( testDir.getAbsolutePath() );

        verifier.deleteArtifact( "org.apache.maven.its.itsample", "parent", "1.0", "pom" );

        verifier.deleteArtifact( "org.apache.maven.its.itsample", "checkstyle-test", "1.0", "jar" );

        verifier.deleteArtifact( "org.apache.maven.its.itsample", "checkstyle-assembly", "1.0", "jar" );

        // 执行Maven命令

        List cliOptions = new ArrayList();

        cliOptions.add( "-N" );

        verifier.executeGoal( "install" );

        // 验证上述命令是否成功

        verifier.verifyErrorFreeLog();

        // 清理,准备下一个命令的执行

        verifier.resetStreams();

    }

}

maven-invoker-plugin

这是一个插件,可以调用Maven,并提供一些BeanShell或Groovy的测试脚本: 

XML

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

<build>

  <plugins>

    <plugin>

      <groupId>org.apache.maven.plugins</groupId>

      <artifactId>maven-invoker-plugin</artifactId>

      <version>1.10</version>

      <configuration>

        <projectsDirectory>src/it</projectsDirectory>

        <pomIncludes>

          <pomInclude>**/pom.xml</pomInclude>

        </pomIncludes>

        <postBuildHookScript>verify</postBuildHookScript>

      </configuration>

      <executions>

        <execution>

          <goals>

            <goal>run</goal>

          </goals>

        </execution>

      </executions>

    </plugin>

    ...

  </plugins>

</build>

调试

远程调试

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

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

示例:

Shell

1

2

3

4

5

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的运行配置,设置环境变量:

Shell

1

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

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

Maven插件工具注解

所有注解示例

Java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

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()

    {

    }

}

相关文章:

  • 【论文复现】——基于逐点前进法的点云数据精简
  • 微服务框架 SpringCloud微服务架构 22 DSL 查询语法 22.1 DSL 查询分类和基本语法
  • Fairseq代码结构
  • [安装] Doris集群搭建环境
  • 【JavaScript】——JS数组的方法(全且详细)
  • GoWeb 进阶的实战项目,基于 Iris 框架实现 JWT 认证(附案例全代码)
  • MySQL数据库 —— 常用语句
  • AI虚拟人千亿级市场来袭,景联文科技提供全方面数据采集标注服务
  • [附源码]Python计算机毕业设计Django兴达五金日杂批发商店管理系统
  • u盘怎么数据恢复?靠这四种解决方法
  • 遥感影像目标检测:从CNN(Faster-RCNN)到Transformer(DETR)实践技术应用
  • Eureka
  • 【C++】list的模拟实现+迭代器的设计思维
  • AI 也会写代码了,但我并不担心
  • Java8 遍历List 使用stream().parallel()并发安全
  • 计算机毕业设计Java企业固定资产管理系统的设计实现(源代码+数据库+系统+lw文档)
  • 实验十一 级数与方程符号求解(MATLAB)
  • [附源码]Python计算机毕业设计Django疫情防控平台
  • Java - 通过反射进行赋值以及函数调用
  • OpUtils的网络扫描
  • 电加热油锅炉工作原理_电加热导油
  • 大型电蒸汽锅炉_工业电阻炉
  • 燃气蒸汽锅炉的分类_大连生物质蒸汽锅炉
  • 天津市维修锅炉_锅炉汽化处理方法
  • 蒸汽汽锅炉厂家_延安锅炉厂家
  • 山西热水锅炉厂家_酒店热水 锅炉
  • 蒸汽锅炉生产厂家_燃油蒸汽发生器
  • 燃煤锅炉烧热水_张家口 淘汰取缔燃煤锅炉
  • 生物质锅炉_炉
  • 锅炉天然气_天燃气热风炉