63-Gradle构建详解

Gradle构建详解

一、知识概述

Gradle是新一代构建自动化工具,结合了Apache Ant的灵活性和Apache Maven的依赖管理能力。Gradle使用Groovy或Kotlin DSL编写构建脚本,具有高度的灵活性和强大的扩展性,是Android和Spring Boot项目的首选构建工具。

本文将深入分析Gradle的核心原理,包括Groovy DSL、任务系统、依赖管理、多项目构建、增量构建等核心内容,并提供实战代码示例。

Gradle的核心优势

  1. 灵活性:基于Groovy/Kotlin的DSL,编写构建逻辑如写代码
  2. 增量构建:智能检测变更,只构建必要的部分
  3. 构建缓存:跨机器复用构建结果
  4. 并行执行:充分利用多核CPU
  5. 生态丰富: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

参考资料

  1. 《Gradle实战》
  2. Gradle官方文档:docs.gradle.org/
  3. Spring Boot Gradle Plugin文档
  4. 《Gradle权威指南》

六、思考与练习

思考题

  1. 基础题:Gradle中implementation、api、compileOnly三种依赖配置有什么区别?分别适用于什么场景?
  2. 进阶题:Gradle的增量构建是如何实现的?任务输入输出的声明对增量构建有什么影响?
  3. 实战题:对比Gradle和Maven在构建脚本可读性、灵活性、性能方面的差异,分析各自适合的项目场景。

编程练习

练习:创建一个Spring Boot多模块Gradle项目,使用version catalog(libs.versions.toml)管理依赖版本,配置构建缓存实现跨机器构建复用。

章节关联

  • 前置章节:Maven核心详解
  • 后续章节:依赖冲突解决
  • 扩展阅读:《Gradle实战》、Gradle官方文档

📝 下一章预告

在实际项目中,依赖冲突是最常见的问题之一。下一章将系统讲解依赖冲突的排查方法和解决策略,让你从容应对"jar包地狱"的困扰。


本章完

相关推荐
Gopher_HBo1 小时前
Go语言学习笔记(三)复杂数据类型channel和自定义结构
后端
Sam_Deep_Thinking1 小时前
一个业务场景只需要一个ThreadLocal实例
java·面试
超梦dasgg1 小时前
Dijkstra(迪杰斯特拉)算法详解
java·数据结构·算法
MacroZheng1 小时前
给Claude Code装上这个超酷的状态栏,瞬间高大上了!
java·人工智能·后端
SimonKing1 小时前
langchain4j进阶:AI记忆与RAG
后端
有梦想的程序星空1 小时前
【环境配置】IDEA+Scala 项目 JAR 打包异常完整排查指南
java·ide·intellij-idea
爱吃大芒果1 小时前
鸿蒙 ArkUI 架构蓝图:MoodLite 的 UI 渲染与数据逻辑解耦实践
ui·架构·harmonyos
小程故事多_801 小时前
从初代架构到大模型时代,英伟达GPU底层架构演进与核心逻辑深度解析
java·人工智能·分布式·架构
dongdonglele5211 小时前
ubuntu 系统x86 架构安装docker,可以使用本地显卡
ubuntu·docker·架构