Maven插件开发
创始人
2024-03-23 02:57:26

简介

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

插件本身也是Maven构件,构件标识的命名约定是 -maven-plugin。不要命名为 maven--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

  4.0.0

  sample.plugin

  hello-maven-plugin

  1.0-SNAPSHOT

  

  maven-plugin

  Sample Parameter-less Maven Plugin

  

    

      org.apache.maven

      maven-plugin-api

      3.0

    

    

    

      org.apache.maven.plugin-tools

      maven-plugin-annotations

      3.4

      provided

    

  

使用插件

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

XML

1

2

3

4

5

6

7

8

9

  

    

      sample.plugin

      hello-maven-plugin

      1.0-SNAPSHOT

    

  

然后调用我们的插件:

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

        sample.plugin

    调用命令简化为 mvn hello:sayhi

附到生命周期

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

XML

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

  

    

      sample.plugin

      hello-maven-plugin

      1.0-SNAPSHOT

      

        

          compile

          

            sayhi

          

        

      

    

  

则在构建生命周期的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

  sample.plugin

  hello-maven-plugin

  1.0-SNAPSHOT

  

    

    Welcome

  

插件参数表达式

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

对象说明
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

    value1

    value2

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

XML

1

2

3

4

    value1

    value2

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

XML

1

2

3

4

5

6

7

8

9

10

  

    propertyName1

    propertyValue1

  

  

    propertyName2

    propertyValue2

  

对象参数 

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

Java

1

2

@Parameter

private MyObject myObject;

对应POM配置示例:  

XML

1

2

3

    test

测试和调试

单元测试

用于测试单个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

  

    

      org.apache.maven.plugins

      maven-invoker-plugin

      1.10

      

        src/it

        

          **/pom.xml

        

        verify

      

      

        

          

            run

          

        

      

    

    ...

  

调试

远程调试

要调试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 = "",

       aggregator = ,

       configurator = "",

       // 执行策略

       executionStrategy = "",

       inheritByDefault = ,

       // 实例化策略

       instantiationStrategy = InstantiationStrategy.,

       // 如果用户没有在POM中明确设置此Mojo绑定到的phase,那么绑定一个MojoExecution到那个phase

       defaultPhase = LifecyclePhase.,

       requiresDependencyResolution = ResolutionScope.,

       requiresDependencyCollection = ResolutionScope.,

       // 提示此Mojo需要被直接调用(而非绑定到生命周期阶段)

       requiresDirectInvocation = ,

       // 提示此Mojo不能在离线模式下运行

       requiresOnline = ,

       // 提示此Mojo必须在一个Maven项目内运行

       requiresProject = ,

       // 提示此Mojo是否线程安全,线程安全的Mojo支持在并行构建中被并发的调用

       threadSafe = ) // (since Maven 3.0)

// 何时执行此Mojo

@Execute( goal = "",           // 如果提供goal,则隔离执行此Mojo

          phase = LifecyclePhase., // 在此生命周期阶段自动执行此Mojo

          lifecycle = "" )  // 在此生命周期中执行此Mojo

public class MyMojo

    extends AbstractMojo

{

    

    @Parameter( name = "parameter",

                // 在POM中可使用别名来配置参数

                alias = "myAlias",

                property = "a.property",

                defaultValue = "an expression, possibly with ${variables}",

                readonly = ,

                required = )

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

    {

    }

}

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...