![97a3f839d3c24b068f46b44097e253ba](https://i-blog.csdnimg.cn/direct/97a3f839d3c24b068f46b44097e253ba.png)

作为 Java 开发者,大概率对 Maven + Java 项目的目录结构 烂熟于心:开发时将业务代码放在 src/main/java、配置文件塞到 src/main/resources,测试代码和测试资源则对应放进 src/test/javasrc/test/resources—— 这套目录结构作为一种“约定”,使得开发人员无需额外配置,Maven 就能自动识别源码位置、处理资源文件,,完成编译打包。

当我们使用 Gradle 构建项目(尤其是 SpringBoot 项目)时,会发现这种熟悉感丝毫未减,因为 Gradle 深度兼容了 Maven 的目录约定,遵循 “约定优于配置” 原则,即便不写一行脚本,只要按上述目录存放代码和资源,Gradle 就能自动执行 compileJava(编译生产代码)、compileTestJava(编译测试代码)等核心任务。而支撑这一切 “无感知兼容” 的核心机制,正是 sourceSets(源码集)—— 它就像 Gradle 背后的 “约定执行者”,默默将目录规则与构建流程绑定。

本文将从 Gradle 项目的标准结构 入手,逐步拆解源码集的概念、默认配置与自定义实战,帮你既掌握 “约定” 的便捷性,又能灵活应对复杂项目的结构设计需求。

不同类型的项目(如 Java、Kotlin、Android)目录结构略有差异,本文以 Java 项目 为例

一、Gradle Java 项目的标准结构

1.1 单模块项目结构

一个典型的 Gradle 单模块项目(以 Java 应用为例)的目录集中在 src 下,所有 Java 源代码、测试代码及资源文件按类型(生产 / 测试)进行划分,比如 src/main/java 对应生产源码、src/test/resources 对应测试资源,典型结构如下:

gradle-project/
├── build.gradle.kts   		// 模块构建脚本(配置依赖、任务、源码集等)
├── settings.gradle.kts 	// 项目设置脚本(指定项目名称、包含的子模块)
└── src/
    ├── main/          		// 生产环境核心代码(必选目录)
    │   ├── java/      		// Java 源代码(业务逻辑、工具类等)
    │   └── resources/ 		// 生产资源文件(application.yml、静态资源等)
    │
    └── test/          		// 测试代码(可选目录)
        ├── java/      		// 测试 Java 源码(单元测试、测试工具类等)
        └── resources/ 		// 测试资源文件(测试用配置、模拟数据等)

1.2 多模块项目的结构

当项目规模扩大时,通常会拆分为多模块(核心层 + 接口层 + 公共层),通过「父模块统一管理,子模块各司其职」降低耦合。典型多模块结构(以 SpringBoot 项目为例)如下:

gradle-project/
├── build.gradle.kts       // 父模块构建脚本(统一管理子模块依赖版本、公共插件)
├── settings.gradle.kts    // 项目设置脚本(声明子模块:common、core、web)
├── common/                // 子模块 1:公共工具模块(封装跨模块工具类、常量)
│   ├── build.gradle.kts   // 子模块专属配置(如引入工具类依赖)
│   └── src/               // 子模块源码(遵循单模块目录约定)
│       ├── main/
│       └── test/
├── core/                  // 子模块 2:核心业务模块(处理业务逻辑、数据访问)
│   ├── build.gradle.kts
│   └── src/
└── api/                   // 子模块 3:api 接口模块
    ├── build.gradle.kts
    └── src/

多模块项目中,父模块的 src 目录通常不使用—— 所有 Java 源代码和资源由子模块各自维护,父模块仅负责全局配置(如依赖版本锁定、插件统一引入),避免子模块配置重复。

二、什么是 Source Sets?

在 Gradle 中,SourceSets 是对项目 “源码分组” 的抽象描述。它不仅定义了 “源码 / 资源该放在哪里”,还关联了 “该分组需要哪些依赖”“编译后输出到哪里”“对应的构建任务是什么”。

简单来说,Source Sets(源码集) 本质是「一组源代码目录 + 资源目录 + 关联任务」的集合。其核心思想是通过「目录」区分项目中不同类型的代码(如生产代码与测试代码),实现代码的隔离与复用;同时自动关联对应的编译、测试、打包等构建流程。

每个 SourceSet 包含以下关键组成部分,共同支撑对源码的全生命周期管理:

组成部分说明
源代码目录存放源代码文件,例如 src/main/javasrc/test/java
资源目录存放非代码资源,如 src/main/resources(生产资源)、src/test/resources(测试资源)。
类路径编译或运行时依赖的 JAR 包、库文件。
关联任务Gradle 自动创建的构建任务,如 compileJava(编译 Java 代码)、processResources(处理资源文件)、test(运行测试用例)等。

2.1 默认源码集

当项目引入 Java 插件(或基于其扩展的插件,如 java-libraryapplication)后,Gradle 会自动创建 maintest 源码集:

源码集名称用途依赖关系默认目录
main生产环境核心代码无(其他源码集依赖它)src/main/javasrc/main/resources
test单元测试 / 集成测试代码依赖 main 源码集src/test/javasrc/test/resources

在这里插入图片描述

2.1.2 main 源码集
目录结构

main 源码集用于存放项目的核心业务代码和生产环境资源,包含 Java 源代码和资源文件两部分:

src/
└── main/
    ├── java/         # Java 源代码(核心业务逻辑)
    └── resources/    # 生产环境资源(配置文件、静态资源等)
目录用途
src/main/java/存放 Java 源代码(核心业务逻辑代码)
src/main/resources/存放生产环境相关的资源文件(如配置文件、静态资源等)
关联任务

Gradle 会为 main 源码集自动生成编译源码处理资源的关联任务,在执行 build 等顶层构建命令时自动触发:

  • compileJava 任务:负责编译 src/main/java 目录下的所有 Java 源代码,输出到 build/classes/java/main 目录。
  • processResources 任务:处理(复制 / 过滤) src/main/resources 目录下的资源文件(如配置文件、静态资源等)到 build/resources/main 目录。

当执行 build 时,Gradle 会优先触发 processResourcescompileJava任务:

  1. compileJava 完成后,build/classes/java/main 中会生成所有生产代码的 .class 文件;

    Alt

  2. processResources 完成后,build/resources/main 中会生成可直接用于运行的资源文件;

    Alt

最终这两个任务的输出(build/classes/java/mainbuild/resources/main)会作为输入,用于后续的 jar(打包)、assemble(聚合产物)等任务,最终生成可交付的 JAR/WAR 包。

在这里插入图片描述

2.2.2 test 源码集
目录结构

test 源码集用于存放验证 main 源码集功能的测试代码(如单元测试、组件测试),包含测试源代码和测试资源文件两部分:

src/
└── test/
    ├── java/         # 测试源代码(基于 JUnit、TestNG 等框架)
    └── resources/    # 测试资源(测试配置、测试数据等)
目录用途
src/test/java/存放测试代码(单元测试、集成测试等),通常基于 JUnit、TestNG 框架。
src/test/resources/存放测试资源文件(测试配置、测试数据等),运行测试时会自动加载。
关联任务

Gradle 会自动为 test 源码集生成以下核心任务支撑完整的测试执行流程:

  • compileTestJava:编译 src/test/java 目录下的测试源代码 .java 文件转换为 .class 字节码,输出到 build/classes/java/test 目录。
  • processTestResources:处理 src/test/resources 目录下的测试资源文件,最终输出到 build/resources/test 目录。

执行 test 任务时,Gradle 会先执行 compileTestJavaprocessTestResources,确保测试代码和资源准备就绪,再通过测试框架(如 JUnit)执行测试用例,最终输出测试结果(成功 / 失败数量、耗时等)。

2.2 自定义任务查看 Source Sets

  1. build.gradle.kts 构建脚本中引入 java 插件:

    plugins {
    java
    }
  2. build.gradle.kts创建自定义sourceSets任务,打印项目所有的 Source Sets 信息:

    tasks.register("sourceSets") {
    doLast {
    // 获取所有的 sourceSets
    sourceSets.forEach { sourceSet ->
    println("Source Set: ${sourceSet.name}")
    // 输出 Java 源码目录的相对路径
    sourceSet.java.srcDirs.forEach {
    println("  Java sources: ${project.relativePath(it)}")
    }
    // 输出资源目录的相对路径
    sourceSet.resources.srcDirs.forEach {
    println("  Resources: ${project.relativePath(it)}")
    }
    }
    }
    }
  3. 在项目根目录执行任务:

    gradle sourceSets
  4. 控制台输出以下:

    在这里插入图片描述

三、自定义 integrationTest 源码集

默认的 maintest 足以满足简单项目需求,但对于复杂项目(如需要区分单元测试和集成测试),自定义源码集是更优选择,自定义源码集需结合「目录创建」「依赖配置」「任务绑定」三步。

集成测试为例(与单元测试隔离,通常需要启动外部服务或数据库),我们将创建 integrationTest 源码集,实现代码隔离和独立执行。

3.1 创建目录结构

遵循 Gradle 源码集的约定优于配置原则,在 src 目录下创建 integrationTest 源码集的目录结构:

src/
├── integrationTest/	# 集成测试源码集
│   ├── java/         	# 集成测试 Java 代码
│   └── resources/    	# 集成测试资源(配置文件等)
├── main/
│   └── ...
└── test/
    └── ...

在这里插入图片描述

3.2 配置源码集

build.gradle.kts 中通过 sourceSets 块添加 integrationTest 源码集配置,指定 integrationTest 源码集的目录路径和类路径,确保集成测试能访问业务代码和必要的依赖:

sourceSets {
// 定义名为 integrationTest 的源码集
create("integrationTest") {
// 配置源码目录
java {
srcDir("src/integrationTest/java") // Java 源码路径
}
resources {
srcDir("src/integrationTest/resources") // 资源路径
}
// 依赖 main 源码集的编译结果(即可以直接调用业务代码)
compileClasspath += sourceSets.main.get().output
runtimeClasspath += sourceSets.main.get().output
// 复用 test 源码集的依赖(如 JUnit 测试框架)
compileClasspath += sourceSets.test.get().output
runtimeClasspath += sourceSets.test.get().output
}
}

刷新 Gradle 后,IDE(如 IntelliJ IDEA)会自动识别为测试源码目录,无需额外配置:
在这里插入图片描述

3.3 关联任务

定义 integrationTest 源码集后,Gradle 会自动生成一系列任务用于处理源码编译、资源拷贝、清理等基础工作,命名遵循如下规则:

[固定前缀] + [源码集名称(驼峰命名)] + [固定后缀]
  • 固定前缀:表示任务的 “核心动作”,由任务类型决定(如 compile 代表编译,process 代表资源处理)。
  • 源码集名称(驼峰命名):关联的自定义源码集名称(如 integrationTest ),与源码集一一对应。
  • 固定后缀:表示任务的 “功能范围”(如 Java 代表编译 Java 代码,Resources 代表处理资源文件)。

结合 “自定义源码集 integrationTest” 为例:

任务类型命名规则示例(源码集:integrationTest)
编译任务compile + [SourceSetName] + JavacompileIntegrationTestJava
资源处理任务process + [SourceSetName] + ResourcesprocessIntegrationTestResources
聚合任务[SourceSetName] + ClassesintegrationTestClasses

在这里插入图片描述

3.4 添加依赖

集成测试需要测试框架(如 JUnit),需要配置依赖:

dependencies {
// 集成测试依赖 JUnit
testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.2")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.2")
}

3.5 创建集成测试任务

需要注册一个 Test 类型的任务,关联 integrationTest 源码集的编译结果,指定测试类路径和运行时路径,并配置测试执行规则(如框架、依赖顺序、过滤规则):

// 创建 integrationTest 任务
tasks.register<Test>("integrationTest") {
  description = "执行集成测试"
  group = "Verification" // 归类到「验证」分组(与 test 任务同组)
  // 关联 integrationTest 源码集的编译结果和类路径
  testClassesDirs = sourceSets["integrationTest"].output.classesDirs
  classpath = sourceSets["integrationTest"].runtimeClasspath
  // 配置测试框架
  useJUnitPlatform()
  // 过滤测试类(仅执行以 IntegrationTest 结尾的类)
  //include("**/*IntegrationTest.java")
  }

3.6 执行集成测试

  1. 创建集成测试代码:src/integrationTest/java/example/NewIntegrationTest.java

    public class NewIntegrationTest {
    @Test
    public void test() {
    System.out.println("集成测试完成");
    }
    }
  2. 在项目根目录执行以下命令运行集成测试:

    gradle integrationTest
  3. 查看控制台输出测试通过信息:

    在这里插入图片描述