Gradle构建详解
一、知识概述
Gradle是新一代构建自动化工具,结合了Apache Ant的灵活性和Apache Maven的依赖管理能力。Gradle使用Groovy或Kotlin DSL编写构建脚本,具有高度的灵活性和强大的扩展性,是Android和Spring Boot项目的首选构建工具。
本文将深入分析Gradle的核心原理,包括Groovy DSL、任务系统、依赖管理、多项目构建、增量构建等核心内容,并提供实战代码示例。
Gradle的核心优势
- 灵活性:基于Groovy/Kotlin的DSL,编写构建逻辑如写代码
- 增量构建:智能检测变更,只构建必要的部分
- 构建缓存:跨机器复用构建结果
- 并行执行:充分利用多核CPU
- 生态丰富:Android、Spring Boot、Kotlin等官方支持
二、知识点详细讲解
2.1 Gradle项目结构
标准目录布局
bash
my-project/
├── build.gradle # 构建脚本(Groovy DSL)
├── settings.gradle # 项目设置(多项目必需)
├── gradle.properties # Gradle属性配置
├── gradlew # Gradle Wrapper脚本(Unix)
├── gradlew.bat # Gradle Wrapper脚本(Windows)
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar # Wrapper JAR
│ └── gradle-wrapper.properties # Wrapper配置
├── src/
│ ├── main/
│ │ ├── java/ # 主代码
│ │ └── resources/ # 主资源
│ └── test/
│ ├── java/ # 测试代码
│ └── resources/ # 测试资源
└── build/ # 构建输出目录
build.gradle基础配置
groovy
// ==================== 插件声明 ====================
plugins {
id 'java' // Java插件
id 'org.springframework.boot' version '3.2.0'
id 'io.spring.dependency-management' version '1.1.4'
}
// ==================== 项目信息 ====================
group = 'com.example'
version = '1.0.0'
description = 'A sample Gradle project'
// ==================== Java版本配置 ====================
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
// ==================== 仓库配置 ====================
repositories {
// 优先使用阿里云镜像
maven { url 'https://maven.aliyun.com/repository/public' }
maven { url 'https://maven.aliyun.com/repository/spring' }
mavenCentral()
google()
}
// ==================== 依赖配置 ====================
dependencies {
// implementation: 编译和运行时都需要,但不传递给依赖方
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-validation'
// compileOnly: 仅编译时需要
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
// runtimeOnly: 仅运行时需要
runtimeOnly 'com.mysql:mysql-connector-j'
// testImplementation: 测试时需要
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.junit.jupiter:junit-jupiter'
}
// ==================== 测试配置 ====================
test {
useJUnitPlatform() // 使用JUnit 5
// 并行执行测试
maxParallelForks = Runtime.runtime.availableProcessors().intdiv(2) ?: 1
// 测试输出
testLogging {
events "passed", "skipped", "failed"
showExceptions true
showCauses true
showStackTraces true
}
}
// ==================== 任务配置 ====================
tasks.named('bootJar') {
archiveFileName = "${project.name}-${project.version}.jar"
}
// ==================== 编码配置 ====================
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
options.compilerArgs += ['-parameters']
}
tasks.withType(Javadoc) {
options.encoding = 'UTF-8'
}
2.2 Groovy DSL语法
基础语法
groovy
// ==================== 变量定义 ====================
// def: 动态类型
def name = 'Gradle'
def version = 8.5
// 强类型
String project_name = 'My Project'
Integer build_number = 100
// 字符串
def single = '单引号字符串' // 不可变
def double = "双引号字符串 ${name}" // 支持插值
def triple = '''多行
字符串'''
// 列表
def list = ['Java', 'Groovy', 'Kotlin']
list << 'Scala' // 追加元素
// 映射
def map = [name: 'Gradle', version: '8.5']
map.name // 访问值
map['version']
// ==================== 闭包(Closure) ====================
// 闭包是Groovy的核心特性
def closure = { String str ->
println "Hello, ${str}!"
}
closure.call('World') // 调用闭包
closure('Gradle') // 简写调用
// 闭包作为参数
def numbers = [1, 2, 3, 4, 5]
def doubled = numbers.collect { it * 2 }
def even = numbers.findAll { it % 2 == 0 }
// ==================== 方法定义 ====================
def greet(name) {
return "Hello, ${name}!"
}
// 默认参数
def build(type = 'jar') {
println "Building ${type}"
}
// 可变参数
def compile(String... sources) {
sources.each { println "Compiling ${it}" }
}
// ==================== 类定义 ====================
class BuildConfig {
String appName
String version
BuildConfig(String name, String ver) {
this.appName = name
this.version = ver
}
void printInfo() {
println "${appName} - ${version}"
}
}
Gradle DSL特殊语法
groovy
// ==================== 任务定义 ====================
// 方式1: 使用task关键字
task hello {
group = 'custom'
description = 'Prints hello message'
doFirst {
println 'Preparing...'
}
doLast {
println 'Hello, Gradle!'
}
}
// 方式2: 使用tasks.register(推荐,懒加载)
tasks.register('bye') {
group = 'custom'
description = 'Prints goodbye message'
doLast {
println 'Goodbye, Gradle!'
}
}
// 方式3: 自定义任务类型
tasks.register('customTask', CustomTask) {
message = 'Custom task executed'
}
class CustomTask extends DefaultTask {
@Input
String message
@TaskAction
void execute() {
println message
}
}
// ==================== 任务依赖 ====================
task compile {
doLast { println 'Compiling...' }
}
task test {
dependsOn compile
doLast { println 'Testing...' }
}
task build {
dependsOn test
doLast { println 'Building...' }
}
// ==================== 文件操作 ====================
// 复制文件
task copyFiles(type: Copy) {
from 'src/main/resources'
into 'build/resources'
include '**/*.properties'
exclude '**/*.bak'
// 文件过滤
filter { line ->
line.replace('@version@', project.version)
}
}
// 删除文件
task cleanFiles(type: Delete) {
delete 'build/tmp'
}
// 创建目录
task createDirs {
doLast {
mkdir 'build/output'
}
}
// ==================== 文件集合 ====================
def sourceFiles = fileTree('src/main/java') {
include '**/*.java'
exclude '**/*Test.java'
}
// 遍历文件
sourceFiles.each { File file ->
println "Found: ${file.name}"
}
2.3 依赖管理
依赖配置类型
groovy
dependencies {
// ==================== Java插件提供的配置 ====================
// implementation: 编译和运行都需要,不传递
implementation 'org.springframework:spring-core:6.1.0'
// api: 编译和运行都需要,会传递(需要java-library插件)
api 'org.apache.commons:commons-lang3:3.14.0'
// compileOnly: 仅编译时需要
compileOnly 'org.projectlombok:lombok:1.18.30'
// runtimeOnly: 仅运行时需要
runtimeOnly 'com.mysql:mysql-connector-j:8.2.0'
// annotationProcessor: 注解处理器
annotationProcessor 'org.projectlombok:lombok:1.18.30'
// testImplementation: 测试编译和运行需要
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0'
// testCompileOnly: 仅测试编译时需要
testCompileOnly 'org.junit.jupiter:junit-jupiter-api:5.10.0'
// testRuntimeOnly: 仅测试运行时需要
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.0'
// ==================== 特殊依赖方式 ====================
// 项目依赖
implementation project(':common')
// 文件依赖
implementation files('libs/custom.jar')
// 文件集合依赖
implementation fileTree('libs') {
include '*.jar'
}
// 依赖约束(constraint)
implementation('org.springframework:spring-core') {
version {
strictly '6.1.0'
prefer '6.1.0'
}
}
// 排除传递依赖
implementation('org.springframework.boot:spring-boot-starter-web') {
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'
}
}
依赖版本管理
groovy
// ==================== 方式1: 版本变量 ====================
ext {
springVersion = '6.1.0'
junitVersion = '5.10.0'
}
dependencies {
implementation "org.springframework:spring-core:${springVersion}"
testImplementation "org.junit.jupiter:junit-jupiter:${junitVersion}"
}
// ==================== 方式2: 版本目录(推荐) ====================
// gradle/libs.versions.toml
/*
[versions]
spring = "6.1.0"
junit = "5.10.0"
[libraries]
spring-core = { module = "org.springframework:spring-core", version.ref = "spring" }
spring-context = { module = "org.springframework:spring-context", version.ref = "spring" }
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" }
[plugins]
spring-boot = { id = "org.springframework.boot", version = "3.2.0" }
*/
// build.gradle
dependencies {
implementation libs.spring.core
implementation libs.spring.context
testImplementation libs.junit.jupiter
}
// ==================== 方式3: BOM导入 ====================
dependencies {
implementation platform('org.springframework.boot:spring-boot-dependencies:3.2.0')
// 不需要指定版本
implementation 'org.springframework:spring-core'
implementation 'org.springframework.boot:spring-boot-starter-web'
}
2.4 多项目构建
settings.gradle配置
groovy
// settings.gradle
rootProject.name = 'multi-project'
// 包含子项目
include 'common'
include 'api'
include 'service'
include 'web'
// 项目目录自定义
// project(':common').projectDir = new File('modules/common')
父项目build.gradle
groovy
// 根项目 build.gradle
plugins {
id 'java'
id 'io.spring.dependency-management' version '1.1.4' apply false
}
// 所有项目通用配置
allprojects {
group = 'com.example'
version = '1.0.0'
repositories {
maven { url 'https://maven.aliyun.com/repository/public' }
mavenCentral()
}
}
// 所有Java项目通用配置
subprojects {
apply plugin: 'java'
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
dependencies {
compileOnly 'org.projectlombok:lombok:1.18.30'
annotationProcessor 'org.projectlombok:lombok:1.18.30'
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0'
}
test {
useJUnitPlatform()
}
}
// 特定项目配置
project(':common') {
dependencies {
api 'org.apache.commons:commons-lang3:3.14.0'
}
}
project(':service') {
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
dependencies {
implementation project(':common')
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.3'
runtimeOnly 'com.mysql:mysql-connector-j'
}
}
project(':web') {
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
dependencies {
implementation project(':service')
implementation 'org.springframework.boot:spring-boot-starter-web'
}
bootJar {
archiveFileName = "${project.name}.jar"
}
}
2.5 增量构建
任务输入输出声明
groovy
// ==================== 增量任务 ====================
abstract class ProcessResourcesTask extends DefaultTask {
// 输入目录
@InputDirectory
abstract DirectoryProperty getInputDir()
// 输出目录
@OutputDirectory
abstract DirectoryProperty getOutputDir()
// 输入属性
@Input
abstract Property<String> getVersion()
@TaskAction
void execute(InputChanges inputChanges) {
if (inputChanges.incremental) {
// 增量构建:只处理变化的文件
inputChanges.getFileChanges(inputDir).each { change ->
if (change.changeType == ChangeType.ADDED ||
change.changeType == ChangeType.MODIFIED) {
processFile(change.file)
} else if (change.changeType == ChangeType.DELETED) {
deleteOutputFile(change.file)
}
}
} else {
// 全量构建
inputDir.get().asFileTree.each { file ->
processFile(file)
}
}
}
private void processFile(File file) {
def output = new File(outputDir.get().asFile,
inputDir.get().asFile.toURI().relativize(file.toURI()).toString())
output.text = file.text.replace('@version@', version.get())
}
private void deleteOutputFile(File inputFile) {
def output = new File(outputDir.get().asFile,
inputDir.get().asFile.toURI().relativize(inputFile.toURI()).toString())
output.delete()
}
}
// 注册任务
tasks.register('processResources', ProcessResourcesTask) {
inputDir = file('src/main/resources')
outputDir = layout.buildDirectory.dir('processed-resources')
version = project.version
}
构建缓存配置
groovy
// gradle.properties
org.gradle.caching=true
org.gradle.parallel=true
org.gradle.configureondemand=true
// settings.gradle
buildCache {
local {
directory = new File(rootDir, 'build-cache')
removeUnusedEntriesAfterDays = 30
}
remote(HttpBuildCache) {
url = 'https://cache.example.com/'
push = System.getenv('CI') == 'true'
credentials {
username = System.getenv('CACHE_USER')
password = System.getenv('CACHE_PASS')
}
}
}
三、可运行Groovy代码示例
完整的Spring Boot项目配置
groovy
// build.gradle
plugins {
id 'java'
id 'org.springframework.boot' version '3.2.0'
id 'io.spring.dependency-management' version '1.1.4'
}
group = 'com.example'
version = '1.0.0'
java {
sourceCompatibility = '17'
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
maven { url 'https://maven.aliyun.com/repository/public' }
mavenCentral()
}
ext {
mybatisPlusVersion = '3.5.5'
hutoolVersion = '5.8.24'
}
dependencies {
// Spring Boot
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-cache'
// MyBatis Plus
implementation "com.baomidou:mybatis-plus-boot-starter:${mybatisPlusVersion}"
// 工具库
implementation "cn.hutool:hutool-all:${hutoolVersion}"
// MySQL
runtimeOnly 'com.mysql:mysql-connector-j'
// Lombok
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
// 配置处理器
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
// 测试
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
}
test {
useJUnitPlatform()
// 测试报告
reports {
html.required = true
junitXml.required = true
}
}
// 打包配置
bootJar {
archiveFileName = "${project.name}.jar"
// 排除依赖
excludes = ['org.projectlombok:lombok']
}
// 开发工具
if (hasProperty('buildScan')) {
buildScan {
termsOfServiceUrl = 'https://gradle.com/terms-of-service'
termsOfServiceAgree = 'yes'
}
}
自定义Gradle任务
groovy
// ==================== 代码生成任务 ====================
tasks.register('generateCode') {
group = 'build'
description = 'Generate source code'
def outputDir = layout.buildDirectory.dir('generated-sources/main')
outputs.dir(outputDir)
doLast {
def srcDir = new File(outputDir.get().asFile, 'com/example/gen')
srcDir.mkdirs()
def sourceFile = new File(srcDir, 'Constants.java')
sourceFile.text = """
package com.example.gen;
public class Constants {
public static final String APP_NAME = "${project.name}";
public static final String VERSION = "${project.version}";
public static final String BUILD_TIME = "${new Date().format('yyyy-MM-dd HH:mm:ss')}";
}
"""
println "Generated: ${sourceFile}"
}
}
// 添加生成的源码目录
sourceSets.main.java.srcDir layout.buildDirectory.dir('generated-sources/main')
// 编译前生成代码
tasks.named('compileJava') {
dependsOn 'generateCode'
}
// ==================== Docker构建任务 ====================
tasks.register('buildDocker', Exec) {
group = 'docker'
description = 'Build Docker image'
dependsOn 'bootJar'
commandLine 'docker', 'build', '-t',
"${project.name}:${project.version}", '.'
}
// ==================== 版本更新任务 ====================
tasks.register('updateVersion') {
group = 'version'
description = 'Update project version'
doLast {
def newVersion = project.findProperty('newVersion')
if (!newVersion) {
throw new GradleException('Please specify newVersion: -PnewVersion=x.x.x')
}
def buildFile = project.file('build.gradle')
def content = buildFile.text
content = content.replaceFirst(
/version\s*=\s*['"][^'"]+['"]/,
"version = '${newVersion}'"
)
buildFile.text = content
println "Version updated to ${newVersion}"
}
}
四、实战应用场景
场景一:Spring Cloud多模块项目
groovy
// 根项目 build.gradle
plugins {
id 'java'
id 'org.springframework.boot' version '3.2.0' apply false
id 'io.spring.dependency-management' version '1.1.4' apply false
}
ext {
springCloudVersion = '2023.0.0'
}
allprojects {
group = 'com.example.cloud'
version = '1.0.0'
repositories {
maven { url 'https://maven.aliyun.com/repository/public' }
mavenCentral()
}
}
subprojects {
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
java {
sourceCompatibility = JavaVersion.VERSION_17
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}
dependencies {
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
}
// 微服务配置
project(':gateway') {
dependencies {
implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
}
}
project(':user-service') {
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery'
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
}
}
project(':order-service') {
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery'
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
runtimeOnly 'com.mysql:mysql-connector-j'
}
}
五、总结与最佳实践
5.1 Gradle vs Maven对比
| 特性 | Gradle | Maven |
|---|---|---|
| 构建脚本 | Groovy/Kotlin DSL | XML |
| 灵活性 | 极高 | 中等 |
| 学习曲线 | 较陡 | 平缓 |
| 增量构建 | 支持 | 不支持 |
| 构建缓存 | 支持 | 不支持 |
| 并行执行 | 原生支持 | 需插件 |
| Android支持 | 官方首选 | 不支持 |
5.2 性能优化建议
properties
# gradle.properties
org.gradle.daemon=true
org.gradle.parallel=true
org.gradle.caching=true
org.gradle.configureondemand=true
org.gradle.jvmargs=-Xmx2g -XX:+UseParallelGC
5.3 常用命令速查
bash
# 清理构建
gradle clean
# 编译
gradle compileJava
# 运行测试
gradle test
# 打包
gradle build
# 运行应用
gradle bootRun
# 查看依赖树
gradle dependencies
# 查看项目属性
gradle properties
# 执行指定任务
gradle <taskName>
# 构建缓存
gradle build --build-cache
# 并行构建
gradle build --parallel
# 离线模式
gradle build --offline
参考资料
- 《Gradle实战》
- Gradle官方文档:docs.gradle.org/
- Spring Boot Gradle Plugin文档
- 《Gradle权威指南》
六、思考与练习
思考题
- 基础题:Gradle中implementation、api、compileOnly三种依赖配置有什么区别?分别适用于什么场景?
- 进阶题:Gradle的增量构建是如何实现的?任务输入输出的声明对增量构建有什么影响?
- 实战题:对比Gradle和Maven在构建脚本可读性、灵活性、性能方面的差异,分析各自适合的项目场景。
编程练习
练习:创建一个Spring Boot多模块Gradle项目,使用version catalog(libs.versions.toml)管理依赖版本,配置构建缓存实现跨机器构建复用。
章节关联
- 前置章节:Maven核心详解
- 后续章节:依赖冲突解决
- 扩展阅读:《Gradle实战》、Gradle官方文档
📝 下一章预告
在实际项目中,依赖冲突是最常见的问题之一。下一章将系统讲解依赖冲突的排查方法和解决策略,让你从容应对"jar包地狱"的困扰。
本章完