【Maven】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 为骨架创建项目
- 选择创建新项目
- 选择通过 maven 创建
- 勾选 Create from archetype 通过项目骨架创建
- 选择 org.apache.maven.archetypes:maven-archetype-mojo
- 点击下一步
2.2 输入在第一步起的项目名
点击 Next
2.3 点击 Finish 完成项目创建
2.4 分析项目文件
项目结构
可以看到生成的项目就是我们最最常见的 maven 项目的结构,生成的文件也很少,一个 pom.xml 文件,一个 MyMojo 文件,简单介绍一下这两个文件
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.coder4j.study.example</groupId>
<artifactId>demo-maven-plugin</artifactId>
<packaging>maven-plugin</packaging>
<version>1.0-SNAPSHOT</version>
<name>demo-mavne-plugin Maven Mojo</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
首先生成的项目 pom.xml 中,自动依赖了两个项目,一个是 maven-plugin-api ,这个是开发 maven 插件必须依赖的核心包。另一个是单元测试时使用的 junit 包。这两个没什么要注意的,真正要注意的是这个项目的 packaging,通常我遇到的 packaging 都是 jar、war、pom,这里比较特殊是 maven-plugin。
MyMojo.java
package cn.coder4j.study.example;
/*
* Copyright 2001-2005 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
/**
* Goal which touches a timestamp file.
*
* @goal touch
*
* @phase process-sources
*/
public class MyMojo
extends AbstractMojo
{
/**
* Location of the file.
* @parameter expression="${project.build.directory}"
* @required
*/
private File outputDirectory;
public void execute()
throws MojoExecutionException
{
File f = outputDirectory;
if ( !f.exists() )
{
f.mkdirs();
}
File touch = new File( f, "touch.txt" );
FileWriter w = null;
try
{
w = new FileWriter( touch );
w.write( "touch.txt" );
}
catch ( IOException e )
{
throw new MojoExecutionException( "Error creating file " + touch, e );
}
finally
{
if ( w != null )
{
try
{
w.close();
}
catch ( IOException e )
{
// ignore
}
}
}
}
}
首先生成的类继承了 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
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.coder4j.study.example</groupId>
<artifactId>demo-maven-plugin</artifactId>
<packaging>maven-plugin</packaging>
<version>1.0-SNAPSHOT</version>
<name>demo-mavne-plugin Maven Mojo</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<version>3.5.2</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<version>3.5.2</version>
</plugin>
</plugins>
</build>
</project>
相较于默认的 pom.xml 文件,我们做了如下几个变动:
- 升级 maven-plugin-api 的插件版本到 3.5.2 。原生的 2.0 实在是太老了。
- 添加 maven-plugin-annotations 这个依赖,还记得上面说的 tag 的事吗?有了这个依赖就可以直接使用注解了
- 添加 maven-plugin-plugin 插件依赖,添加这个依赖主要是为了在 jdk1.8 能编译通过,否则会报错
3.2 DemoMojo.java
/*
*
* * *
* * * blog.coder4j.cn
* * * Copyright (C) 2016-2020 All Rights Reserved.
* *
*
*/
package cn.coder4j.study.example;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
/**
* @author buhao
* @version DemoMojo.java, v 0.1 2020-03-30 22:51 buhao
*/
@Mojo(name = "hello")
public class DemoMojo extends AbstractMojo {
@Parameter(name = "name", defaultValue = "kiwi")
private String name;
public void execute() throws MojoExecutionException, MojoFailureException {
getLog().info("hello " + name);
}
}
首先,同生成的类一样,我们的类必须继承 AbstractMojo 并实现他的 execute 方法,而 execute 方法其实就是这个插件的入口类。
示例代码中有两个很重要的注解,一个是 @Mojo ,它主要用来定义插件相关的信息相当于上面说的 @goal ,其中 name 属性用来指定这个插件名称,同 clean 类似。
另外一个重要注解 @Parameter ,则是用来指定插件运行时使用的参数,其中 name 是参数名,defaultValue 顾名思义是默认值,也就是在用户没有设置的时候使用的值。
详细的插件及作用如下:
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()
{
}
}
回到示例上了,我们这个插件作用很简单,根据配置输出 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。
好的,我们再运行一下,结果如下:
好的,大功告成。
其它
参考链接
项目源码
因为篇幅有限,无法贴完所有代码,如遇到问题可到 github 上查看源码。
关于我
本文由博客一文多发平台 OpenWrite 发布!
【Maven】maven 插件开发实战的更多相关文章
- [Maven]Maven 那点事儿
0. 前言 Jason Van Zyl,在 Java 十大风云人物排行榜上或许会看到他. 这兄弟是干嘛的? 他就是 Maven 的创始人,人们都尊称他为"Maven 他爸". 毋庸 ...
- Maven - Maven速成
Maven Maven是一个项目构建和管理工具,有助于开发者快速完成项目的配置,快速建立开发环境,从而提高开发效率. 管理项目构建(build)的生命周期(清理.编译.测试.打包.发布.部署.报告等) ...
- 【eclipse插件开发实战】Eclipse插件开发3——OSGi、RCP
Eclipse插件开发实战3--OSGi.RCP 一.OSGi 1. 什么是OSGi框架 OSGi(Open Service Gateway Initiative)框架是运行在JavaVM环境里的服务 ...
- 【eclipse插件开发实战】Eclipse插件开发2——SWT
Eclipse插件开发实战2--SWT 一.SWT简介 SWT(StandardWidget Toolkit) 标准小窗口工具箱,一开源的GUI编程框架,与AWT/Swing有相似的用处,eclips ...
- 【eclipse插件开发实战】Eclipse插件开发1——eclipse内核结构、扩展点机制
Eclipse插件开发实战1--eclipse内核结构.扩展点机制 一.前言 本系列总体介绍eclipse插件开发基本理论.插件项目结构及开发步骤,最后再给出两个插件开发实例. 总体安排结构如下: 1 ...
- spring boot插件开发实战和原理
本文转载自spring boot插件开发实战和原理 实战:编写spring boot插件 为什么要编写boot插件 因为我们在开发的时候需要提供一些共同的功能,所以我们编写个共同的jar包.开发人员在 ...
- 工作随笔——一次简单的Maven加速构建实战
注意:所有的编译.打包.部署全部是通过Jenkins完成的. 公司内部有一个项目,开始做的时候已经预计到会有很多客服端.所以开发就搞了如下的结构: fft-api # 公用的API,所有的程序都必须使 ...
- some knowledge of maven {maven实战}
maven是跨平台的,不仅是一个构建工具,也是一个可以管理依赖的工具.它最大化的消除了构件的重复,并且提供了中央仓库,能帮我们自动下载构件.------------------------------ ...
- springboot+maven+thymeleaf配置实战demo
本案例使用thymeleaf,与springboot配置使用.thymeleaf是一种模板语言,可以动态或者静态显示文本内容. 1 .项目结构 2.构建springboot项目 通过idea的new ...
随机推荐
- Redis主从 部署和配置
目录 一.主从简介 主从介绍 主从原理 二.主从部署 环境介绍 主从配置 临时主从 三.主从测试 一.主从简介 主从介绍 Redis都是主节点.每个从节点只能有一个主节点,而主节点可以同时具有多个从节 ...
- Redis学习推荐资料合集
目录 一.官方 二.书籍 三.推荐博客 一.官方 redis官网 redis中文网 redis中文命令网 redis模块 redis-github源码 redis报告 二.书籍 <Redis开发 ...
- h5文件下载
// type1 await getFile(fileUrl).then((res) => { console.log('download',res); let bFile = window.U ...
- <转>android 解析json数据格式
json数据格式解析我自己分为两种:一种是普通的,一种是带有数组形式的: 普通形式的:服务器端返回的json数据格式如下:{"userbean":{"Uid": ...
- 日期与时间函数(Excel函数集团)
此处文章均为本妖原创,供下载.学习.探讨! 文章下载源是Office365国内版1Driver,如有链接问题请联系我. 请勿用于商业!谢谢 下载地址:https://officecommunity-m ...
- C# 使用TimeSpan秒数转化为时分秒的写法
1.TimeSpan的生成方法 // 参数: // ticks: // A time period expressed in 100-nanosecond units. public TimeSpan ...
- 【LeetCode】478. Generate Random Point in a Circle 解题报告(Python & C++)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 题目地址: https://leetcode.com/problems/generate ...
- 【LeetCode】763. Partition Labels 解题报告(Python & C++)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 解题方法 日期 题目地址:https://leetcode.com/pr ...
- 【LeetCode】199. Binary Tree Right Side View 解题报告(Python)
[LeetCode]199. Binary Tree Right Side View 解题报告(Python) 标签: LeetCode 题目地址:https://leetcode.com/probl ...
- 【LeetCode】82. Remove Duplicates from Sorted List II 解题报告(Python&C++)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 题目地址:https://leetcode.com/problems/remove-du ...