Gradle核心概念与快速上手

文章目录

安装下载

可以直接从腾讯云下载镜像下载

关于软件版本是一个非常有意思的事情,有兴趣可以看看:理解软件版本标识含义与版本号语义

从gradle的distribution可以看出来,更新非常快,基本上两周一个patch版本,两三个月一个minor版本,一年一个major版本。

  1. gradle-xxx-all.zip:包含源码、文档、示例代码、编译好的可执行文件
  2. gradle-xxx-bin.zip:只包含可执行文件
  3. gradle-xxx-src.zip:如果想要自己编译项目可以使用这个,all中只是源码文件,不是工程文件

一般现在bin版本就可以,不要选all,不然解压都要等很久。

下载好之后直接解压到指定目录就可以:

GRADLE_USER_HOME

首先,我们先来了解一下GRADLE_USER_HOME这个非常重要的目录:

GRADLE_USER_HOME默认在用户目录下的.gradle目录。

Windows:C:\Users\<user_name>\.gradle\

Linux:$HOME/.gradle

我们可以通过GRADLE_USER_HOME环境变量来自定义到指定的目录。

  1. .tmp:临时文件目录,存储构建过程中下载依赖、执行任务时的中间数据:临时jar包、插件解压文件等
  2. build-scan-data:Build Scan缓存数据,记录构建过程的性能、依赖等信息
  3. caches:全局缓存,本地仓库(本地缓存jar包)就在这个目录下,还有插件、构建任务中间缓存等
  4. daemon:Gradle 守护进程的日志和状态文件
  5. jdks:存储 Gradle 通过工具链(Toolchain) 自动下载的JDK
  6. kotlin-profile:Kotlin插件的配置、编译缓存
  7. native:存储平台相关的原生库(如 C/C++ 编译后的.so/.dll文件),适用于 NDK 或 Gradle Native 项目
  8. notifications:存储构建过程中的通知信息,如构建结果、警告的缓存
  9. workers:存储 Gradle 工作线程(Worker API)的元数据、临时文件(用于并行任务执行)
  10. wrapper:存储 Gradle Wrapper 自动下载的 Gradle 发行版(位于wrapper/dists目录下)

重点关注有3个:

  1. caches:Gradle的本地仓库就在caches\modules-2\files-2.1,为什么放在caches下,因为本地仓库其实就相当于本地缓存的jar包
  2. jdks:项目多了,每个项目可能依赖于不同版本的jdk,甚至是不同厂商的jdk,Gradle提供了插件,可以自动下载jdk,下载的jdk就放在这个目录
  3. wrapper:因为Gradle迭代非常快,不同的Major版本很多API不兼容,所以不同项目可能使用不同的Gradle版本,Gradle Wrapper自动下载的Gradle默认就在这个目录下

gradle wrapper

什么是Gradle Wrapper以及为什么需要它

因为gradle的升级非常快,所以不同的版本差异可能会很大,各种项目的版本都需要兼容,所以很难

不像maven,同一个项目(pom.xml)不同的人使用不同版本的maven基本不会有太大问题,如果遇到问题,基本往高版本升级基本不会有太大问题,比较maven现在major版本也才3。

但是gradle的major版本已经到9了,major版本不需要做向下兼容,所以升级major版本的时候一定要小心。

就算是相同的major版本,gradle项目,同一个项目,相同build.gradle配置文件,使用不同的版本很可能会出问题。

例如:

txt 复制代码
Build file 'E:\app\me\gradle\learn-gradle\build.gradle' line: 3

An exception occurred applying plugin request [id: 'org.springframework.boot', version: '4.0.0']
> Failed to apply plugin 'org.springframework.boot'.
   > Spring Boot plugin requires Gradle 8.x (8.14 or later) or 9.x. The current version is Gradle 8.5

所以,通常在gradle项目中会使用一个gradle wrapper的东西,主要就是为项目指定gradle版本,这样当其他人拿到相同的项目去执行构建的时候,就自动去下载对应版本的gradle。

通过gradle wrapper的方式,开发就不用自己去下载管理各种不同的Gradle版本了。

Gradle Wrapper配置(gradle-wrapper.properties)

gradle-wrapper.jar非常小只有几十KB,主要是用来下载gradle的。

如果项目已经使用了gradle-wrapper,那也可以通过配置gradle-wrapper.properties来修改为本地gradle。

gradle-wrapper.properties

