如何开发一个对自己有用的Maven Plugin

本文部分内容来源于Maven官方网址:https://maven.apache.org/plugin-developers/index.html

1.介绍

Maven实际上是Maven Plugin集合的核心框架,而Plugin是Maven执行实际操作的关键,例如常见的插件:创建Jar文件、创建WAR文件、编译代码、单元测试、创建项目文档等.你可以想象到的对项目执行的所有操作几乎都是Maven Plugin来执行的.

Maven Plugin的实际上是由一个或多个MOJO组成

MOJO来源于POJO(Plain-Old-Java-Object),而M所代表的是Maven的意思

所以Maven插件的Mojo所代表的意思:Maven Plain Old Java Object

所以在Maven Plugin中的核心组成实际上就是一组简单的MOJO

2.第一个Maven插件

既然MOJO是Maven的插件核心实体类,那么我们开发自己的Maven插件只需要创建Maven的POJO就可以了

2.1Maven插件命名规范

以Apache官方团队维护的Maven插件命名风格是maven--plugin,通常我们自己开发Maven插件不能使用这种命名,这种命名模式是对Apache Maven商标的侵犯

第三方Maven插件的命名规范:-maven-plugin

2.2第一个Maven插件

要开发一个Maven插件,首先第一步是新建MOJO实体类,通过继承AbstractMojo类,并重写该类的execute方法,通过注解@Mojo标注这是一个Maven插件,如下代码:

package sample.plugin;
 
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Mojo;
 
/**
 * Maven Plugin HelloWorld
 *
 */
@Mojo( name = "hello")
public class GreetingMojo extends AbstractMojo
{
    public void execute() throws MojoExecutionException
    {
        //会在执行该Plugin时,在控制台进行打印输出
        System.out.println("Hello Maven Plugin");
    }
}

2.3Maven插件的条件约束

Maven插件实际上也是一组jar项目,但是项目类型会所有不同,主要区别:

1、按照规定的maven插件命名风格进行命名

2、我们在Maven插件项目中,在pom.xml文件需要将packagin类型设置为maven-plugin

3、需要添加Apache Maven官方提供的插件依赖,例如上面的@Mojo注解就来源于maven-plugin-annotationsjar包

pom.xml示例如下:

<project>
  <modelVersion>4.0.0modelVersion>
 
  <groupId>sample.plugingroupId>
  
  <artifactId>hello-maven-pluginartifactId>
  <version>1.0-SNAPSHOTversion>
  
  <packaging>maven-pluginpackaging>
 
  <name>Sample Parameter-less Maven Pluginname>
 
  <dependencies>
    
    <dependency>
      <groupId>org.apache.mavengroupId>
      <artifactId>maven-plugin-apiartifactId>
      <version>3.0version>
    dependency>
 
    
    <dependency>
      <groupId>org.apache.maven.plugin-toolsgroupId>
      <artifactId>maven-plugin-annotationsartifactId>
      <version>3.4version>
      <scope>providedscope>
    dependency>
  dependencies>
project>

2.4使用Maven插件

我们在开发完成Maven插件后,需要执行Maven Install命令,将Maven插件安装在本地Maven仓库中,然后在项目中就可以使用了

2.4.1通过在pom.xml添加使用Plugin组件

我们在项目中的节点添加我们开发完成的Maven插件即可使用,如下:

<plugin>
    <groupId>org.examplegroupId>
    <artifactId>hello-maven-pluginartifactId>
    <version>1.0version>
plugin>

此时,在项目的命令行,可以通过执行命令mvn hello:hello执行我们的插件

这里的命令所代表的含义:mvn pluginName:mojoName

第一个hello是因为我们的插件是以hello-maven-plugin进行命名的,这也是Maven插件的简写方式

第二个hello是因为@Mojo注解中定义的name名称

@Mojo( name = "hello")
public class GreetingMojo extends AbstractMojo
{
    //more...
}

####2.4.2在settings.xml全局文件中添加插件引用

我们可以在${user.home}/.m2/settings.xml配置文件中添加我们的插件groupId,例如:

<pluginGroups>
    
    <pluginGroup>org.examplepluginGroup>
  pluginGroups>

这样即使我们不在项目的pom.xml中引用插件,依然可以全局执行该插件

2.4.3在Maven的生命周期构造时执行Maven插件

我们可以指定我们的插件在Maven项目构建时某一个周期(compile,package等等)时执行

例如以下配置,指定我们的插件在项目compile阶段执行我们的插件操作

 <plugin>
     <groupId>org.examplegroupId>
     <artifactId>hello-maven-pluginartifactId>
     <version>1.0version>
     <executions>
         <execution>
             <phase>compilephase>
             <goals>
                 
                 <goal>hellogoal>
             goals>
         execution>
     executions>
plugin>

