Gradle 自定义插件
使用版本 5.6.2
插件被用来封装构建逻辑和一些通用配置。将可重复使用的构建逻辑和默认约定封装到插件里,以便于其他项目使用。
你可以使用你喜欢的语言开发插件,但是最终是要编译成字节码在 jvm 运行的。
gradle 有两种插件,脚本插件和二进制插件。
关于插件的介绍,可以参考我的另一篇文章 gradle 插件
这里讲的自定义插件是二进制插件,二进制插件可以打包发布,有利于分享。
可以在三个地方定义插件
- 在脚本里
- 在 buildsrc 下
- 在单独的项目里
三个地方的插件的用途目的不同。
在脚本里的插件
其他项目无法使用,只能在本脚本里使用。
在 buildsrc 下
在项目的 buildsrc 目录下的插件,这个项目里的所有(子)项目都可以使用。
在单独的项目里
你可以为你的插件创建一个项目,这个项目可以打包发布 jar,提供给其他任何项目使用。
创建一个插件
建议使用静态语言,例如 java ,kotlin,开发工具建议使用 intellij idea 。
一个插件就是个实现了 plugin
当插件被应用到项目时,gradle 会实例化这个插件并调用 plugin.apply() 方法,并将这个项目的实例当做参数传递进去。插件就可以对这个项目进行各种配置了。 customplugin.java 前面说到可以在三个地方创建插件,现在来一一实现下。 可以在 build.gradle 脚本里任意地方定义。 build.gradle 在 gradle 窗口就可以看到应用插件后的添加的任务 双击任务或者命令行输入都可以执行 hello 任务 这里使用的是 groovy 。 在这个目录下创建项目会被 gradle 自动识别的。 结构如下 $projectdir/buildsrc/src/main/groovy 这里做简单的示范: 在插件里为 jar 任务添加一个操作:生成记录文件 jarlogplugin.groovy 上面使用了一个扩展来接收参数, 普通的对象就可以,例如 logextension.groovy 扩展在这里就是用来为插件配置 dsl 用的。 插件可以使用 dsl 接收参数,在插件或者任务里直接通过 project 实例访问即可。 插件创建完成后,在项目的里就可以使用了。 现在可以使用类名应用插件了。 build.gradle 插件应用成功后就可以使用 dsl 为插件配置参数。 配置记录文件地址: build.gradle 例如 src / main / resources / meta-inf / gradle-plugins / com.github.skymxc.sample.properties 然后就可以使用插件 id 了 关于插件 id 的规范: 关于 groovy 的语法,可以参考 groovy 语法。 这次仍然是使用 groovy 语言。 这里的插件项目其实就是一个 groovy 项目,当然了你如果使用 java 语言就是一个 java 工程。 创建一个工程 创建出来的项目就是这样子,标准的 groovy 工程目录 更改 build.gradle 脚本,配置项目 最后就是这样子 创建两个插件: 一个是上面创建的那个,就不重复粘贴了。 另一个插件 greet,配置一个任务,简单的输出一句话。 hello.groovy 插件 id 的配置是跟上面一样的。 执行 maven-publish 的 publish 任务,将插件发布到指定仓库。 发布成功后的仓库 插件创建完成了,也发布了,下面就是使用这个插件了。 这里对插件的使用就简单介绍一下,详细的可以查看之前的这篇介绍:gradle 插件 我分别在两个 java 项目里使用了插件: lib_2/ build.gradle 使用 类名的方式 lib_1/ build.gradle 使用 id 的方式 应用插件后的 gradle 视图,可以看到已经添加的任务。 像上面一样创建一个项目,不过这次是一个 java 项目,然后应用这个插件。 java-gradle-plugin 可以减少重复代码,它自动的应用 java 插件,添加 gradleapi() 依赖。 使用 gradleplugin {} 配置块可以配置开发的每一个插件,不用手动创建对应的属性文件了。 插件会在 jar 文件里自动生成对应的 meta-inf 目录。 配合 maven-publish 可以为每个插件创建对应的发布任务。 在发布时也会为每个插件发布对应的 “插件标记工件” 。 关于 插件标记工件这里插一下: 每个 maven 工件都是由三部分标识的 平常我们添加依赖的这样的: 而我们的插件是通过 id 应用的,怎么通过 id 找到对应的工件呢,这就有了“插件标记工件”。 即: 举个上面的例子:com.github.skymxc.greet 插件对应的工件就是: com.github.skymxc.greet:com.github.skymxc.greet.gradle.plugin:1.0.0 部分代码: 简单介绍一下 maven-publish 的发布任务 generatepomfilefor 为名字为 pubname 的的发布创建一个 pom 文件,填充已知的元数据,例如项目名称,项目版本和依赖项。pom文件的默认位置是build / publications / $ pubname / pom-default.xml。 publish 将 pubname 发布 发布到名为 reponame 的仓库。 publish 将 pubname 发布以及本地发布的 pom 文件和其他元数据复制到本地maven缓存中 publish 依赖于:所有的 publish publishtomavenlocal 依赖于:所有的 publish 将所有定义的发布(包括它们的元数据(pom文件等))复制到本地maven缓存。 这张图列出了为每个插件生成的对应的任务。 执行发布任务 publish 后可以在对应的仓库查看 应用插件后就可以在 gradle 的窗口看到对应的任务了。 然后可以根据需要配置 dsl 。 和插件的交互就是通过 dsl 配置块进行的。 那怎么为插件配置 dsl 呢,答案是随便一个普通类都可以的。 通过 gradle 的 api 可以将一个普通的类添加为 project 的扩展,即 project 的属性。 举个例子,插件里的任务需要两个参数:文件地址,文件名字,就要通过 dsl 配置的方式解决。 jarlogextension.java 一个普通的类,有两个属性,分别是 name , path 在插件里将这个类添加为项目的扩展。 应用插件后就可以在脚本里使用这个 dsl 配置了。 build.gradle 接下来就是在插件或者任务里获取 dsl 配置的参数了。 可以通过名字或者类型获取到这个扩展对象。 在我们日常的使用中,嵌套 dsl 很常见,那怎么实现的呢。 现在我来实现下: 首先是创建里面的嵌套对象,需要注意的是要为 dsl 配置对应的方法。 userdata.java 然后是外层的 dsl 对应的类,因为有 dsl 嵌套,所以要使用闭包 最后就是添加到项目的扩展了,和前面一样 在任务中的获取也是一样的 再看一个 dsl 配置,这种集合嵌套也经常见到,下面也来简单实现一下。 这种配置是配合 nameddomainobjectcontainer 实现的,它接收一个类,这个类必须有一个包含 name 参数的构造方法。 fruit.java 配置一个 factory fruitfactory.java 接着就是创建 nameddomainobjectcontainer 对象并添加到 project 。 greetplugin.java 现在应用这个插件就可以在脚本里使用上述的 dsl 配置了。 最后是 dsl 配置的接收了 关于自定义插件的相关介绍就这些了,更详细的文档可以查看 gradle 用户手册 这篇文章的源码已经放在 github 上:gradlepractice end// 定义一个插件
class customplugin implements plugin<project>{
@override
void apply(project target) {
// do something
}
}
在脚本里创建一个插件
// 定义一个插件
class customplugin implements plugin<project>{
@override
void apply(project target) {
//添加一个任务
target.task('hello', group: 'util') {
dolast {
logger.quiet("hello plugin.")
}
}
}
}
//直接在脚本里应用
apply plugin:customplugin
gradle hello
在项目的 buildsrc 目录下创建项目
/**
* 输出 生成记录到指定文件
*/
class jarlogplugin implements plugin<project> {
@override
void apply(project target) {
//增加一个扩展配置用来接收参数
target.extensions.create("log", logextension)
//添加一个任务
target.task(type: jar,group:'util','jarwithlog',{
dolast {
//使用配置
def file = target.log.outputpath;
if (file==null){
file = new file(target.projectdir,"/log/jarlog.txt").getpath()
}
println "存储目录是 ${file}"
def content = "${getarchivefilename().get()}---${getnow()}\n"
writefile(file,content)
}
})
//为 jar 任务添加一个 操作,
target.tasks.jar.dolast {
println "当前时间是 ${getnow()},打了一个 jar-> ${version}"
//存到指定文件记录
def file = new file(target.projectdir,"/log/jarlog.txt");
def content = "${version}---${getnow()}\n"
writefile(file.getabsolutepath(),content)
}
}
def string getnow(){
def dateformat = new simpledateformat("yyyy-mm-dd hh:mm:ss.sss");
return dateformat.format(calendar.getinstance().gettime());
}
def void writefile(string path,string content){
def file = new file(path);
if (!file.exists()){
if (!file.getparentfile().exists()){
file.getparentfile().mkdirs();
}
file.createnewfile();
}
filewriter writer = new filewriter(file.getabsolutepath(),true);
bufferedwriter bufferedwriter = new bufferedwriter(writer);
bufferedwriter.write(content);
bufferedwriter.close();
}
}
配置 dsl
class logextension {
string outputpath;
}
//为 项目添加了一个 logextension 类型的属性 名字是 log
project.extensions.create("log", logextension)
def file = project.log.outputpath;
import com.github.skymxc.jarlogplugin
apply plugin: jarlogplugin
log {
outputpath rootproject.projectdir.getpath()+"\\record\\jar.txt"
}
为插件创建 id
implementation-class= com.github.skymxc.jarlogplugin
plugins {
id 'com.github.skymxc.sample'
}
在单独的项目里创建插件
plugins {
id 'groovy'
id 'maven-publish'
}
group 'com.github.skymxc'
version '1.0.0'
sourcecompatibility = 1.8
repositories {
mavencentral()
}
//使用 groovy 和 gradle 依赖
dependencies {
compile gradleapi()
compile localgroovy()
}
publishing {
repositories {
maven {
name 'local'
url 'file://e:/libs/localmaven'
}
}
publications {
maven(mavenpublication) {
groupid = 'com.github.skymxc'
artifactid = 'plugin'
version = '1.0.0'
from components.java
}
}
}
class greet implements plugin<project> {
@override
void apply(project target) {
target.extensions.create("hello", hello)
target.task("hello") {
dolast {
println "message -> ${target.hello.message}"
}
}
}
}
class hello {
string message
}
buildscript {
repositories {
maven {
url 'file://e:/libs/localmaven'
}
}
dependencies {
classpath 'com.github.skymxc:plugin:1.0.2'
}
}
······
apply plugin:'com.github.skymxc.greet'
hello{
message '使用了 com.github.skymxc.greet 插件'
}
······
plugins {
id 'java'
id 'com.github.skymxc.jarlog'
}
······
logconfigure {
outputpath rootproject.projectdir.getpath()+"\\record\\jar.txt"
}
使用 java-gradle-plugin 开发插件
plugins {
id 'java-gradle-plugin'
}
gradleplugin {
plugins {
greetplugin {
id = 'com.github.skymxc.greet'
implementationclass = 'com.github.skymxc.greetplugin'
}
jarwithlogplugin {
id = 'com.github.skymxc.jar-log'
implementationclass = 'com.github.skymxc.jarwithlogplugin'
}
}
}
插件标记工件
implementation 'groupid:artifactid:version'
应用插件时会把 id 映射成这样:plugin.id: plugin.id.gradle.plugin:plugin.version
plugins {
id 'java-gradle-plugin'
id 'maven-publish'
}
group 'com.github.skymxc'
version '1.0.0'
gradleplugin {
plugins {
greetplugin {
id = 'com.github.skymxc.greet'
implementationclass = 'com.github.skymxc.greetplugin'
}
jarwithlogplugin {
id = 'com.github.skymxc.jar-log'
implementationclass = 'com.github.skymxc.jarwithlogplugin'
}
}
}
publishing {
repositories {
maven {
name 'local'
url 'file://e:/libs/localmaven'
}
}
}
maven-publish 的任务
${pubname}
publication${pubname}
publicationto${reponame}
repository
如果仓库定义没有明确的名称,则 reponame 默认为 “ maven”。${pubname}
publicationtomavenlocal
(通常为$user_home / .m2 / repository)。${pubname}
publicationto${reponame}
repository 任务
将所有定义的发布发布到所有定义的仓库的聚合任务。不包括复制到本地 maven 缓存的任务。${pubname}
publicationtomavenlocal 任务发布插件后的使用
pluginmanagement {
repositories {
maven {
url 'file://e:/libs/localmaven'
}
}
}
plugins {
id 'java'
id 'com.github.skymxc.greet' version '1.0.13'
id 'com.github.skymxc.jar-log' version '1.0.0'
}
为插件配置 dsl
package com.github.skymxc.extension;
public class jarlogextension {
private string name;
private string path;
//省略 setter/getter
}
public class jarwithlogplugin implements plugin<project> {
@override
public void apply(project target) {
//添加扩展
target.getextensions().add("jarlog", jarlogextension.class);
//创建任务
target.gettasks().create("jarwithlog", jarwithlogtask.class);
}
}
······
/**
* 为 jarwithlog 配置的 dsl
*/
jarlog {
path getbuilddir().path+"/libs"
name "record.txt"
}
······
public class jarwithlogtask extends jar {
@taskaction
private void writelog() throws ioexception {
//获取到配置
jarlogextension extension = getproject().getextensions().getbytype(jarlogextension.class);
file file = new file(extension.getpath(),extension.getname());
string s = file.getabsolutepath();
string content = getnow()+" --- "+getarchivefilename().get();
system.out.println("path --> "+s);
writefile(s,content);
}
}
嵌套 dsl
hello {
message '使用 pluginmanagement 管理插件'
user {
name 'mxc'
age 18
}
}
package com.github.skymxc.extension;
/**
* 为了实践嵌套 dsl 建的
*/
public class userdata {
private string name;
private int age;
public string getname() {
return name;
}
/**
* 注意此方法 没有 set
* @param name
*/
public void name(string name) {
this.name = name;
}
public int getage() {
return age;
}
public void age(int age) {
this.age = age;
}
}
package com.github.skymxc.extension;
import org.gradle.api.action;
/**
* 为 hellotask 创建的扩展,用于接收配置参数
*/
public class helloextension {
private string message;
private final userdata user = new userdata();
/**
* 注意此方法没有 set
* @param action
*/
public void user(action<? super userdata> action) {
action.execute(user);
}
// 省略其他 getter/setter
}
public class greetplugin implements plugin<project> {
@override
public void apply(project target) {
target.getextensions().create("hello", helloextension.class);
target.gettasks().create("hello", hellotask.class);
}
}
helloextension hello = getproject().getextensions().getbytype(helloextension.class);
userdata user = hello.getuser();
集合对象
fruits {
apple {
color '红色'
}
grape {
color '紫红色'
}
banana {
color '黄色'
}
orange {
color '屎黄色'
}
}
/**
* 必须有一个 name 属性,并且有一个 name 参数的构造函数
*/
public class fruit {
private string name;
private string color;
public fruit(string name) {
this.name = name;
}
public void color(string color){
setcolor(color);
}
//省略 setter/getter
}
import org.gradle.api.nameddomainobjectfactory;
import org.gradle.internal.reflect.instantiator;
public class fruitfactory implements nameddomainobjectfactory<fruit> {
private instantiator instantiator;
public fruitfactory(instantiator instantiator) {
this.instantiator = instantiator;
}
@override
public fruit create(string name) {
return instantiator.newinstance(fruit.class, name);
}
}
public class greetplugin implements plugin<project> {
@override
public void apply(project target) {
instantiator instantiator = ((defaultgradle)target.getgradle()).getservices().get(instantiator.class);
nameddomainobjectcontainer<fruit> fruits = target.container(fruit.class,new fruitfactory(instantiator));
target.getextensions().add("fruits",fruits);
target.gettasks().create("printlnfruits", showfruittask.class);
}
}
public class showfruittask extends defaulttask {
@taskaction
public void show(){
nameddomainobjectcontainer<fruit> fruits = (nameddomainobjectcontainer<fruit>) getproject().getextensions().getbyname("fruits");
fruits.foreach(fruit -> {
string format = string.format("name: %s , color: %s", fruit.getname(), fruit.getcolor());
getlogger().quiet("fruit : {}",format);
});
}
}
资料
下一篇: HTML select下拉列表标签