1. 单体项目与多模块项目的差异

从单体走向多模块是 Gradle 项目规模增长中的关键转折点。本章从项目结构、构建速度和代码复用三个维度拆解两者的本质差异,为后续多模块配置与优化建立基础认知。

1.1 项目结构对比

单体项目将所有源码、资源、配置放在同一模块内;多模块项目将功能拆分为独立子模块,每个模块有明确的职责边界。

对比维度单体项目多模块项目
目录组织单一 src/ 下按 package 划分每个模块独立 src/ ,根项目仅含配置
构建文件单个 build.gradle根 build.gradle + 每个模块 build.gradle
模块间依赖同一 JVM 类路径下直接引用通过 project() 声明依赖,Gradle 管理编译顺序
资源隔离无,易冲突模块内独立,资源路径不重合
部署单元单一 JAR/WAR可独立产出 JAR,按需组合部署

单体项目典型配置:

// build.gradle(单体)
plugins {
    id 'java'
    id 'application'
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web:2.7.0'
}

多模块项目典型配置:

// settings.gradle(根项目)
rootProject.name = 'my-app'
include 'common', 'api', 'service', 'web'

// web/build.gradle
dependencies {
    implementation project(':common')
    implementation project(':api')
    implementation project(':service')
}

1.2 构建速度影响

单体项目每次修改都必须重新编译整个工程。多模块项目借助 Gradle 的增量编译与任务缓存,仅重新构建受影响模块及其依赖链。

  • 本地增量编译:Gradle 通过输入/输出快照判断哪些任务需要重跑,多模块粒度更细,跳过无变化模块。
  • 构建缓存:各模块可单独缓存,CI 中命中缓存时直接复用产物,节省 60%~90% 构建时间。
  • 并发构建:Gradle 并行执行无依赖的模块任务,充分利用多核 CPU。
实测参考:某电商项目从单体迁移为 6 个模块后,全量构建时间从 8 分钟降至 2 分钟;增量修改后仅需 30 秒(原单体仍需 8 分钟)。

1.3 代码复用路径

多模块项目通过 Gradle 的 api 与 implementation 配置控制依赖暴露范围,实现精确的复用与封装。

配置暴露给下游典型场景
api所有间接依赖方公共工具类、接口定义层
implementation仅当前模块(不泄漏)内部实现细节、数据库驱动
compileOnly仅编译期,不参与打包Lombok、注解处理器
// :common/build.gradle
dependencies {
    api 'com.google.guava:guava:31.1-jre'
}

// :api/build.gradle
dependencies {
    implementation project(':common')  // 使用 common 中的 Guava,但不会传递给 :web
}

依赖传播原则:implementation 依赖的 api 依赖会透传,避免重复 jar 冲突;compileClasspath 与 runtimeClasspath 分离,提供更快的配置解析。

单体 vs 多模块项目结构 单体项目 (Monolith) controller/ service/ dao/ resources/ 多模块项目 (Multi-Module) :common (公共模块) :api (接口定义) :service (业务逻辑) :web (接入层) 每个模块独立编译、缓存、测试,依赖通过 project() 声明
本章核心结论
  • 结构:多模块以模块为边界,根项目仅做组合;单体以包为边界,所有代码平铺。
  • 速度:多模块利用增量、缓存、并行构建,全量构建提速 3~5 倍,增量构建提速 10~20 倍。
  • 复用:通过 api/implementation 精确控制依赖可见性,避免 jar 冲突与无用依赖。