3.Maven插件外部参数引用

我们通常在使用Maven插件时,需要将外部参数传入插件中,插件根据传入配置文件进行初始化,然后执行操作

例如Mybatis的代码生成插件,配置如下:

<plugin>
    <groupId>org.mybatis.generatorgroupId>
    <artifactId>mybatis-generator-maven-pluginartifactId>
    <version>1.3.2version>
    <configuration>
        
        <configurationFile>src/main/resources/generator/generatorConfig.xmlconfigurationFile>
        <overwrite>trueoverwrite>
        <verbose>trueverbose>
    configuration>
plugin>

在mybatis的generator插件中,会有参数的定义,通过@Parameter注解进行引用,代码如下:

@Mojo(name = "generate", defaultPhase = LifecyclePhase.GENERATE_SOURCES,
        requiresDependencyResolution = ResolutionScope.TEST)
public class MyBatisGeneratorMojo extends AbstractMojo {
    
    private ThreadLocal<ClassLoader> savedClassloader = new ThreadLocal<>();

    /**
     * Maven Project.
     *
     */
    @Parameter(property = "project", required = true, readonly = true)
    private MavenProject project;
 

    /**
     * Location of the configuration file.
     */
    @Parameter(property = "mybatis.generator.configurationFile",
            defaultValue = "${project.basedir}/src/main/resources/generatorConfig.xml", required = true)
    private File configurationFile;
    
    //more....
}

Maven插件通过参数外置,对于Maven插件来说是非常有用的,参数提供两个非常重要的功能:

  • 用户可以根据参数开发出适合自己需求的插件,最终真正达到Plugin的目的
  • 它提供了一种无需导航对象即可轻松地从POM中提取元素值的方法。

3.1定义参数

在Maven插件中定义插件非常简单,只需要使用@Parameter注解即可,例如:

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

defaultValue所代表的是参数的默认值,开发者可以使用项目引用的表达式进行配置,例如${project.version}

关于项目引用的表达式配置,更多的信息请参考PluginParameterExpressionEvaluator

property属性通过引用用户通过-D选项设置的系统属性,该参数运行从命令行配置Mojo参数

3.2传入参数

在Maven的Mojo中定义好参数后,我们在Maven的pom.xml文件中引用插件时,可以通过configuration节点添加参数的引用,示例如下:

 <plugin>
     <groupId>org.examplegroupId>
     <artifactId>hello-maven-pluginartifactId>
     <version>1.0version>
     <configuration>
        <greeting>Welcomegreeting>
      configuration>
plugin>

3.3参数类型

####3.3.1布尔类型(Boolean)

在Mojo中定义布尔类型的参数变量,代码如下:

/**
* My boolean.
*/
@Parameter
private boolean myBoolean;

在配置文件中添加引用

<myBoolean>truemyBoolean>

只有配置了true的情况下,myBoolean变量为真,其他情况该变量的值都是false

####3.3.2数值类型(Integer)

数值类型包含的类型:byteByteintIntegerlongLongshortShort

当读取configuration节点中的配置属性时,会调用Integer.parseInt()方法或者valueOf()方法进行将文本数值进行转换(如果类型是Integer的话),这意味着配置的数值类型必须是合法的

Mojo中参数定义

/**
* My Integer.
*/
@Parameter
private Integer myInteger;

在配置文件中添加引用

<myInteger>10myInteger>

####3.3.3浮点型类型(Float)

浮点类型包含doubleDoublefloatFloat类型,同整数类型,读取配置文件时会调用valueOf()方法进行转换

Mojo中参数定义:

/**
* My Double.
*/
@Parameter
private Double myDouble;

在配置文件中添加引用

<myDouble>1.0myDouble>

####3.3.4日期类型(Date)

日期类型需要传入指定格式:yyyy-MM-dd HH:mm:ss.S a或者yyyy-MM-dd HH:mm:ssa

读取配置文件时最终是通过调用DateFormat.parse()方法进行转换

Mojo中参数定义:

/**
* My Date.
*/
@Parameter
private Date myDate;

在配置文件中添加引用

<myDate>2005-10-06 2:22:55.1 PMmyDate>

####3.3.5文件和文件夹(File)

可以配置File类型,如果配置的路径不是以根目录开始的(非/开头或者不是以盘符C:等开头),则该文件时相对于项目的pom.xml引用的相对路径

Mojo中参数定义

/**
* My File.
*/
@Parameter
private File myFile;

在配置文件中添加引用

<myFile>c:\tempmyFile>

####3.3.6链接(URL)

URL类型,配置的格式必须遵循RFC 2396准则,并且类似于任何Web浏览器URL,在进行转换时,对URL的内容没有任何限制

Mojo中参数定义

/**
* My URL.
*/
@Parameter
private URL myURL;

