14、自定义插件开发(一):创建插件项目、实现Plugin接口、注册Task、发布到本地仓库

说实话,Gradle 的插件机制是我最喜欢它的地方之一。你想想看,如果每次都要在几十个模块里重复写同样的配置,那得多痛苦?我早期带团队的时候,就吃过这个亏——项目一多,build.gradle 文件里全是复制粘贴的代码,改个版本号得全局搜索替换,那叫一个酸爽。

后来我下定决心,把那些重复的逻辑抽成自定义插件。嗯,从那以后,维护成本直线下降。今天我们就来走一遍自定义插件开发的全流程,从零开始创建一个插件项目,实现 Plugin 接口,注册 Task,最后发布到本地仓库。

为什么需要自定义插件?

说白了,Gradle 插件就是一段可复用的构建逻辑。你可以把它理解成一个「构建工具箱」,里面装好了各种 Task、配置、扩展点。别人用的时候,只需要 apply 一下,就能拿到整套能力。

我在项目中遇到过最典型的场景:公司内部有统一的代码规范检查、统一的版本号管理、统一的发布流程。如果每个子项目都自己写一遍,不仅累,还容易出错。抽成插件后,一行 apply plugin: 'com.company.standard' 就搞定了。

核心价值:插件 = 封装 + 复用 + 标准化。一次开发,到处使用。

第一步:创建插件项目结构

Gradle 插件本质上是一个 JVM 项目,你可以用 Java、Groovy 或 Kotlin 来写。我个人习惯用 Java,因为团队里 Java 开发者最多,上手没门槛。

先创建一个标准的 Gradle 项目,目录结构如下:

my-plugin/
├── build.gradle
├── settings.gradle
├── src/
│   └── main/
│       ├── java/
│       │   └── com/
│       │       └── example/
│       │           └── MyPlugin.java
│       └── resources/
│           └── META-INF/
│               └── gradle-plugins/
│                   └── com.example.myplugin.properties

注意那个 META-INF/gradle-plugins/ 目录下的 properties 文件。这个文件是 Gradle 识别插件的关键,文件名就是你的插件 ID。比如 com.example.myplugin.properties,内容就一行:

implementation-class=com.example.MyPlugin

这行配置告诉 Gradle:当用户 apply 插件 com.example.myplugin 时,实际执行的是 com.example.MyPlugin 这个类。

注意:properties 文件的路径和文件名必须严格遵循 META-INF/gradle-plugins/{插件ID}.properties 的格式。我曾经因为少写了一个字母,导致插件死活加载不上,排查了半天才发现是文件名拼错了。

第二步:编写 build.gradle

插件项目本身也需要构建配置。我们需要引入 Gradle 的插件开发 API:

plugins {
    id 'java-gradle-plugin'
    id 'maven-publish'
}

group = 'com.example'
version = '1.0.0'

repositories {
    mavenCentral()
}

dependencies {
    implementation gradleApi()
    // 如果你需要其他依赖,比如解析 JSON,可以加在这里
    // implementation 'com.google.code.gson:gson:2.10.1'
}

java-gradle-plugin 这个插件会自动帮我们生成一些必要的配置,比如自动注册插件描述文件。而 maven-publish 则是用来发布插件的,后面会用到。

第三步:实现 Plugin 接口

现在来写核心代码。实现 org.gradle.api.Plugin 接口,只需要重写一个 apply 方法:

package com.example;

import org.gradle.api.Plugin;
import org.gradle.api.Project;

public class MyPlugin implements Plugin<Project> {

    @Override
    public void apply(Project project) {
        System.out.println("Hello from MyPlugin! 项目名称: " + project.getName());

        // 在这里注册 Task、添加扩展、配置依赖等
        project.getTasks().register("myTask", MyTask.class, task -> {
            task.setDescription("这是一个自定义 Task");
            task.setGroup("MyPlugin");
        });
    }
}

你看,apply 方法会在插件被应用时自动调用。参数 Project 就是当前应用插件的项目对象,通过它可以访问项目的所有配置和 Task。

这里我注册了一个名为 myTask 的 Task,类型是 MyTask。接下来我们创建这个 Task 类。

第四步:创建自定义 Task

Task 类需要继承 DefaultTask,并用 @TaskAction 注解标记要执行的方法:

package com.example;

import org.gradle.api.DefaultTask;
import org.gradle.api.tasks.TaskAction;

public class MyTask extends DefaultTask {

    @TaskAction
    public void doSomething() {
        System.out.println("执行 myTask...");
        System.out.println("当前项目: " + getProject().getName());
        // 这里可以写任何构建逻辑
        // 比如复制文件、执行命令、生成报告等
    }
}

嗯,这里要注意:Task 类里的属性可以用 @Input@Output 等注解来声明输入输出,这样 Gradle 就能做增量构建。不过那是下一章的内容,今天我们先跑通基本流程。

第五步:发布到本地仓库

插件写好了,怎么给别人用?最简单的方式就是发布到本地 Maven 仓库。在 build.gradle 中配置发布信息:

publishing {
    publications {
        mavenJava(MavenPublication) {
            from components.java
        }
    }
    repositories {
        maven {
            // 发布到本地仓库,默认路径是 ~/.m2/repository
            url = uri(layout.buildDirectory.dir("repo"))
            // 也可以发布到本地文件系统的其他位置
            // url = uri("file:///Users/me/local-repo")
        }
    }
}

然后执行发布命令:

./gradlew publish

执行成功后,你会在 build/repo 目录下看到生成的 jar 包和 pom 文件。结构大概是这样的:

build/repo/
└── com/
    └── example/
        └── my-plugin/
            └── 1.0.0/
                ├── my-plugin-1.0.0.jar
                ├── my-plugin-1.0.0.pom
                └── ...

小技巧:如果你想让其他项目直接引用本地仓库的插件,可以在其他项目的 settings.gradlebuild.gradle 中添加仓库地址:

pluginManagement {
    repositories {
        maven {
            url = uri("file:///path/to/my-plugin/build/repo")
        }
        gradlePluginPortal()
        mavenCentral()
    }
}

第六步:在其他项目中使用插件

发布完成后,就可以在其他项目中应用这个插件了。在目标项目的 build.gradle 中:

plugins {
    id 'com.example.myplugin' version '1.0.0'
}

然后执行 ./gradlew myTask,就能看到控制台输出:

Hello from MyPlugin! 项目名称: my-app
执行 myTask...
当前项目: my-app

看到这个输出,说明插件已经成功工作了。

核心流程总结

整个开发流程可以用下面这张图来概括:

自定义插件开发核心流程 1. 创建插件项目 标准 Gradle 项目结构 2. 实现 Plugin 接口 重写 apply 方法 3. 注册 Task @TaskAction 注解 4. 配置发布 maven-publish 插件 5. 发布到本地仓库 ./gradlew publish 6. 在其他项目引用 plugins { id '...' } 关键文件:META-INF/gradle-plugins/{插件ID}.properties → implementation-class=全限定类名 发布命令:./gradlew publish → 生成 jar + pom 到本地仓库

常见问题与避坑指南

  • 插件 ID 冲突:插件 ID 最好是反向域名格式,比如 com.company.plugin。我曾经见过有人用 myplugin 这种简单名字,结果跟其他库冲突了,排查起来很麻烦。
  • properties 文件位置不对:必须放在 src/main/resources/META-INF/gradle-plugins/ 下,文件名必须和插件 ID 完全一致。少一个字母都不行。
  • 发布后找不到插件:检查一下其他项目的 pluginManagement 仓库配置是否指向了正确的本地路径。我习惯把本地仓库路径写成一个变量,方便切换。
  • Task 不执行:确认 Task 类上有 @TaskAction 注解,并且方法签名正确(无参数,返回 void)。

核心要点回顾:

  • 插件项目需要 java-gradle-pluginmaven-publish 两个插件
  • 实现 Plugin<Project> 接口,在 apply 方法中注册 Task
  • Task 类继承 DefaultTask,用 @TaskAction 标记执行方法
  • 通过 META-INF/gradle-plugins/ 下的 properties 文件声明插件实现类
  • 使用 ./gradlew publish 发布到本地仓库

好了,到这里你已经掌握了自定义插件开发的基本功。下一章我们会深入插件的扩展点,让用户可以在 build.gradle 中配置参数,实现更灵活的构建逻辑。不过那是后话了,今天先把基础打牢。


公众号:蓝海资料掘金营,微信deep3321