properties 复制代码
# 可以直接使用url,默认的就是从官方下载,网络好的,直接用默认的就行
# distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip
# 可以配置腾讯云的镜像地址
# distributionUrl=https://mirrors.cloud.tencent.com/gradle/gradle-9.2.1-bin.zip
# 也可以下载到本地,配置为本地地址,注意是zip文件,不是目录,Gradle会自动拷贝解压这个文件
distributionUrl=file:///D:/ptool/gradle/gradle-9.2.0-bin.zip

networkTimeout=10000
validateDistributionUrl=true

# 下载的解压的Gradle放到什么地方
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
# 下载的Gradle zip文件放在什么地方
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

wrapper工具的主类是GradleWrapperMain,gradle_user_home查找逻辑在GradleUserHomeLookup,具体实现在wrapper子项目中的PathAssembler类。

网速不好就尽量用本地安装包吧,它没有端点续传的功能,每次出现Read timed out之类的错误,都得从头开始下载。

gradlew脚本命令

gradle与gradlew的关系:有点像npm和pnpm,gradlew是gradle的包装,主要是处理了路径问题,使用指定版本的gradle而已。

在项目中,基本我们都会使用wrapper模式,通常我们使用的命令是gradlew,而不是gradle,因为gradle通常是全局统一配置的gradle,gradlew是项目的gradle。

下面的命令是gradlew的基本命令,如果没有使用wrapper,替换为gradle就可以。

基本命令

sh 复制代码
# 查看本地 Gradle 安装版本
gradlew -v
gradlew --version

# 启动守护进程
gradlew --daemon
# 禁用守护进程运行任务
gradlew --no-daemon
# 停止所有守护进程
gradlew --stop

任务

sh 复制代码
# 查看基础任务
gradlew tasks
# 查看所有任务(包括子项目和隐藏任务)
gradlew tasks --all  
gradlew help --task <任务名>
# 查看 build 任务的详情
gradlew help --task build
gradlew help --task assemble
# 编译Java代码
gradlew compileJava
# 仅运行测试和校验:执行代码检查Checkstyle、单元测试、集成测试等校验任务,不打包
gradlew check

# 删除项目的build目录:构建产物、编译缓存等
gradlew clean

依赖*

依赖非常重要,我们需要解决的绝大多数问题都是依赖问题。

所以在看依赖命令之前,我们先来简单的解释一下依赖:

  • implementation:编译和运行都需要
  • api:主要用于库或者框架项目
  • runtimeOnly:编译时需要,如JDBC驱动、日志实现(如logback-classic,编译时只需要日志接口slf4j-api,运行时才需要实现)
  • testImplementation:测试代码编译 + 运行阶段,如JUnit
  • testRuntimeOnly:测试运行时

api我们使用的比较少,但是在开源项目和框架中,我们能大量看到它的身影。

api和implementation最大的不同是,api具有传递性。

什么意思呢?

举个例子,我有一个库项目A:

groovy 复制代码
dependencies {
    api 'com.alibaba:fastjson:2.0.42'

    implementation 'com.google.guava:guava:32.1.3-jre'
}

现在有一个项目B,依赖了A:B->A:

B项目是可以直接使用com.alibaba:fastjson:2.0.42,不需要单独在B项目中引入fastjson。

但是B项目如果要使用com.google.guava:guava:32.1.3-jre,就必须在项目中单独在引入guava。

如果B项目没有在B项目中引入guava,而使用了它,就会出现Class Not Found错误。

什么时候使用api呢?库、框架暴露的接口中包含了依赖的库,例如接口中返回值或者参数使用了JSON,这样依赖的库肯定也需要fastjson,这样就可以使用api。

sh 复制代码
# 查看所有依赖
gradlew dependencies

# 查看指定配置的依赖(推荐,缩小范围)
gradlew dependencies --configuration <配置名>
# 查看指定期依赖(compileClasspath、runtimeClasspath)
gradlew dependencies --configuration compileClasspath
# 查看运行时依赖
gradlew dependencies --configuration runtimeClasspath
# 子项目可以加:子项目名称作为前缀
gradlew :app:dependencies --configuration runtimeClasspath

gradlew dependencyInsight --dependency <依赖名> --configuration <配置名>
# 查看 guava 依赖的详细信息(版本、来源、冲突解决)
# 配置名:compileClasspath、runtimeClasspath、implementation、testCompileClasspath、testRuntimeClasspath、testImplementation
gradlew dependencyInsight --dependency guava --configuration implementation
gradlew :app:dependencyInsight --dependency guava --configuration compileClasspath
# 锁定依赖版本
gradlew dependencies --write-locks

我们可以看到相关内容还是非常详细的。