在配置文件中添加引用

<myURL>http://maven.apache.orgmyURL>

3.3.7文本字符

文本字符主要包含的类型包括:charCharacterStringBufferString

3.3.8枚举类型(Enum)

枚举类型同样可以使用,首先你得在你的Plugin中定义枚举,然后才能使用,例如枚举的定义如下:

public enum Color {
    GREEN,
    RED,
    BLUE
}

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

在配置文件中引用

<myColor>GREENmyColor>

当然,你也可以通过defaultValue默认值进行引用,如下:

public enum Color {
    GREEN,
    RED,
    BLUE
}

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

3.3.9数组&集合类型

下面列出了各种类型的复合对象,他们可以作为mojo中的参数

3.3.9.1数组(Array)

在Mojo类中定义如下:

/**
* My Array.
*/
@Parameter
private String[] myArray;

在配置文件引用:

<myArray>
  <param>value1param>
  <param>value2param>
myArray>
3.3.9.2集合(Collection)

在Java中主要实现java.util.Collection接口的集合类,例如ArrayList或者HashSet

Mojo类中定义如下:

/**
* My List.
*/
@Parameter
private List myList;

在配置文件中引用:

<myList>
  <param>value1param>
  <param>value2param>
myList>
3.3.9.3键值对(Map)

对于键值对类型主要是以实现java.util.Map类型的类型,例如HashMap,但是这里不包含java.util.Properties

定义规则value

在Mojo类中定义如下:

/**
* My Map.
*/
@Parameter
private Map myMap;

在配置文件中引用:

<myMap>
  <key1>value1key1>
  <key2>value2key2>
myMap>
3.3.9.4配置文件(Properties)

针对配置文件的类型java.util.Properties,配置的规则有所不同

定义规则:

<property>
    <name>myNamename>
    <value>myValuevalue>
property>

在Mojo中的定义如下:

/**
* My Properties.
*/
@Parameter
private Properties myProperties;

在配置文件中引用:

<myProperties>
  <property>
    <name>propertyName1name>
    <value>propertyValue1value>
  <property>
  <property>
    <name>propertyName2name>
    <value>propertyValue2value>
  <property>
myProperties>

4.Maven插件核心注解释义

通过阅读上面的章节内容,我们要开发一个基本的能工作的插件基本需要使用到2个核心注解,分别是:

  • @Mojo:定义一个Maven插件的Mojo标志注解

  • @Parameter:该Mojo中所需要引用的外部参数

4.1@Mojo注解

完整请参考:http://maven.apache.org/developers/mojo-api-specification.html#The_Descriptor_and_Annotations

示例代码:

/**
 * Mojo Description. @Mojo( name = "" ) is the minimal required annotation.
 * @since 
 * @deprecated 
 */
@Mojo( name = "",
       aggregator = <false|true>, 
       configurator = "",
       executionStrategy = "",
       inheritByDefault = <true|false>,
       instantiationStrategy = InstantiationStrategy.<strategy>,
       defaultPhase = LifecyclePhase.<phase>,
       requiresDependencyResolution = ResolutionScope.<scope>,
       requiresDependencyCollection = ResolutionScope.<scope>, // (since Maven 3.0)
       requiresDirectInvocation = <false|true>,
       requiresOnline = <false|true>,
       requiresProject = <true|false>,
       requiresReports = <false|true>, // (unsupported since Maven 3.0)
       threadSafe = <false|true> ) // (since Maven 3.0)
@Execute( goal = "",
          phase = LifecyclePhase.<phase>,
          lifecycle = "" )
public class MyMojo
    extends AbstractMojo
{
    //more...
}

先来看@Mojo注解的源码,定义的属性

@Documented
@Retention( RetentionPolicy.CLASS )
@Target( ElementType.TYPE )
@Inherited
public @interface Mojo
{
    /**
     * goal name (required).
     * @return the goal name
     */
    String name();

    /**
     * default phase to bind your mojo.
     * @return the default phase
     */
    LifecyclePhase defaultPhase() default LifecyclePhase.NONE;

    /**
     * the required dependency resolution scope.
     * @return the required dependency resolution scope
     */
    ResolutionScope requiresDependencyResolution() default ResolutionScope.NONE;

    /**
     * the required dependency collection scope.
     * @return the required dependency collection scope 
     */
    ResolutionScope requiresDependencyCollection() default ResolutionScope.NONE;

    /**
     * your Mojo instantiation strategy. (Only per-lookup and singleton are supported)
     * @return the instantiation strategy
     */
    InstantiationStrategy instantiationStrategy() default InstantiationStrategy.PER_LOOKUP;

    /**
     * execution strategy: once-per-session or always.
     * @return once-per-session or always
     */
    String executionStrategy() default "once-per-session";

