第 7 章 · Plugin 体系:从用插件到造插件

3 个互动演示帮你理解 Plugin、Convention Plugin、buildSrc vs build-logic 的差异。

📑 模板代码大消除:Convention Plugin 实战

有 5 个子模块,每个都需要:JDK 17、JUnit 5、UTF-8 编码、统一仓库。看看用 / 不用 Convention Plugin 的差距。

❌ 没用 Convention Plugin(每个模块复制粘贴)

// app/build.gradle.kts
plugins { java }
java { toolchain { languageVersion = JavaLanguageVersion.of(17) } }
repositories { mavenCentral() }
dependencies { testImplementation("org.junit.jupiter:junit-jupiter:5.10.0") }
tasks.test { useJUnitPlatform() }
tasks.withType<JavaCompile>().configureEach { options.encoding = "UTF-8" }

// lib-core/build.gradle.kts —— 完全一样
plugins { java }
java { toolchain { languageVersion = JavaLanguageVersion.of(17) } }
repositories { mavenCentral() }
dependencies { testImplementation("org.junit.jupiter:junit-jupiter:5.10.0") }
tasks.test { useJUnitPlatform() }
tasks.withType<JavaCompile>().configureEach { options.encoding = "UTF-8" }

// lib-data/build.gradle.kts —— 又一样
... (再 12 行)

// feature-billing/build.gradle.kts
... (再 12 行)

// feature-payment/build.gradle.kts
... (再 12 行)
总行数
~ 60 行
改 JDK 版本要改
5 个文件

✅ 用 Convention Plugin(buildSrc 抽出公共配置)

// buildSrc/src/main/kotlin/my-java-conventions.gradle.kts
plugins { java }
java { toolchain { languageVersion = JavaLanguageVersion.of(17) } }
repositories { mavenCentral() }
dependencies { "testImplementation"("org.junit.jupiter:junit-jupiter:5.10.0") }
tasks.named<Test>("test") { useJUnitPlatform() }
tasks.withType<JavaCompile>().configureEach { options.encoding = "UTF-8" }


// app/build.gradle.kts —— 一行
plugins { id("my-java-conventions") }

// lib-core/build.gradle.kts
plugins { id("my-java-conventions") }

// lib-data/build.gradle.kts
plugins { id("my-java-conventions") }

// feature-billing/build.gradle.kts
plugins { id("my-java-conventions") }

// feature-payment/build.gradle.kts
plugins { id("my-java-conventions") }
总行数
12 行
改 JDK 版本要改
1 个文件

💡 Convention Plugin 的本质:把"项目通用配置"抽出来 = DRY 原则在 Gradle 上的体现。当模块数 ≥ 3,强烈建议用。

📂 自定义 Plugin 放哪儿?三种位置对比

📦 buildSrc(约定目录)

my-project/ ├── buildSrc/ ← 必须叫这个名 │ ├── build.gradle.kts │ └── src/main/kotlin/ │ └── my-conv.gradle.kts └── app/ └── build.gradle.kts
  • 零配置,约定目录
  • 编译产物自动 cp 到所有子模块
  • 改任何文件 → 所有子模块重新配置
  • 大型项目慢
适合:小项目、学习阶段

🌍 独立项目 + 发布到仓库

my-plugin/ ← 独立 Git 项目 ├── build.gradle.kts └── src/main/kotlin/ └── MyPlugin.kt # 其他项目使用: plugins { id("com.example.my-plugin") version "1.0" }
  • 跨项目复用
  • 可发布到 Plugin Portal / 公司私服
  • 有版本号管理
  • 维护成本高(要发布、版本管理)
适合:通用工具插件、公司级共享插件

🎯 决策树

需要 Plugin 吗?
├── 单项目内复用?
│   ├── 学习 / 小项目 → buildSrc
│   └── 生产 / 大项目 → build-logic(推荐)
└── 跨项目复用?
    ├── 公司内部 → 独立项目 + 发到公司 Nexus
    └── 全球开源 → 独立项目 + 发到 Plugin Portal

📝 Precompiled Script vs Plugin 类

同一个目的:"给项目应用一组配置 + 加一个 task",两种写法对比。

📝 Precompiled Script (.gradle.kts 文件)

// buildSrc/src/main/kotlin/
//   my-java-conv.gradle.kts

plugins {
    java
}

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

repositories { mavenCentral() }

dependencies {
    "testImplementation"("org.junit.jupiter:junit-jupiter:5.10.0")
}

tasks.named<Test>("test") { useJUnitPlatform() }

tasks.register("hello") {
    doLast { println("Hello from script plugin!") }
}
  • 跟 build.gradle.kts 一样的写法
  • 文件名 = plugin id(去掉 .gradle.kts)
  • 不需要写 implementationClass
  • 简单快速
  • 不易测试
  • 复杂逻辑写起来啰嗦

🎯 Plugin 类 (实现 Plugin<Project>)

// build-logic/convention/src/main/kotlin/
//   com/example/JavaConvPlugin.kt

class JavaConvPlugin : Plugin<Project> {
    override fun apply(target: Project) {
        with(target) {
            pluginManager.apply("java")

            extensions.configure<JavaPluginExtension>("java") {
                toolchain.languageVersion.set(JavaLanguageVersion.of(17))
            }

            repositories.mavenCentral()

            dependencies.add("testImplementation",
                "org.junit.jupiter:junit-jupiter:5.10.0")

            tasks.withType<Test>().configureEach {
                useJUnitPlatform()
            }

            tasks.register("hello") {
                doLast { println("Hello!") }
            }
        }
    }
}

// build-logic/convention/build.gradle.kts
gradlePlugin {
    plugins {
        register("javaConv") {
            id = "my.java-conv"
            implementationClass = "com.example.JavaConvPlugin"
        }
    }
}
  • 可单元测试
  • 逻辑组织清晰
  • 可发布到 Maven 仓库
  • 支持复杂逻辑(条件、循环、共享代码)
  • 代码量稍多
  • 学习曲线稍陡
💡 实战建议:
  • 简单的"应用一堆配置" → Precompiled Script(80% 场景)
  • 有复杂逻辑、要测试、要发布 → Plugin 类
  • 两种可以共存,按需选择