Gradle默认处理依赖冲突的策略是:选择最高版本,所以,我们看到最终选择了更好版本的guava包。

当然可以指定强制版本:

groovy 复制代码
configurations {
    compileClasspath {
        resolutionStrategy {
            force 'com.google.guava:guava:33.4.6-jre'
        }
    }
}

也可以排除依赖:

groovy 复制代码
dependencies {
    implementation('com.google.guava:guava:33.5.0-jre') {
        exclude group: 'com.google.guava', module: 'listenablefuture'
    }
}

--write-locks是个什么东西呢?因为Gradle支持类似npm的动态版本,不过简化一些:

groovy 复制代码
dependencies {
    // 1. 通配符:匹配 2.15.x 系列的最新版本
    implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.+'
    // 2. 区间范围:匹配 1.0.0 到 2.0.0 之间的最新版本(左闭右开)
    implementation 'org.apache.commons:commons-lang3:[1.0.0, 2.0.0)'
    // 3. 关键字:匹配最新正式版(不推荐)
    implementation 'com.google.guava:guava:latest.release'
}

有动态版本,自然需要像npm的lock文件,解决不同人拉的库是相同的版本。

为什么需要动态版本呢?

因为我们通常需要自动去依赖patch版本,就是做了bug修复的版本,特别是一些重大漏洞。

但是我们很少随时去关注依赖的包做了哪些更新,复杂的项目依赖成千上万的包,要了解每个依赖包的更新,那啥事都别干了。

怎么办呢?答案就是动态版本。

我们再开发的时候,就可以使用implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.+'这一的版本。

这样,jackson做了patch更新的时候,会修改他们的patch版本号,Gradle就会自动拉取最新的patch版本。

因为,如果jackson遵照规范,新patch版本肯定是API兼容的,所以,我们也不用担心会出现不兼容的情况。

这样,我们不用去随时关系jackson做了什么bug修复、漏洞修复,因为我们使用的是最新的patch版本。

关于软件版本可以参考:理解软件版本标识含义与版本号语义

测试

sh 复制代码
# 运行所有测试
gradlew test

# 运行单个测试类
gradlew test --tests vip.oschool.MyTestClass

# 运行单个测试方法
gradlew test --tests vip.oschool.MyTestClass.myTestMethod

# 通配符匹配
gradlew test --tests "vip.oschool.*"  # 匹配包下所有测试
gradlew test --tests "*MyTestClass*"  # 匹配类名包含的测试

构建

sh 复制代码
# 构建项目
gradlew build
# 先清理,再完整构建
gradlew clean build
# -x 表示排除指定任务
gradlew build -x test
# 刷新依赖
gradlew build --refresh-dependencies
gradlew build --parallel
# 手动更新锁定版本,有动态版本的时候
gradlew build --update-locks
# 离线模式,通常用于测试
gradlew build --offline

注意一下--refresh-dependencies参数,这个是去强制刷新snapshot版本和动态版本依赖,不是去删除所有依赖重新下载。

通常我们不会使用snapshot版本,很多公司也不会使用动态版本,所以--refresh-dependencies很多时候没有必要。

安装发布包

sh 复制代码
# 将产物安装到本地 Maven 仓库
gradlew install
# 发布到仓库
gradlew publish

scan

构建扫描,全方位分析构建过程,并生成报告,会上传到Gradle云端,公司项目慎用。

sh 复制代码
# 普通构建并生成扫描报告
gradlew build --scan

# 对指定子项目执行任务并生成扫描
gradlew :app:build --scan

# 依赖分析并生成扫描
gradlew :app:dependencies --scan

工具链配置

工具链主要用来指定JDK版本的,Gradle脚本中可以为不同的项目、插件、甚至任务指定不同的JDK版本。

不同版本怎么管理切换呢?

答案是:toolchain

groovy 复制代码
// 自定义使用版本
java {
    toolchain {
        languageVersion.set(JavaLanguageVersion.of(17))
        vendor = JvmVendorSpec.AMAZON
    }
}

常用的vendor:ADOPTIUM、AZUL、AMAZON、MICROSOFT

Gradle会自动探测JDK,顺序如下:

  1. 系统属性
  2. 环境变量 JAVA_HOME
  3. GRADLE_USER_HOME/.gradle/jdks/
  4. /usr/lib/jvm/
  5. C:\Program Files\Java\ (Windows)
sh 复制代码
# 查看可用工具链
gradlew -q javaToolchains

javaToolchains

Adoptium-jdk-华中科技

Adoptium-jdk-清华

openjdk

injdk

自动下载JDK插件

