Maven实战-做一个自己的Maven插件
前言
写本篇文章主要源于看了一下yapi接口信息采集代码,此工具就是实现了一个maven插件,然后发现自己对如何开发maven插件好像并不是很熟悉,就趁机学习了一下。maven本身主要功能都插件提供的,因此了解maven插件对学习maven也是很有帮助的。本文通过实现一个简单的插件来讲述如果和开发maven插件,比较简单基础。
Maven简单介绍
引用官网的说明:
Apache Maven is a software project management and comprehension tool. Based on the concept of a project object model (POM), Maven can manage a project's build, reporting and documentation from a central piece of information.
可以清晰的看到,Apache Maven是一个软件项目管理和理解工具。它基于项目工程对象建模(POM)的概念,能够通过一个中心信息管理项目构建,报告?和文档。简单的理解就是个中心管理项目对象模型的东西。
当然本文的重点是介绍插件构建,首页能够看到
image.png地址如下
插件简介
Maven大家应该都知道,是一个非常强大的构建工具,生命周期包含项目的:清理,初始化,编译,测试,打包,验证,部署和站点生成等几乎所有的构建步骤,同时,它还是一个依赖管理工具和项目管理工具,帮助我们高效完成这些繁琐的任务,然后大家就可以高高兴兴的写代码了。
而Maven的核心是它的生命周期,但实际上,生命周期又不做任何事情,所有的事情都是交给插件来完成的,每个插件都可以有多个功能,每个功能就是一个插件目标,这种设计思想和模板方法的设计模式比较类似。
例如:我们最常用的命令:mvn clean install,这个命令实际上就使用了maven-clean-plugin和maven-install-plugin插件。目的是清理上一次构建生成的文件并将工程编译打包,安装到maven本地仓库。
开发自己的插件
maven的文档里介绍说,大部分的插件都可以在插件库里面找到,如果实在找不到才需要自己实现一个maven插件,找不到的比例又非常低,据说只有1%(这个具体数字没考证过)
1. pom必要内容
依赖
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>3.5.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<version>3.6.0</version>
<scope>provided</scope>
</dependency>
注意点
image.png常规的配置这里就不讲, 注意要配置packaging的类型,配置如下:
<packaging>maven-plugin</packaging>
<artifactId>hyjal-maven-plugin</artifactId>
<groupId>com.funnycode.maven.plugin</groupId>
<version>1.0.0-SNAPSHOT</version>
You will typically name your plugin <yourplugin>-maven-plugin.
Calling it maven-<yourplugin>-plugin (note "Maven" is at the beginning of the plugin name) is strongly discouraged since it's a reserved naming pattern for official Apache Maven plugins maintained by the Apache Maven team with groupId org.apache.maven.plugins. Using this naming pattern is an infringement of the Apache Maven Trademark.
如官方所说,我们插件名字选择<yourplugin>-maven-plugin,即artifactId
2. 创建一个mojo类
什么是Mojo?
A Mojo is really just a goal in Maven, and plug-ins consist of any number of goals (Mojos). Mojos can be defined as annotated Java classes or Beanshell script. A Mojo specifies metadata about a goal: a goal name, which phase of the lifecycle it fits into, and the parameters it is expecting.
https://maven.apache.org/guides/introduction/introduction-to-plugins.html
Mojo我们简单的理解就是个Maven的入口目标,注意能够被定义成带注解的Java类。实际列子如下:
@Mojo(name = "hyjal")
public class HyjalPlugin extends AbstractMojo {
@Parameter(defaultValue = "${project.groupId}")
private String groupId;
@Parameter(defaultValue = "${project.artifactId}")
private String artifactId;
@Parameter(defaultValue = "${project.version}")
private String version;
@Parameter(defaultValue = "hello")
private String greeting;
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
StringBuilder sb = new StringBuilder();
sb.append(greeting)
.append(":")
.append("groupId:")
.append(groupId)
.append(" artifactId:")
.append(artifactId)
.append(" version:")
.append(version);
this.getLog().info("========================================");
this.getLog().info("==============Hyjal Plugin==============");
this.getLog().info("========================================");
this.getLog().info(sb.toString());
}
}
- The class
org.apache.maven.plugin.AbstractMojo
provides most of the infrastructure required to implement a mojo except for theexecute
method. - The annotation "
@Mojo
" is required and control how and when the mojo is executed. - The
execute
method can throw two exceptions:-
org.apache.maven.plugin.MojoExecutionException
if an unexpected problem occurs. Throwing this exception causes a "BUILD ERROR" message to be displayed. -
org.apache.maven.plugin.MojoFailureException
if an expected problem (such as a compilation failure) occurs. Throwing this exception causes a "BUILD FAILURE" message to be displayed.
-
- 还有个log的这边不做说明
3. 导出插件
在pom增加build的内容,
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<version>3.4</version>
<configuration>
<!-- 如果使用maven2,就一定要加这一行 -->
<skipErrorNoDescriptorsFound>true</skipErrorNoDescriptorsFound>
</configuration>
<executions>
<execution>
<id>mojo-descriptor</id>
<goals>
<goal>descriptor</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
配置好后,执行mvn clean install
就会打到本地仓库
4. 项目引入
在另一个模块的pom中引入
<plugin>
<groupId>com.funnycode.maven.plugin</groupId>
<artifactId>hyjal-maven-plugin</artifactId>
<version>1.0.0-SNAPSHOT</version>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>hyjal</goal>
</goals>
</execution>
</executions>
<configuration>
<greeting>welcome</greeting>
</configuration>
</plugin>
5. 配置介绍
可以看到上面的<configuration>
标签里面有个<greeting>
标签,它和代码中的
@Parameter(defaultValue = "hello")
private String greeting;
字段名称greeting
对应,@Parameter
是属性映射的一个注解,defaultValue是hello,如果不配置Mojo对象的此属性就是hello,而例子中我们设置成welcome
配置的内容比较多,可以查看官方说明如下:
http://maven.apache.org/guides/mini/guide-configuring-plugins.html
6. 插件执行命令
mvn groupId:artifactId:version:goal
我们的测试插件就是
mvn com.gongdao.sample:hyjal-maven-plugin:1.0.0-SNAPSHOT:hyjal
效果如下:
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building sample-all-start 1.0.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- hyjal-maven-plugin:1.0.0-SNAPSHOT:hyjal (default-cli) @ sample-all-start ---
[INFO] ========================================
[INFO] ==============Hyjal Plugin==============
[INFO] ========================================
[INFO] welcome:groupId:com.funnycode.sample artifactId:sample-all-start version:1.0.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.548s
[INFO] Finished at: Thu Jul 25 13:30:31 CST 2019
[INFO] Final Memory: 7M/309M
7. 简化命令
通过上面的操作,我们需要执行的命令如下:
mvn com.gongdao.sample:hyjal-maven-plugin:1.0.0-SNAPSHOT:hyjal
对于冗长的命令我们肯定用的不舒服,maven提供了几种解决方案:
- mvn com.alibaba.maven.plugins.test:maven-gav-plugin:gav,去掉版本后,会调用本地仓库的最新版本
- maven解析插件仓库元数据时会先找默认的groupId,默认的有:org.apache.maven.plugins和org.codehaus.mojo两个,其次找到对应的artifactId,然后结合当前groupId和最新的版本来确定坐标,即可以将自己的groupId改为:org.apache.maven.plugins或org.codehaus.mojo
- 通过配置settings.xml文件让maven检查其他的groupId上的插件仓库元数据,即在settings文件中添加如下配置:
<pluginGroups>
<pluginGroup>com.funnycode.maven.plugins</pluginGroup>
</pluginGroups>
就可以使用mvn hyjal:hyjal
来运行了插件了
8. 插件工程创建
Mojo archetype
mvn archetype:generate \
-DgroupId=sample.plugin \
-DartifactId=hello-maven-plugin \
-DarchetypeGroupId=org.apache.maven.archetypes \
-DarchetypeArtifactId=maven-archetype-plugin
常用插件
大量的插件可以从apache和codehaus获得,还有一些分布在googlecode,sourceforge,github等托管服务中,如Alibaba也有自己的插件库,groupId为:com.alibaba.maven.plugins
结束语
本文只是个简单的入门例子,方便大家的学习。
比如我们看看Mojo的代码有很多属性:
public @interface Mojo {
String name();
LifecyclePhase defaultPhase() default LifecyclePhase.NONE;
ResolutionScope requiresDependencyResolution() default ResolutionScope.NONE;
ResolutionScope requiresDependencyCollection() default ResolutionScope.NONE;
InstantiationStrategy instantiationStrategy() default InstantiationStrategy.PER_LOOKUP;
String executionStrategy() default "once-per-session";
boolean requiresProject() default true;
boolean requiresReports() default false;
boolean aggregator() default false;
boolean requiresDirectInvocation() default false;
boolean requiresOnline() default false;
boolean inheritByDefault() default true;
String configurator() default "";
boolean threadSafe() default false;
}
我会在后面的文章中一一解惑。