Gradle构建脚本详解
# Gradle 构建脚本详解
build.gradle 是 Gradle 项目的核心构建脚本,相当于 Maven 的 pom.xml。但与 XML 不同,它是一段可执行的代码,可以用 Groovy 或 Kotlin 编写。
本文以 Groovy DSL 为主讲解,涉及 Kotlin DSL 差异时标注。
# 脚本整体结构
一个典型的 build.gradle 包含以下几个核心块:
// 1. 插件声明
plugins {
id 'java'
}
// 2. 项目元信息
group = 'com.example'
version = '1.0.0'
// 3. 仓库配置
repositories {
mavenCentral()
}
// 4. 依赖声明
dependencies {
implementation 'org.springframework:spring-core:6.1.0'
}
// 5. 任务配置
tasks.named('test') {
useJUnitPlatform()
}
// 6. 扩展配置(插件提供的 DSL)
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
下面逐一拆解每个块。
# plugins 块
plugins 块用于声明项目使用的 Gradle 插件,每个插件会带来一组 Task 和约定。
# 基本用法
plugins {
id 'java' // Java 构建
id 'war' // WAR 打包
id 'org.springframework.boot' version '4.1.0' // Spring Boot
}
2
3
4
5
# 使用 Version Catalog 引用插件
在多模块或需要统一版本管理时,推荐通过 Version Catalog 引用插件。以 interview-guide 项目为例:
plugins {
id 'java'
alias(libs.plugins.spring.boot)
alias(libs.plugins.spring.dependency.management)
}
2
3
4
5
对应的 gradle/libs.versions.toml:
[plugins]
spring-boot = { id = "org.springframework.boot", version.ref = "spring-boot" }
spring-dependency-management = { id = "io.spring.dependency-management", version = "1.1.7" }
2
3
alias()方式的好处是版本统一在 Version Catalog 中管理,多模块项目不会出现插件版本不一致的问题。详见 Gradle 依赖管理。
# plugins vs apply plugin
| 方式 | 语法 | 说明 |
|---|---|---|
plugins {} 块 | id 'java' | 推荐,支持类型安全、性能更好 |
apply 方法 | apply plugin: 'java' | 旧式写法,仍可用但不推荐 |
plugins {}块在构建脚本解析阶段就处理插件,比apply更快、更安全。
# group 与 version
group = 'com.interview'
version = '0.0.1-SNAPSHOT'
2
| 属性 | 说明 | Maven 对应 |
|---|---|---|
group | 组织 ID | <groupId> |
version | 项目版本 | <version> |
name | 项目名称(自动从 settings.gradle 获取) | <artifactId> |
Gradle 的坐标三元组
group:name:version与 Maven 完全一致,发布到 Maven 仓库时兼容。
# repositories 块
repositories 声明从哪里下载依赖,类似 Maven 的 <repositories>。
# 常用仓库
repositories {
mavenLocal() // 本地 Maven 仓库
mavenCentral() // Maven 中央仓库
maven { url = uri('https://maven.aliyun.com/repository/public/') } // 阿里云镜像
google() // Google Maven 仓库(Android)
gradlePluginPortal() // Gradle 插件门户
}
2
3
4
5
6
7
# 实际项目示例
interview-guide 项目配置了多个仓库,按顺序解析:
repositories {
maven { url = uri('https://maven.aliyun.com/repository/public/') }
maven { url = uri('https://maven.aliyun.com/repository/spring/') }
maven { url = uri('https://maven.aliyun.com/repository/gradle-plugin/') }
mavenCentral()
maven { url = uri('https://repo.spring.io/milestone') } // Spring 里程碑版本
}
2
3
4
5
6
7
Gradle 按从上到下的顺序查找依赖,找到即停止。国内项目通常把阿里云镜像放在最前面加速下载。
# 仓库认证
需要认证的私有仓库:
repositories {
maven {
url = uri('https://repo.example.com/private')
credentials {
username = 'myUser'
password = 'myPassword'
}
}
}
2
3
4
5
6
7
8
9
不要在构建脚本中硬编码密码,建议通过
gradle.properties或环境变量注入。
# dependencies 块
dependencies 声明项目依赖,是构建脚本中最核心的部分。
# 基本语法
dependencies {
// 字符串形式:group:name:version
implementation 'org.springframework.boot:spring-boot-starter-web:3.2.0'
// Map 形式
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '3.2.0'
}
2
3
4
5
6
7
# 依赖配置项(Configuration)
Gradle 的依赖配置项比 Maven 的 scope 更细粒度:
| 配置项 | 说明 | Maven 对应 |
|---|---|---|
implementation | 主依赖,编译和运行都需要,不暴露给下游 | compile |
api | 主依赖,编译和运行都需要,暴露给下游 | compile(传递) |
compileOnly | 仅编译时需要,不打包 | provided |
runtimeOnly | 仅运行时需要,打包但不参与编译 | runtime |
testImplementation | 测试编译和运行需要 | test |
testCompileOnly | 仅测试编译需要 | - |
testRuntimeOnly | 仅测试运行需要 | - |
annotationProcessor | 注解处理器(编译期) | - |
developmentOnly | 开发时需要(如 DevTools) | - |
implementationvsapi是 Gradle 依赖管理的核心区别,详见 Gradle 依赖管理。
# 实际项目示例
interview-guide 项目的依赖声明:
dependencies {
// Spring Boot Starters
implementation 'org.springframework.boot:spring-boot-starter-webmvc'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
// 运行时依赖(数据库驱动)
runtimeOnly 'org.postgresql:postgresql'
// 仅测试
testRuntimeOnly 'com.h2database:h2'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
// 注解处理器
compileOnly libs.lombok
annotationProcessor libs.lombok
// Version Catalog 引用
implementation libs.tika.core
implementation libs.springdoc
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
依赖管理的详细内容(Version Catalog、排除依赖、BOM 等)见 Gradle 依赖管理。
# java 扩展配置
应用 java 插件后,可以通过 java {} 块配置 Java 相关选项。
# Toolchain(工具链)
Toolchain 用于指定编译和运行使用的 JDK 版本,无需依赖本地安装的 JDK:
java {
toolchain {
languageVersion = JavaLanguageVersion.of(25)
}
}
2
3
4
5
配合
foojay-resolver-convention插件(在 settings.gradle 中声明),Gradle 会自动下载所需的 JDK,团队成员无需手动安装指定版本。
interview-guide 项目使用 Java 25:
java {
toolchain {
languageVersion = JavaLanguageVersion.of(25)
}
}
2
3
4
5
# Source Compatibility vs Toolchain
| 方式 | 说明 |
|---|---|
sourceCompatibility / targetCompatibility | 指定源码和目标字节码版本,仍使用当前 JDK 编译 |
toolchain | 指定使用哪个版本的 JDK 来编译和运行,更推荐 |
// 旧式(不推荐)
sourceCompatibility = '25'
targetCompatibility = '25'
// 新式(推荐)
java {
toolchain {
languageVersion = JavaLanguageVersion.of(25)
}
}
2
3
4
5
6
7
8
9
10
# tasks 配置
Task 是 Gradle 构建的核心。插件提供的 Task 可以通过 tasks.named() 配置,也可以用 tasks.register() 自定义。
# 配置已有 Task
// 配置 test task
tasks.named('test') {
useJUnitPlatform() // 使用 JUnit 5
excludeTags 'rag-eval' // 排除特定标签的测试
jvmArgs '--enable-native-access=ALL-UNNAMED'
}
2
3
4
5
6
interview-guide 项目配置了测试排除和 JVM 参数:
tasks.named('test') {
useJUnitPlatform {
excludeTags 'rag-eval', 'voice-context-eval'
}
jvmArgs '--enable-native-access=ALL-UNNAMED'
}
2
3
4
5
6
# 自定义 Task
// 注册一个新 task
tasks.register('hello') {
group = 'custom'
description = '打印问候语'
doLast {
println 'Hello from Gradle!'
}
}
2
3
4
5
6
7
8
执行:gradlew hello
# Task 的执行阶段
Gradle Task 有两个主要执行阶段:
| 阶段 | 配置方式 | 说明 |
|---|---|---|
| 配置阶段 | 直接在 task 块中写代码 | 每次构建都执行(无论 task 是否运行) |
| 执行阶段 | doFirst / doLast | 仅当 task 实际执行时才运行 |
tasks.register('myTask') {
println '配置阶段执行' // 每次构建都打印
doFirst {
println '执行阶段 - 最先执行'
}
doLast {
println '执行阶段 - 最后执行'
}
}
2
3
4
5
6
7
8
9
10
11
重要:耗时操作(文件读写、网络请求)必须放在
doFirst/doLast中,不要直接写在 task 块里,否则每次构建配置阶段都会执行,拖慢构建速度。
# 自定义 Test Task 示例
interview-guide 项目注册了专门的 RAG 测评 task:
tasks.register('ragEvaluation', Test) {
description = '运行 RAG 测评(需要 RUN_RAG_EVAL=true)'
group = 'verification'
testClassesDirs = sourceSets.test.output.classesDirs
classpath = sourceSets.test.runtimeClasspath
useJUnitPlatform {
includeTags 'rag-eval'
}
jvmArgs '--enable-native-access=ALL-UNNAMED'
outputs.upToDateWhen { false } // 每次都重新执行
}
2
3
4
5
6
7
8
9
10
11
这个 task 继承自
Test类型,只运行带rag-eval标签的测试。outputs.upToDateWhen { false }禁用了增量检查,确保每次都执行。详见 Gradle 进阶与实战。
# 编译配置
# 全局编码
tasks.withType(JavaCompile).configureEach {
options.encoding = 'UTF-8'
options.release = 25
}
2
3
4
| 配置项 | 说明 |
|---|---|
options.encoding | 源文件编码 |
options.release | 目标字节码版本(替代 --release 编译参数) |
configureEach是延迟配置,只有当JavaCompiletask 实际创建时才应用配置,比all更高效。
# Spring Boot 专属配置
应用 org.springframework.boot 插件后,可以使用 Spring Boot 专属 DSL:
springBoot {
// 生成 build-info.properties
buildInfo()
}
// 配置 bootJar
tasks.named('bootJar') {
archiveFileName = 'app.jar'
launchScript() // 生成可执行 shell 脚本
}
// 配置 bootRun
tasks.named('bootRun') {
// 传递系统属性
systemProperty 'spring.profiles.active', 'dev'
// JVM 参数
jvmArgs '-Dfile.encoding=UTF-8'
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
interview-guide 项目在 bootRun 中注入了 .env 文件:
tasks.named('bootRun') {
jvmArgs = [
'-Dfile.encoding=UTF-8',
'--enable-native-access=ALL-UNNAMED'
]
// 加载 .env 文件中的环境变量
def envFile = rootProject.file('.env')
if (envFile.exists()) {
envFile.eachLine { line ->
// 解析 KEY=VALUE 并注入
// ...
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
bootRun类似 Maven 的spring-boot:run,用于本地启动 Spring Boot 应用。
# 完整脚本示例
以下是 interview-guide 项目 app/build.gradle 的结构概览:
plugins {
id 'java'
alias(libs.plugins.spring.boot)
alias(libs.plugins.spring.dependency.management)
}
group = 'com.interview'
version = '0.0.1-SNAPSHOT'
repositories {
maven { url = uri('https://maven.aliyun.com/repository/public/') }
mavenCentral()
maven { url = uri('https://repo.spring.io/milestone') }
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-webmvc'
runtimeOnly 'org.postgresql:postgresql'
compileOnly libs.lombok
annotationProcessor libs.lombok
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(25)
}
}
tasks.withType(JavaCompile).configureEach {
options.encoding = 'UTF-8'
options.release = 25
}
tasks.named('test') {
useJUnitPlatform {
excludeTags 'rag-eval', 'voice-context-eval'
}
}
tasks.named('bootRun') {
jvmArgs '-Dfile.encoding=UTF-8'
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# Kotlin DSL 对照
同样的配置用 Kotlin DSL(build.gradle.kts)编写:
plugins {
java
alias(libs.plugins.spring.boot)
alias(libs.plugins.spring.dependency.management)
}
group = "com.interview"
version = "0.0.1-SNAPSHOT"
repositories {
mavenCentral()
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-webmvc")
runtimeOnly("org.postgresql:postgresql")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(25)
}
}
tasks.withType<JavaCompile>().configureEach {
options.encoding = "UTF-8"
options.release = 25
}
tasks.named<Test>("test") {
useJUnitPlatform()
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# Groovy vs Kotlin DSL 语法差异速查
| 语法点 | Groovy DSL | Kotlin DSL |
|---|---|---|
| 字符串引号 | 单引号 '...' | 双引号 "..." |
| 插件引用 | id 'java' | java(无引号) |
| 依赖声明 | implementation 'g:n:v' | implementation("g:n:v") |
| Task 配置 | tasks.named('test') | tasks.named<Test>("test") |
| 属性赋值 | group = 'x' | group = "x" |