    /**
     * does your mojo requires a project to be executed?
     * @return requires a project
     */
    boolean requiresProject() default true;

    /**
     * does your mojo requires a reporting context to be executed?
     * @return requires a reporting context
     */
    boolean requiresReports() default false;

    /**
     * if the Mojo uses the Maven project and its child modules.
     * @return uses the Maven project and its child modules
     */
    boolean aggregator() default false;

    /**
     * can this Mojo be invoked directly only?
     * @return invoked directly only
     */
    boolean requiresDirectInvocation() default false;

    /**
     * does this Mojo need to be online to be executed?
     * @return need to be online
     */
    boolean requiresOnline() default false;

    boolean inheritByDefault() default true;

    /**
     * own configurator class.
     * @return own configurator class
     */
    String configurator() default "";

    /**
     * is your mojo thread safe (since Maven 3.x)?
     * @return is thread safe
     */
    boolean threadSafe() default false;
}

其实通过注释,我们基本已经能知道部分属性的含义,详细说明如下:

4.1.1name

定义Maven插件的goal名称,我们一般在项目中使用Maven插件时如下Xml:

<plugin>
    <groupId>org.examplegroupId>
    <artifactId>hello-maven-pluginartifactId>
    <version>1.0version>
    <executions>
        <execution>
            <phase>compilephase>
            <goals>
                
                <goal>hellogoal>
            goals>
        execution>
    executions>
plugin>

4.1.2defaultPhase

该属性代表默认在Maven那个生命周期(常见的compilepackageinstall等等)时执行,对于自定义开发的Maven插件,一般默认是None,即不指定

Xml配置

<plugin>
    <groupId>org.examplegroupId>
    <artifactId>hello-maven-pluginartifactId>
    <version>1.0version>
    <executions>
        <execution>
             
            <phase>compilephase>
            <goals>
                <goal>hellogoal>
            goals>
        execution>
    executions>
plugin>

####4.1.3requiresDependencyResolution

指定Maven插件依赖jar包的Scope(常见的如test,runtime,compile等)范围,该值是一个枚举

枚举定义如下:

枚举值 说明
None
COMPILE 所规定的依赖scope范围是compile+system+provided
COMPILE_PLUS_RUNTIME 在上面COMPILE的基础上加上runtime
RUNTIME 所规定的scope范围是compile+runtime
RUNTIME_PLUS_SYSTEM RUNTIME的基础上添加system
TEST 该值几乎涵盖所有,范围是compile+system+provided+runtime+test

如果指定为默认的None,则范围默认为RUNTIME

4.1.4requiresDependencyCollection

Flags this mojo as requiring information about the dependencies that would make up the specified class path. As the name suggests, this annotation is similar to @requiresDependencyResolution and supports the same values for . The important difference is that this annotation will not resolve the files for the dependencies, i.e. the artifacts associated with a Maven project can lack a file. As such, this annotation is meant for mojos that only want to analyze the set of transitive dependencies, in particular during early lifecycle phases where full dependency resolution might fail due to projects which haven’t been built yet. A mojo may use both this annotation and @requiresDependencyResolution at the same time. The resolution state of any dependency that is collected but not requested to be resolved is undefined. Since Maven 3.0.

4.1.5instantiationStrategy

你的Maven插件Mojo实例化策略,该值是一个枚举类型

4.2@Parameter注解

@Parameter注解所代表的是我们的插件Mojo所需要的外部参数定义,可以通过外部参数传入的方式让我们的插件更健壮

4.2.1name

参数的名称,默认使用java字段名

4.2.2alias

指定参数的别名,此选项让我们在配置configuration节点时可以根据参数的别名配置参数值

4.2.3property

property参数存在的意义是运行我们通过命令行使用Maven插件时,通过-D参数指定Mojo中的属性进行赋值,从而使Mojo完成工作

即不需要通过在项目的pom.xml中添加plugin来使用Maven插件

例如执行我们的第一个插件时(在全局settings.xml配置pluginGroup节点)

mvn hello:hello -DparameterName=test

4.2.4defaultValue

参数的默认值

4.2.5required

该参数值是否必须

4.2.6readonly

该参数指定我们的Mojo中的属性不能由用户直接配置(例如在pom.xml中指定)

例如我们需要使用到和本工程相关的参数来作为最终输出参数,如下:

@Parameter(property = "project", required = true, readonly = true)
private MavenProject project;

5.实战

根据Maven Plugin的开发思想,开发了一个模板工厂template-factory的Maven Plugin

目前在工作中,碰到了越来越多的可重复性代码,每次针对一个新的需求时,都需要重新手写一遍(service\controller\vo\dto等),极其繁琐,因此开发了template-factory

项目地址:https://gitee.com/dt_research_institute/template-factory

你可能感兴趣的:(程序杂记)