注意Gradle插件分为两类:

  1. Settings 插件:仅作用于构建初始化阶段,必须在settings.gradle(.kts)中应用
  2. Project 插件:作用于具体项目,在build.gradle(.kts)中应用

例如我们自动下载JDK的插件,可以通过settings.gradle配置:

groovy 复制代码
pluginManagement {
    plugins {
        id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0'
    }
}

// 根据java.toolchain配置自动下载JDK
plugins {
    id 'org.gradle.toolchains.foojay-resolver-convention'
}

网速不行,自动下载基本就没不要,但是可以通过Gradle属性org.gradle.java.installations.paths指定本地位置。

实测,网速还是非常快啊,比很多时候自己下都快:

下载完有4个文件:

Gradle各种配置文件

Gradle属性配置文件(gradle.properties)

和SpringBoot一样可以指定application.properties一样,gradle也可以配置gradle.properties

有很多地方可以放,优先级从高到低;

  1. 命令行参数
  2. 项目根目录的gradle.properties(如果有子项目,子项目自己的优先级更高)
  3. GRADLE_USER_HOME目录(可-Dgradle.user.home修改)下gradle.properties(默认位置:用户目录/.gradle/gradle.properties)
  4. 环境变量(比较麻烦,通常使用比较少)

属性文件还是比较有用,例如我们自己的本地的JDK,怎么告诉Gradle呢,就可以通过org.gradle.java.installations.paths属性配置

又比如说,我们因为仓库需要使用代理,那么我们就可以通过属性文件配置。

因为这些不是项目配置,是我们自己比较个性化的配置,所以就可以单独建一个gradle.properties文件,放在GRADLE_USER_HOME目录下。

这样就不会影响项目的配置,并且我们本地的所有Gradle的项目都可以共享这些配置。

properties 复制代码
org.gradle.java.installations.paths=D:/Env/JDK/openjdk16,D:/Env/JDK/openjdk17,D:/Env/JDK/openjdk19,D:/Env/JDK/openjdk21

systemProp.http.proxyHost=198.185.120.100
systemProp.http.proxyPort=7000
systemProp.http.proxyUser=tim
systemProp.http.proxyPassword=123456

systemProp.https.proxyHost=198.185.120.100
systemProp.https.proxyPort=7000
systemProp.https.proxyUser=tim
systemProp.https.proxyPassword=123456

# -Dorg.gradle.jvmargs=-Xmx2048m
# org.gradle.jvmargs=-Xmx512m

命令行的配置优先级最高,通常是某一次运行的特殊配置,比如:

sh 复制代码
gradle -Dorg.gradle.java.installations.paths=D:/Env/JDK/openjdk17 -Pgradle.user.home=F:/custom/home

我们再build.gradle脚本中,可以通过类似下面的方式直接获取属性值:

groovy 复制代码
println("test.gradle.c=" + project.getProperty("test.gradle.c"))
System.properties.each { key, value ->
    if (key.startsWith('http.proxy') || key.startsWith('https.proxy')) {
        println "  $key = $value"
    }
}

Gradle构建环境

Gradle所有初始化脚本、构建脚本及其优先级

除了扩展名为.properties的属性文件,Gradle还有扩展名是.gradle构建脚本,最常见的就是项目根目录下的build.gradle文件。

除了项目下的build.gradle文件,Gradle有一些初始化脚本。

  1. 在命令行指定文件,例如:gradle --init-script F:/tmp/init.gradle -q taskName
  2. GRADLE_USER_HOME目录下的init.gradle文件
  3. GRADLE_USER_HOME目录下的init.d目录下的*.init.gradle(所有以.init.gradle结尾的文件)
  4. GRADLE_HOME目录下的init.d目录下的*.init.gradle(所有以.init.gradle结尾的文件)

注意:

  1. 如果使用的是kotlin,指的是包含.kts后缀的文件,例如:build.gradle.kts
  2. GRADLE_USER_HOME目录下文件名必须是:init.gradle,其子目录init.d下的只需要以.init.gradle结尾即可
  3. 通常我们使用GRADLE_USER_HOME,而不是GRADLE_HOME

初始化脚本,可以用来放我们自己的一些工具脚本。

例如前面我们放在properties属性文件中的org.gradle.java.installations.paths,也可以通过init.gradle初始化脚本来设置:

groovy 复制代码
System.setProperty('org.gradle.java.installations.paths', 
    'E:/language/java/openjdk17,E:/language/java/openjdk21,E:/language/java/openjdk22,E:/language/java/openjdk23,E:/language/java/openjdk24,E:/language/java/openjdk25')

