• 【Maven】插件开发实战


    前言

      众所周知,maven 实质上是一个插件执行框架,所有的工作都是通过插件完成的。包括我们日常使用到的类似 install、clean、deploy、compiler。。。这些命令,其实底层都是一个一个的 maven 插件。

    如何开发自己的插件

    1. maven 插件的命名规范

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

      好的,我们的第一个 maven 插件项目就叫 demo-maven-plugin 吧。

    2. 创建项目

      名称起好了,下一步就是创建这个项目。如果你使用 idea 的话,那么创建十分的便捷,按如下步骤即可:

    2.1 选择 org.apache.maven.archetypes:maven-archetype-mojo 为骨架创建项目

      

    1. 选择创建新项目
    2. 选择通过 maven 创建
    3. 勾选 Create from archetype 通过项目骨架创建
    4. 选择 org.apache.maven.archetypes:maven-archetype-mojo
    5. 点击下一步

    2.2 输入在第一步起的项目名

      

      点击 Next

    2.3 点击 Finish 完成项目创建

      

    2.4  分析项目文件

    项目结构

      

      可以看到生成的项目就是我们最最常见的 maven 项目的结构,生成的文件也很少,一个 pom.xml 文件,一个 MyMojo 文件,简单介绍一下这两个文件 

    pom.xml
     1 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     2   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
     3   <modelVersion>4.0.0</modelVersion>
     4   <groupId>cn.coder4j.study.example</groupId>
     5   <artifactId>demo-maven-plugin</artifactId>
     6   <packaging>maven-plugin</packaging>
     7   <version>1.0-SNAPSHOT</version>
     8   <name>demo-mavne-plugin Maven Mojo</name>
     9   <url>http://maven.apache.org</url>
    10   <dependencies>
    11     <dependency>
    12       <groupId>org.apache.maven</groupId>
    13       <artifactId>maven-plugin-api</artifactId>
    14       <version>2.0</version>
    15     </dependency>
    16     <dependency>
    17       <groupId>junit</groupId>
    18       <artifactId>junit</artifactId>
    19       <version>3.8.1</version>
    20       <scope>test</scope>
    21     </dependency>
    22   </dependencies>
    23 </project>

       首先生成的项目 pom.xml 中,自动依赖了两个项目,一个是 maven-plugin-api ,这个是开发 maven 插件必须依赖的核心包。另一个是单元测试时使用的 junit 包。这两个没什么要注意的,真正要注意的是这个项目的 packaging,通常我遇到的 packaging 都是 jar、war、pom,这里比较特殊是 maven-plugin。

    MyMojo.java
     1 package cn.coder4j.study.example;
     2 
     3 /*
     4  * Copyright 2001-2005 The Apache Software Foundation.
     5  *
     6  * Licensed under the Apache License, Version 2.0 (the "License");
     7  * you may not use this file except in compliance with the License.
     8  * You may obtain a copy of the License at
     9  *
    10  *      http://www.apache.org/licenses/LICENSE-2.0
    11  *
    12  * Unless required by applicable law or agreed to in writing, software
    13  * distributed under the License is distributed on an "AS IS" BASIS,
    14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15  * See the License for the specific language governing permissions and
    16  * limitations under the License.
    17  */
    18 
    19 import org.apache.maven.plugin.AbstractMojo;
    20 import org.apache.maven.plugin.MojoExecutionException;
    21 
    22 import java.io.File;
    23 import java.io.FileWriter;
    24 import java.io.IOException;
    25 
    26 /**
    27  * Goal which touches a timestamp file.
    28  *
    29  * @goal touch
    30  * 
    31  * @phase process-sources
    32  */
    33 public class MyMojo
    34     extends AbstractMojo
    35 {
    36     /**
    37      * Location of the file.
    38      * @parameter expression="${project.build.directory}"
    39      * @required
    40      */
    41     private File outputDirectory;
    42 
    43     public void execute()
    44         throws MojoExecutionException
    45     {
    46         File f = outputDirectory;
    47 
    48         if ( !f.exists() )
    49         {
    50             f.mkdirs();
    51         }
    52 
    53         File touch = new File( f, "touch.txt" );
    54 
    55         FileWriter w = null;
    56         try
    57         {
    58             w = new FileWriter( touch );
    59 
    60             w.write( "touch.txt" );
    61         }
    62         catch ( IOException e )
    63         {
    64             throw new MojoExecutionException( "Error creating file " + touch, e );
    65         }
    66         finally
    67         {
    68             if ( w != null )
    69             {
    70                 try
    71                 {
    72                     w.close();
    73                 }
    74                 catch ( IOException e )
    75                 {
    76                     // ignore
    77                 }
    78             }
    79         }
    80     }
    81 }  

      首先生成的类继承了 AbstractMojo 这个抽象类,这里是 maven 插件的规范要求,maven 插件必须要继承 AbstractMojo 并实现他的 execute 方法。

      另外可以看到类与方法使用了很多 tag。注意是 tag 而不是注解,注解是直接标记的,而 tag 是在文档注释里面的。

      其中 @goal 这个 tag 的作用是指定插件的命名,比如我们常用的 mvn clean,这个 clean 就是他的 @goal 。

      而 @phase 是绑定插件执行的生成周期,比如你绑定在 clean 这个周期,那你在执行 clean 的时候会自动触发你的插件。

      @parameter 用来指定插件的参数。

      小朋友你是否有很多问号?tag 这个东西写在文档注释里面的东西,方便是方便但是容易写错呀,写错没有语法报错,写对时候也没有语法提示,为什么不直接用注解的形式呢?原因是 java 的注解是 jdk1.5 之后才有的,而实现 maven 的时候还没有这种语法。所以要一条路走到黑,一直背这个历史包袱吗?当然不是,后面我们会说解决办法。另外,这种写法虽然不推荐使用了,但是有些 maven 的经典插件由于完成时间比较早,熟悉这些 tag 对于理解代码也有帮助。

    3. 开发插件

    3.1 代码未动,依赖先行 pom.xml

     1 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     2   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
     3   <modelVersion>4.0.0</modelVersion>
     4   <groupId>cn.coder4j.study.example</groupId>
     5   <artifactId>demo-maven-plugin</artifactId>
     6   <packaging>maven-plugin</packaging>
     7   <version>1.0-SNAPSHOT</version>
     8   <name>demo-mavne-plugin Maven Mojo</name>
     9   <url>http://maven.apache.org</url>
    10   <dependencies>
    11     <dependency>
    12       <groupId>org.apache.maven</groupId>
    13       <artifactId>maven-plugin-api</artifactId>
    14       <version>3.5.2</version>
    15     </dependency>
    16     <dependency>
    17       <groupId>org.apache.maven.plugin-tools</groupId>
    18       <artifactId>maven-plugin-annotations</artifactId>
    19       <version>3.5.2</version>
    20       <scope>provided</scope>
    21     </dependency>
    22   </dependencies>
    23 
    24   <build>
    25     <plugins>
    26       <plugin>
    27         <groupId>org.apache.maven.plugins</groupId>
    28         <artifactId>maven-plugin-plugin</artifactId>
    29         <version>3.5.2</version>
    30       </plugin>
    31     </plugins>
    32   </build>
    33 </project>

    相较于默认的 pom.xml 文件,我们做了如下几个变动:

    1. 升级 maven-plugin-api 的插件版本到 3.5.2 。原生的 2.0 实在是太老了。
    2. 添加 maven-plugin-annotations 这个依赖,还记得上面说的 tag 的事吗?有了这个依赖就可以直接使用注解了
    3. 添加 maven-plugin-plugin 插件依赖,添加这个依赖主要是为了在 jdk1.8 能编译通过,否则会报错

    3.2 DemoMojo.java

     1 /*
     2  *
     3  *  * *
     4  *  *  * blog.coder4j.cn
     5  *  *  * Copyright (C) 2016-2020 All Rights Reserved.
     6  *  *
     7  *
     8  */
     9 package cn.coder4j.study.example;
    10 
    11 import org.apache.maven.plugin.AbstractMojo;
    12 import org.apache.maven.plugin.MojoExecutionException;
    13 import org.apache.maven.plugin.MojoFailureException;
    14 import org.apache.maven.plugins.annotations.Mojo;
    15 import org.apache.maven.plugins.annotations.Parameter;
    16 
    17 /**
    18  * @author buhao
    19  * @version DemoMojo.java, v 0.1 2020-03-30 22:51 buhao
    20  */
    21 @Mojo(name = "hello")
    22 public class DemoMojo extends AbstractMojo {
    23 
    24     @Parameter(name = "name", defaultValue = "kiwi")
    25     private String name;
    26 
    27     public void execute() throws MojoExecutionException, MojoFailureException {
    28         getLog().info("hello " + name);
    29     }
    30 }

      首先,同生成的类一样,我们的类必须继承 AbstractMojo 并实现他的 execute 方法,而 execute 方法其实就是这个插件的入口类。

      示例代码中有两个很重要的注解,一个是 @Mojo ,它主要用来定义插件相关的信息相当于上面说的 @goal ,其中 name 属性用来指定这个插件名称,同 clean 类似。

      另外一个重要注解 @Parameter ,则是用来指定插件运行时使用的参数,其中 name 是参数名,defaultValue 顾名思义是默认值,也就是在用户没有设置的时候使用的值。

      详细的插件及作用如下:

     1 import org.apache.maven.execution.MavenSession;
     2 import org.apache.maven.plugin.AbstractMojo;
     3 import org.apache.maven.plugin.MojoExecution;
     4 import org.apache.maven.plugin.descriptor.PluginDescriptor;
     5 import org.apache.maven.plugins.annotations.Component;
     6 import org.apache.maven.plugins.annotations.Execute;
     7 import org.apache.maven.plugins.annotations.InstantiationStrategy;
     8 import org.apache.maven.plugins.annotations.LifecyclePhase;
     9 import org.apache.maven.plugins.annotations.Mojo;
    10 import org.apache.maven.plugins.annotations.Parameter;
    11 import org.apache.maven.plugins.annotations.ResolutionScope;
    12 import org.apache.maven.project.MavenProject;
    13 import org.apache.maven.settings.Settings;
    14  
    15        // 此Mojo对应的目标的名称
    16 @Mojo( name = "<goal-name>",
    17        aggregator = <false|true>, 
    18        configurator = "<role hint>",
    19        // 执行策略
    20        executionStrategy = "<once-per-session|always>",
    21        inheritByDefault = <true|false>,
    22        // 实例化策略
    23        instantiationStrategy = InstantiationStrategy.<strategy>,
    24        // 如果用户没有在POM中明确设置此Mojo绑定到的phase,那么绑定一个MojoExecution到那个phase
    25        defaultPhase = LifecyclePhase.<phase>,
    26        requiresDependencyResolution = ResolutionScope.<scope>,
    27        requiresDependencyCollection = ResolutionScope.<scope>,
    28        // 提示此Mojo需要被直接调用(而非绑定到生命周期阶段)
    29        requiresDirectInvocation = <false|true>,
    30        // 提示此Mojo不能在离线模式下运行
    31        requiresOnline = <false|true>,
    32        // 提示此Mojo必须在一个Maven项目内运行
    33        requiresProject = <true|false>,
    34        // 提示此Mojo是否线程安全,线程安全的Mojo支持在并行构建中被并发的调用
    35        threadSafe = <false|true> ) // (since Maven 3.0)
    36  
    37 // 何时执行此Mojo
    38 @Execute( goal = "<goal-name>",           // 如果提供goal,则隔离执行此Mojo
    39           phase = LifecyclePhase.<phase>, // 在此生命周期阶段自动执行此Mojo
    40           lifecycle = "<lifecycle-id>" )  // 在此生命周期中执行此Mojo
    41 public class MyMojo
    42     extends AbstractMojo
    43 {
    44     
    45     @Parameter( name = "parameter",
    46                 // 在POM中可使用别名来配置参数
    47                 alias = "myAlias",
    48                 property = "a.property",
    49                 defaultValue = "an expression, possibly with ${variables}",
    50                 readonly = <false|true>,
    51                 required = <false|true> )
    52     private String parameter;
    53  
    54     @Component( role = MyComponentExtension.class,
    55                 hint = "..." )
    56     private MyComponent component;
    57  
    58  
    59     @Parameter( defaultValue = "${session}", readonly = true )
    60     private MavenSession session;
    61  
    62     @Parameter( defaultValue = "${project}", readonly = true )
    63     private MavenProject project;
    64  
    65     @Parameter( defaultValue = "${mojoExecution}", readonly = true )
    66     private MojoExecution mojo;
    67  
    68     @Parameter( defaultValue = "${plugin}", readonly = true )
    69     private PluginDescriptor plugin;
    70  
    71     @Parameter( defaultValue = "${settings}", readonly = true )
    72     private Settings settings;
    73  
    74     @Parameter( defaultValue = "${project.basedir}", readonly = true )
    75     private File basedir;
    76  
    77     @Parameter( defaultValue = "${project.build.directory}", readonly = true )
    78     private File target;
    79  
    80     public void execute()
    81     {
    82     }
    83 }

      回到示例上了,我们这个插件作用很简单,根据配置输出 hello xxx,如果没有配置就输出 hello kiwi。我们在写插件时,当然不会这样写,但是通过这个 demo,你就掌握了 maven 插件的大部分知识,可以自己做一些很有趣的插件。

    4. 使用插件

      首先上面我们的代码写完了,必须要 Install 一下,否则别的项目无法直接依赖,如果你还想给其它人使用,那还需上传到 maven 仓库。

    4.1 依赖插件

    <build>
            <plugins>
                <plugin>
                    <groupId>cn.coder4j.study.example</groupId>
                    <artifactId>demo-maven-plugin</artifactId>
                    <version>1.0-SNAPSHOT</version>
                </plugin>
            </plugins>
        </build>

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

    4.2 启动插件

      

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

      好的,我们双击一下,demo:hello ,会输出如下日志:
      

    这样,我们的第一个 Maven 插件就好了。

    4.3 配置参数

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

     <build>
            <plugins>
                <plugin>
                    <groupId>cn.coder4j.study.example</groupId>
                    <artifactId>demo-maven-plugin</artifactId>
                    <version>1.0-SNAPSHOT</version>
                    <configuration>
                        <name>tom</name>
                    </configuration>
                </plugin>
            </plugins>
        </build>

    其中 configuration 标签内的标签,对应你定义的参数名称,而且 idea 还有语法提示,很 nice。

    好的,我们再运行一下,结果如下:

      

    好的,大功告成。

    原文链接:https://www.cnblogs.com/kiwifly/p/12602407.html

     

  • 相关阅读:
    SVN报错working copy is not uptodate
    AndroidStudio中获得的VersionCode一直为1和VersionName一直为1.0
    OkHttp
    MockWebServer使用指南(转载)
    Android的Toolbar(含溢出菜单设置[弹出菜单的使用])的使用PopMenu的样式
    8-13笔记-安卓兼容
    自定义Dialog
    安卓圆角Button XML文件
    递归方法扫面文件夹(JAVA控制台程序)
    8月12笔记-安卓文件扫描
  • 原文地址:https://www.cnblogs.com/h--d/p/15341010.html
Copyright © 2020-2023  润新知