持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第6天,点击查看活动详情
前言
本系列目录
- Task
- Project、Task常用API
- 文件操作
- 依赖管理
- 多模块构建
- 插件编写
- SpringBoot插件源码分析
- 过度到Kotlin
平常我们都会多多少少用到Gradle插件,比如java插件,他提供了如打包jar的功能,所以这章我们来学习下如何编写一个插件,来提高工作中繁琐的步骤。
Gradle有三个地方可以存放插件代码。
-
build.gradle
这种方式就是直接把插件的代码放在build.gradle里面,开发插件也不会采用这种方式,就不说了。
-
buildSrc
这种方式将插件的代码放在
rootProjectDir/buildSrc/src/main/{java|kotlin|groovy}
中,注意看,Gradle插件可以使用任何在JVM上运行的语言,最常见的就是java、kotlin、groovy,这种方式针的场景是插件不会对外使用,只供本项目使用。 -
独立项目
这种方式是最常见的,最终将插件打包成jar包,可以分享给其他项目使用,或者发布到maven公共仓库,而我们学习的就是这种方式。
创建插件项目
gradle本身就提供了一个插件项目模板,可以使用下面方式创建。
gradle init
复制代码
之后会让你选择项目类型,则选4,其余看自己的喜好,比如用java还是kotlin编写。
Select type of project to generate:
1: basic
2: application
3: library
4: Gradle plugin
Enter selection (default: basic) [1..4]
复制代码
之后就可以使用IDEA打开了,这个模板默认创建了一个task,只输出了一句话,具体API我们在后面说。
首先看一下build.gradle中这段配置,我们需要给插件起一个id,比如这里是test-gradle-plugin,还有要指定这个插件的实现类。
注意,plugins下的greeting是可以随便起的。
gradlePlugin {
// Define the plugin
plugins {
greeting {
id = 'test-gradle-plugin'
implementationClass = 'com.hxl.plugin.GradlePluginPlugin'
}
}
}
复制代码
配置完这些信息就可以打包了,直接执行jar任务即可,打包出来就是一个插件包,位于目录build/libs
下。
引入
打包成jar后接下来看下如何在其他项目中引入,需要在settings.gradle中配置,flatDir告诉插件所在的目录,classpath告诉插件的名字,上面我们打包出来的插件默认名就是plugin.jar,但必须在前面加:
。
# settings.gradle
pluginManagement {
resolutionStrategy {
eachPlugin {
println(requested.id)
}
}
buildscript {
repositories {
flatDir dirs: "/home/HouXinLin/project/gradle-plugin/plugin/build/libs/"
}
dependencies {
classpath(":plugin")
}
}
}
复制代码
接下来需要在build.gradle中引入这个插件,如下,这里的id就是在插件项目中配置的,如果你细心的话,可以查看打包出来的jar包,有这样一个路径/META-INF/gradle-plugins/test-gradle-plugin.properties
,这也是gradle查找插件的一个约定,改插件名也可以从这里把这个文件名改了。
plugins {
id 'java'
id("test-gradle-plugin")
}
复制代码
由于我们在插件项目中创建了一个任务greeting,所以在这里可以直接执行。
$ ./gradlew greeting
Hello from plugin 'gradle.plugin.greeting'
复制代码
java-gradle-plugin 插件
他是用来开发gradle插件的一个插件,上面方式创建的插件项目默认就会引入这个插件。
插件入口Plugin
编写插件时需要实现一个Plugin接口,Gradle会实例化他并调用他的Plugin.apply()方法,参数是一个Project对象,Project对象我们在前面已经说过了,是Gradle API开始的地方,可以通过他创建任务,获取项目构建路径等,比如下面,是创建一个greeting的任务,并输出一句话。
public class GradlePluginPlugin implements Plugin<Project> {
public void apply(Project project) {
project.getTasks().register("greeting", task -> {
task.doLast(s -> System.out.println("Hello from plugin 'gradle.plugin.greeting'"));
});
}
}
复制代码
扩展
插件可能还需要一些配置选项,用来为插件提供一些配置,Project有一个关联的ExtensionContainer对象来做到这一点。
由于上面示例中输出的字符是死的,所以下面我们通过这个特性把他改成活得。
如下,我们创建一个名为testExtension的扩展,类型是TestPluginExtension。
class TestPluginExtension {
String message = "default";
}
public class GradlePluginPlugin implements Plugin<Project> {
public void apply(Project project) {
TestPluginExtension testPluginExtension= project.getExtensions().create("testExtension", TestPluginExtension.class);
project.getTasks().register("greeting", task -> {
task.doLast(s -> System.out.println(testPluginExtension.message));
});
}
}
复制代码
然后在项目中就可以使用testExtension来获取或设置当前message的值。
plugins {
id("test-gradle-plugin")
}
testExtension.message="message"
复制代码
当我们执行greeting任务时,如果没有配置message,那么默认就打印default。
还可以这样配置。
testExtension{
message="message"
}
复制代码
当然还可以嵌套,其中ObjectFactory是用于创建各种model的工厂,他的实例可以通过构造方法或方法上标有javax.inject.Inject
注解注入获得,它也可以通过Project.getObjects()
获取。
ObjectFactory这种方式是在参考其他文章所知道的,但不知这样做的意义在哪里,当然也可以不使用他,直接new。
class TestPluginExtension {
String message = "default";
User user;
@Inject
public TestPluginExtension(ObjectFactory objectFactory) {
this.user = objectFactory.newInstance(User.class);
}
void user(Action<? super User> action) {
action.execute(user);
}
}
class User {
String name = "name";
public User() {
}
}
public class GradlePluginPlugin implements Plugin<Project> {
public void apply(Project project) {
TestPluginExtension testPluginExtension = project.getExtensions().create("testExtension", TestPluginExtension.class);
project.getTasks().register("greeting", task -> {
task.doLast(s -> System.out.println(testPluginExtension.user.name + " " + testPluginExtension.message));
});
}
}
复制代码
plugins {
id("test-gradle-plugin")
}
testExtension{
message ='message'
user{
name="111"
}
}
复制代码
这样在执行任务时,输出如下。
> Task :greeting
111 message
复制代码
独立task类
如果task的逻辑比较多,可以单独使用一个类。如下, 标有@TaskAction注解的方法告诉Gradle这是任务执行的方法。
class MyTask extends DefaultTask {
private Project project;
@Inject
public MyTask(Project project) {
this.project = project;
}
@TaskAction
public void action() {
System.out.println(this.project.getRootDir());
}
}
public class GradlePluginPlugin implements Plugin<Project> {
public void apply(Project project) {
project.getTasks().register("greeting", MyTask.class,project);
}
}
复制代码
设置task组
默认创建的task,都会在other中,如果想指定一个组,可以通过setGroup设置组名。
public class GradlePluginPlugin implements Plugin<Project> {
public void apply(Project project) {
TestPluginExtension testPluginExtension = project.getExtensions().create("testExtension", TestPluginExtension.class);
TaskProvider<Task> greeting = project.getTasks().register("greeting", task -> {
task.doLast(s -> System.out.println(testPluginExtension.user.name + " " + testPluginExtension.message));
});
greeting.get().setGroup("testGroup");
}
}
复制代码
同样如果在脚本中,也可以指定一个组。
task taskA{
group("testgroup")
doLast {
println("a")
}
}
复制代码
追加现有任务
如上,我们的greeting任务是在jar包中的,如果我们想在greeting执行结束后,在执行一个我们自己的任务,可以这样做。
tasks.greeting{
doLast {
println("test")
}
}
tasks.getByName("greeting"){
doLast {
println("getByName")
}
}
tasks.named("greeting"){
it.doLast {
println("named")
}
}
复制代码
tasks是TaskContainer的实例,他责管理所有task,我们可以使用他提供的某种方法来定位一个现有任务,例如TaskCollection.getByName(String)
。
doLast、doFirst会追加到自身任务队列的最后、最前,并依次执行,而像group等会进行覆盖。