12、自定义Plugin:创建自定义插件(脚本插件、buildSrc、独立项目)、扩展Extension、发布插件
说实话,Gradle 的插件机制是我最喜欢它的地方之一。你想想看,项目里那些重复的配置——打 Jar 包、代码检查、版本管理——如果能封装成一个插件,一行代码就能复用,那得多爽?
这一章我们就来聊聊怎么自己写插件。我会从最简单的脚本插件开始,一步步带你走到发布独立插件的完整流程。嗯,这里要注意,插件不光是写代码,怎么设计扩展(Extension)让用户能配置,才是真正体现功力的地方。
- 三种插件形态:脚本插件、buildSrc 插件、独立项目插件
- Extension 的设计与实现
- 插件的发布与使用
12.1 脚本插件:最轻量的方式
脚本插件说白了就是一个 .gradle 文件。你把它抽出来,然后其他地方用 apply from 引入。我在小项目里经常这么干,省事。
举个例子,假设我们有个 common.gradle 文件:
// common.gradle
ext {
projectVersion = '1.0.0'
javaVersion = JavaVersion.VERSION_17
}
tasks.register('hello') {
doLast {
println "Hello from common script! Version: $projectVersion"
}
}
然后在 build.gradle 里引入:
// build.gradle
apply from: 'common.gradle'
tasks.named('hello').configure {
dependsOn 'build'
}
这种方式的好处是简单直接。但缺点也很明显——没有类型安全,IDE 没法给你代码提示。我曾经在一个 20 多个模块的项目里用脚本插件管理公共配置,后来改得多了,每次都要全局搜索替换,那叫一个痛苦。
12.2 buildSrc 插件:项目内复用利器
buildSrc 是 Gradle 的一个特殊目录。放在这个目录下的代码,会自动编译并加入到项目的 classpath 中。说白了,就是项目内所有模块都能直接用。
目录结构长这样:
project-root/
├── buildSrc/
│ ├── build.gradle.kts
│ └── src/
│ └── main/
│ └── java/
│ └── com/
│ └── example/
│ └── MyPlugin.groovy
├── app/
│ └── build.gradle
└── settings.gradle
buildSrc 的 build.gradle.kts 很简单:
// buildSrc/build.gradle.kts
plugins {
`java-gradle-plugin`
}
repositories {
mavenCentral()
}
然后写插件类:
// buildSrc/src/main/java/com/example/MyPlugin.groovy
package com.example
import org.gradle.api.Plugin
import org.gradle.api.Project
class MyPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
project.task('myTask') {
doLast {
println "Hello from buildSrc plugin!"
}
}
}
}
在 app/build.gradle 里使用:
plugins {
id 'com.example.my-plugin'
}
注意,buildSrc 的插件 ID 默认是类的全限定名。你也可以在 buildSrc 的 build.gradle 里配置 gradlePlugin 块来指定 ID。
12.3 独立项目插件:真正的复用
如果你想把插件分享给多个项目,甚至发布到公共仓库,那就得用独立项目的方式。说白了,就是单独建一个 Gradle 项目,专门用来开发插件。
目录结构:
my-plugin/
├── build.gradle.kts
├── settings.gradle.kts
└── src/
└── main/
└── java/
└── com/
└── example/
└── MyPlugin.groovy
build.gradle.kts:
plugins {
`java-gradle-plugin`
`maven-publish`
}
group = 'com.example'
version = '1.0.0'
gradlePlugin {
plugins {
register("myPlugin") {
id = "com.example.my-plugin"
implementationClass = "com.example.MyPlugin"
}
}
}
publishing {
publications {
create<MavenPublication>("pluginMaven") {
from(components["java"])
}
}
repositories {
maven {
url = uri("https://your-repo.com/releases")
}
}
}
发布命令:
./gradlew publish
使用方在 settings.gradle.kts 里添加仓库:
pluginManagement {
repositories {
maven {
url = uri("https://your-repo.com/releases")
}
gradlePluginPortal()
}
}
然后在 build.gradle 里:
plugins {
id 'com.example.my-plugin' version '1.0.0'
}
12.4 扩展 Extension:让插件可配置
插件如果写死了,那跟脚本插件有什么区别?真正的插件应该让用户能自定义行为。这就需要 Extension 了。
说白了,Extension 就是一个数据类,用户可以在 build.gradle 里配置它,插件在运行时读取这些配置。
先定义 Extension 类:
// src/main/java/com/example/MyExtension.groovy
package com.example
class MyExtension {
String message = "Default message"
boolean verbose = false
}
然后在插件里注册它:
class MyPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
// 注册扩展
def extension = project.extensions.create('myConfig', MyExtension)
project.task('myTask') {
doLast {
println "Message: ${extension.message}"
if (extension.verbose) {
println "Verbose mode enabled"
}
}
}
}
}
用户使用:
myConfig {
message = "Hello from user config!"
verbose = true
}
是不是很直观?我个人习惯把 Extension 设计成嵌套的,比如支持多个子配置块,这样用户用起来更灵活。
Property<T> 类型,这样支持延迟赋值和任务依赖。Gradle 官方推荐的做法。
12.5 知识体系图
下面这张图帮你理清三种插件形态的关系和适用场景:
12.6 发布插件到 Gradle Plugin Portal
如果你想把自己的插件分享给全世界,可以发布到 Gradle Plugin Portal。流程也不复杂:
- 在 plugins.gradle.org 注册账号
- 获取 API Key
- 在 build.gradle 里配置发布插件
plugins {
id 'com.gradle.plugin-publish' version '1.2.1'
}
pluginBundle {
website = 'https://github.com/your/project'
vcsUrl = 'https://github.com/your/project.git'
description = 'A plugin that does awesome things'
tags = ['gradle', 'plugin', 'example']
plugins {
myPlugin {
displayName = 'My Awesome Plugin'
}
}
}
然后执行:
./gradlew publishPlugins
发布成功后,全世界的人都能通过 plugins DSL 使用你的插件了。
12.7 避坑指南
- 我曾经犯过的错:在插件里直接修改 Project 的默认配置,导致用户无法覆盖。后来我改成用 Extension 让用户显式配置,问题就解决了。
- 另一个坑:插件里用了全局变量,导致多个项目同时构建时出现冲突。记住,插件应该是无状态的,每次 apply 都是独立的。
- 还有一个:发布插件时忘记配置最小 Gradle 版本,用户用低版本 Gradle 构建直接报错。现在我会在插件里加一个
project.gradleVersion检查。
好了,自定义插件这块就聊这么多。从脚本插件到独立项目,再到 Extension 的设计,每一步都有它的适用场景。我个人建议,只要项目超过 3 个模块,就值得用 buildSrc 或独立插件来管理公共逻辑。别等到项目乱成一锅粥了再重构,那时候成本就高了。