按需加载子模块是让gradle运行时只解析被触发的模块,避免冗余初始化;通过includebuild()替代硬编码include、条件化引入、配置缓存、composite builds及jvm调优,可使超大型单体仓库配置耗时降60%以上。

按需加载子模块不是“跳过构建”,而是让 Gradle 在执行时只解析、配置和执行真正被触发的模块,避免对数百个无关模块做冗余的初始化和依赖图计算。这对超大型单体仓库(比如含 200+ 子模块、日均提交频繁的内部平台)效果显著——配置阶段耗时可下降 60% 以上。
用 includeBuild() 替代硬编码 include()
传统 settings.gradle 中写死 include ':service-order', ':service-payment', ':web-admin',会导致所有模块在每次构建时都被强制加载。正确做法是:只保留核心骨架模块,其余通过条件化 include 或动态注册。
- 把非必启模块移出 settings.gradle,改用 includeBuild('modules/payment') 按需引入(适用于独立构建或测试特定模块)
- 对高频变更模块,保留 include;对稳定/低频模块(如 legacy-report、audit-archiver),改用 if (System.getenv('ENABLE_AUDIT') == 'true') { include 'audit-archiver' }
- 配合 gradle.properties 定义开关:enable.payment=true,再在 settings.gradle 中读取并控制 include
启用配置缓存 + 延迟插件应用
Gradle 9.4 的配置缓存(configuration cache)要求所有模块的构建脚本必须“纯函数式”——不能在配置阶段读取环境变量、访问文件系统或调用非确定性方法。这倒逼你把模块加载逻辑从“静态 include”转向“运行时判断”。
- 在根 build.gradle.kts 中用 plugins { id("com.example.module-loader") version "1.0" apply false } 声明插件但不立即应用
- 在各子模块的 build.gradle.kts 里,仅当该模块被 include 后才 apply(plugin = "com.example.module-loader")
- 避免在 settings.gradle 里直接 apply plugin,否则会污染整个配置上下文
用 composite builds 隔离冷模块
把长期不变、极少参与日常构建的模块(如文档生成器、合规扫描器、历史数据迁移工具)抽成独立 Git 仓库,再用 includeBuild('../compliance-tools') 引入。这样它们的 build.gradle 不会被主仓库反复解析,也不会拖慢主构建的配置阶段。
- composite build 下的模块完全独立生命周期:可单独测试、单独发布、单独升级 Gradle 版本
- 主仓库只需维护一个指向子仓库 commit hash 的版本锁(如 gradle/libs.versions.toml 中定义 compliance-tools = "1.2.0+abc123")
- CI 流水线中可设置 ./gradlew build -PincludeCompliance=false 彻底跳过该模块
结合 JVM 参数与守护进程优化启动开销
按需加载后,单次构建涉及的模块变少,但 JVM 初始化和 Daemon 启动成本依然存在。需针对性调优:
- 在 gradle.properties 中设 org.gradle.jvmargs=-Xmx3g -XX:MaxMetaspaceSize=512m -XX:+UseZGC(Java 26 默认 ZGC,停顿低于 10ms)
- 禁用无用的 JVM 功能:-XX:-UseBiasedLocking -XX:+DisableExplicitGC 减少 GC 干扰
- 确保 org.gradle.daemon=true(默认开启),且 Daemon 复用率高——按需加载后,不同命令(test vs build)更可能复用同一 Daemon 实例
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