代理也可以:

groovy 复制代码
System.properties.putAll([
    'http.proxyHost': 'proxy.company.com',
    'http.proxyPort': '8080',
    'https.proxyHost': 'proxy.company.com',
    'https.proxyPort': '8080',
])

统一的仓库管理,这样避免在每个项目中再单独去配置仓库地址。

groovy 复制代码
allprojects {
    buildscript {
        repositories {
            maven { url 'https://maven.aliyun.com/repository/public' }
            maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
            gradlePluginPortal()
        }
    }
    
    repositories {
        maven { url 'https://maven.aliyun.com/repository/public' }
        mavenCentral()
    }
}

建议:本地jDK、仓库地址、代理地址、插件仓库地址、jvm都统一配置,不然,每次导入项目的时候,都要去配置一遍。

最关键的问题是,想IDEA这些集成环境导入项目就自动开始下载了,如果没有提前配置好,就使用了默认的配置了。

Gradle初始化脚本

gradle在IDEA配置

IDEA对Gradle的支持比较好,我们来简单说一下几个重要的问题:

创建项目选Gradle就可以:

也可以通过命令行初始化,按照步骤走就可以:

sh 复制代码
gradle init

理解build.gradle配置原理

build.gradle配置文件其实是一个脚本文件,使用的是Groovy或者Kotlin语言。

只是Gradle相当于提供了很多工具,例如:build.gradle你就可以直接使用preoject等对象。

要想清楚Gradle配置的原理,只需要理解Groovy的闭包。

下面就是一个简单的示例,可以直接放在build.gradle文件中,刷新就能执行。

groovy 复制代码
class ServerConfig {
    String host
    int port
    List<String> plugins = []

    def host(String host) { this.host = host }
    def port(int port) { this.port = port }
    def plugin(String plugin) { plugins << plugin }
}

class ConfigBuilder {
    ServerConfig serverConfig = new ServerConfig()

    def server(@DelegatesTo(ServerConfig) Closure closure) {
        closure.delegate = serverConfig
        // 执行闭包中的方法,闭包中没有的时候,先去查找闭包的delegate,这里就是serverConfig
        closure.resolveStrategy = Closure.DELEGATE_FIRST
        closure()
        // Groovy默认最后一行是作为返回值,所以server方法返回了ServerConfig
        serverConfig
    }
}

// 创建ConfigBuilder对象,并调用server方法
// server后面的{}就是闭包,就是一个代码块,被编译为Closure传给server方法
// server中closure()就相当于调用{}代码块
// 因为代码块中没有 host方法,就调用代理serverConfig的host方法
def config = new ConfigBuilder().server {
    // 调用host方法,闭包本身没有,先去查找闭包的delegate,就是ServerConfig的host方法
    host "localhost"
    // 同理,这里相当于调用ServerConfig的port方法
    port 8080
    plugin "security"
    plugin "monitoring"
}

println config.host
println config.port
println config.plugins

不熟悉Groovy没有关系,如果你会Java,Groovy就非常简单,Groovy就是Java的增强、脚本化。

有兴趣可以看一下下面2篇文章你就能够快速上手Groovy:

快速上手Groovy

深入理解Groovy

资源

gradle官网

gradle services

gradle distributions

war
task

gradle Sync

下载镜像

清华大学镜像

华中科技大学镜像

腾讯云下载镜像

gradle下载

gradle github

相关推荐
黄林晴12 小时前
警惕!AGP 9.2 别只改版本号,R8 规则与构建链路全线收紧
android·gradle
followYouself7 天前
Gradle、AGP、Plugin插件基本知识
android·gradle·plugin·agp
千码君201610 天前
Flutter:在win10上第一次安装和尝试开发记录
flutter·gradle·android-studio·安卓模拟器
Ww.xh19 天前
Flutter配置Gradle完整教程
flutter·gradle·android studio
vortex520 天前
Gradle 从入门到实战
java·gradle
蜡台23 天前
Android Studio Gradlew JDK配置
java·gradle·android studio·intellij-idea
spencer_tseng24 天前
java.net.SocketTimeoutException: Connect timed out
gradle
蜡台1 个月前
Android Studio 高版本兼容低版本项目配置
android·ide·jdk·gradle·android studio
狂龙骄子1 个月前
Android Studio下载与版本选择指南
jdk·gradle·android studio·intellij idea·androidsdk·agp·归档版本
阿明的小蝴蝶1 个月前
记一次Gradle环境的编译问题与解决
android·前端·gradle