Gradle 8+反向继承配置:子模块向父项目同步方案咨询
1. 用自定义Gradle插件封装通用构建逻辑
别再复制粘贴重复配置了,搞个独立的build-conventions模块,把所有通用配置(Java版本、编译选项、JUnit配置、自定义函数等)封装成Gradle插件——这是Gradle里复用配置最靠谱的方式,比零散脚本插件灵活,还能版本化发布,应用和模块直接引用就能继承全部配置。
步骤1:搭建build-conventions模块结构
在项目根目录下新建build-conventions文件夹,结构如下:
build-conventions/ ├── build.gradle.kts ├── settings.gradle.kts └── src/ └── main/ └── kotlin/ └── com/yourteam/conventions/ ├── JavaConventionsPlugin.kt └── ApplicationConventionsPlugin.kt
步骤2:编写插件实现通用配置
比如JavaConventionsPlugin.kt搞定Java基础配置:
package com.yourteam.conventions import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.plugins.JavaPluginExtension import org.gradle.jvm.toolchain.JavaLanguageVersion import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.withType import org.gradle.api.tasks.testing.Test class JavaConventionsPlugin : Plugin<Project> { override fun apply(target: Project) { with(target) { plugins.apply("java") plugins.apply("jacoco") // 统一Java版本,用toolchain更稳定 configure<JavaPluginExtension> { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 toolchain { languageVersion.set(JavaLanguageVersion.of(17)) } } // 统一JUnit平台配置 tasks.withType<Test> { useJUnitPlatform() finalizedBy(tasks.named("jacocoTestReport")) } // 统一项目group group = "com.yourteam" } } }
ApplicationConventionsPlugin.kt继承Java插件,再加应用专属配置:
package com.yourteam.conventions import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.plugins.ApplicationPlugin import org.gradle.api.tasks.compile.JavaCompile class ApplicationConventionsPlugin : Plugin<Project> { override fun apply(target: Project) { // 先应用Java通用配置 target.plugins.apply(JavaConventionsPlugin::class.java) target.plugins.apply(ApplicationPlugin::class.java) // 统一编译选项 target.tasks.withType<JavaCompile> { options.encoding = "UTF-8" options.compilerArgs.add("-Xlint:unchecked") } // 注册自定义函数,供应用调用 target.extensions.add("customBuildHelper", CustomBuildHelper()) } } class CustomBuildHelper { fun printBuildInfo(project: Project) { println("Building ${project.name} | Group: ${project.group}") } }
步骤3:配置build-conventions的构建脚本
build-conventions/build.gradle.kts:
plugins { `java-gradle-plugin` `kotlin-dsl` // 用Kotlin写插件更顺手 } gradlePlugin { plugins { // 注册Java通用插件,给15个模块用 create("javaConventions") { id = "com.yourteam.conventions.java" implementationClass = "com.yourteam.conventions.JavaConventionsPlugin" } // 注册应用专属插件,给5个应用用 create("applicationConventions") { id = "com.yourteam.conventions.application" implementationClass = "com.yourteam.conventions.ApplicationConventionsPlugin" } } } repositories { mavenCentral() }
这个模块可以单独构建、发布到你们的私有仓库,作为版本化制品使用。
2. 用java-platform统一依赖和插件版本
搞个platform模块,把所有依赖版本、插件版本集中管理,避免每个项目里写一堆零散版本号:
platform模块结构
platform/ ├── build.gradle.kts └── settings.gradle.kts
platform/build.gradle.kts配置:
plugins { `java-platform` `maven-publish` } javaPlatform { allowDependencies() // 允许管理普通依赖,不只是平台依赖 } dependencies { constraints { // 统一依赖版本,引用时不用写版本号 api("org.junit.jupiter:junit-jupiter-api:5.10.0") api("org.junit.jupiter:junit-jupiter-engine:5.10.0") // 其他依赖都集中放这... // 统一自定义插件版本 api("gradle.plugin.com.yourteam:build-conventions:1.0.0") } } publishing { publications { create<MavenPublication>("maven") { from(components["javaPlatform"]) } } repositories { // 配置你们的私有仓库,比如Nexus、Artifactory } }
之后在各项目中,只需要引入这个平台,依赖就自动用统一版本:
dependencies { implementation(platform("com.yourteam:platform:1.0.0")) implementation("org.junit.jupiter:junit-jupiter-api") // 不用写版本号 }
3. 复用settings.gradle.kts的重复逻辑
settings里的仓库配置、插件仓库这些重复代码,抽成脚本插件放在build-conventions的src/main/resources下,比如settings-conventions.gradle.kts:
pluginManagement { repositories { mavenCentral() gradlePluginPortal() // 你们的私有仓库 } resolutionStrategy { eachPlugin { // 让Gradle能找到咱们的自定义插件 if (requested.id.namespace == "com.yourteam.conventions") { useModule("com.yourteam:build-conventions:${requested.version}") } } } } dependencyResolutionManagement { repositories { mavenCentral() // 你们的私有仓库 } }
然后在各项目的settings.gradle.kts里直接引入:
// 本地开发用相对路径,生产用远程路径 val localConventionsExists = file("../build-conventions").exists() apply(from = if (localConventionsExists) "../build-conventions/src/main/resources/settings-conventions.gradle.kts" else "https://your-repo-url/build-conventions/settings-conventions.gradle.kts")
4. 本地开发 vs 生产构建的依赖切换
要支持本地用项目依赖、生产用制品依赖,在根项目的settings.gradle.kts加个判断:
// 检查本地有没有build-conventions和platform模块 val localConventionsExists = file("../build-conventions").exists() val localPlatformExists = file("../platform").exists() // 本地开发时,直接引用本地项目 if (localConventionsExists) { includeBuild("../build-conventions") } if (localPlatformExists) { includeBuild("../platform") } // 应用settings约定 apply(from = if (localConventionsExists) "../build-conventions/src/main/resources/settings-conventions.gradle.kts" else "https://your-repo-url/build-conventions/settings-conventions.gradle.kts")
这样本地开发时,Gradle直接用本地的配置模块,不用发布;生产构建时自动拉取远程制品。
5. 支持模块单独构建
每个模块本身是独立的Gradle项目,只要在各自的build.gradle.kts里应用自定义插件,settings.gradle.kts引入约定脚本就行:
// 模块的build.gradle.kts plugins { id("com.yourteam.conventions.java") } dependencies { implementation(platform("com.yourteam:platform:1.0.0")) // 模块专属依赖 }
执行gradle build就能单独构建模块,完全不依赖根项目。
内容的提问来源于stack exchange,提问作者Pavel Niedoba

