Gradle
Gradle 的历史和背景是与 Java 社区的构建工具发展紧密相关的。在 Gradle 出现之前,Java 社区主要使用的构建工具是 Apache Ant 和 Apache Maven。
Apache Ant 是一个早期的构建工具,它使用 XML 文件来定义构建任务和依赖关系。Ant 的优点是它非常灵活,可以执行几乎任何类型的任务。然而,Ant 的缺点是它的构建脚本(build.xml)往往非常冗长和复杂,而且缺乏一致的构建模型。
Apache Maven 在 Ant 之后出现,它试图提供一个更加统一的构建生命周期和依赖管理机制。Maven 使用了一个项目对象模型(Project Object Model, POM),以 XML 格式定义在 pom.xml
文件中。Maven 的依赖管理是通过中央仓库和坐标系统来实现的,这使得依赖管理变得更加简单。然而,Maven 的缺点是它相对不够灵活,有时候很难执行一些特殊的构建任务。
Gradle 在 2007 年由 Adam Murdoch 和 Hans Dockter 创造,它试图结合 Ant 和 Maven 的优点,提供一个既灵活又具有统一构建生命周期的构建工具。Gradle 使用 Groovy 语言来编写构建脚本,这极大地提高了脚本的灵活性和可读性。Gradle 还引入了增量构建的概念,这意味着只有那些自上次构建以来发生变化的部分会被重新构建,从而大大提高了构建速度。
Gradle 的第一个正式版本是在 2012 年发布的,从那时起,它迅速获得了 Java 社区的认可。Gradle 的一个重要里程碑是在 2013 年被 Google 选中作为 Android 官方的构建工具,这一决策极大地推动了 Gradle 的普及。
随着时间的推移,Gradle 社区不断成长,许多新的特性和插件被添加进来,以支持更多的语言和框架。2016 年,Gradle 添加了对 Kotlin 编程语言的支持,允许开发者使用 Kotlin DSL 来编写构建脚本,这进一步提高了脚本的类型安全性和工具支持。
设计理念
Gradle 的核心思想是将构建逻辑抽象为任务(tasks),并且通过一种基于 Groovy 的领域特定语言(DSL)来描述这些任务和它们之间的关系。
Gradle 的设计理念包括以下几点:
- 声明式构建模型:Gradle 结合了声明式构建和编程式构建的特点,用户可以通过声明的方式来定义任务依赖和输入输出,同时也可以通过编程的方式来编写复杂的构建逻辑。
- 增量构建:Gradle 能够智能地处理任务,只有当输入输出发生变化时才会重新执行任务,从而大大提高了构建的效率。
- 依赖管理:Gradle 内置了强大的依赖管理功能,支持多种依赖仓库,如 Maven Central、JCenter,并且能够自动处理传递依赖。
- 插件系统:Gradle 的插件系统能够让开发者扩展构建逻辑,创建可重用的构建组件。社区提供了大量的插件,支持不同的构建需求,如 Java、Android、Kotlin 等。
- 多项目构建:Gradle 天生支持多项目构建,能够方便地管理和构建大型项目。
项目(Project)
在 Gradle 中,每个构建都是由一个或多个项目组成的。一个项目代表了一个被构建的组件,比如一个库或者一个应用程序。每个项目都有一个与之对应的 build.gradle
(Groovy DSL)或 build.gradle.kts
(Kotlin DSL)文件,这个文件被称为项目的构建脚本,它定义了项目的构建逻辑、依赖、配置和其他属性。 项目可以包含子项目,形成一个层次结构。在多项目构建中,根项目通常包含子项目的通用配置,而子项目可以继承和覆盖根项目的配置。 项目有以下几个主要特点:
- 生命周期:项目有自己的生命周期,包括初始化、配置和执行阶段。
- 属性:项目可以有各种属性,如名称、版本、描述等,这些属性可以在构建脚本中设置和访问。
- 任务:项目包含一个或多个任务,这些任务是构建过程中的基本工作单元。
- 依赖管理:项目可以声明依赖,Gradle 会自动解析和下载这些依赖。
- 插件:项目可以通过应用插件来扩展其功能,插件可以添加新的任务、依赖配置和属性。
任务(Task)
任务是 Gradle 构建过程中的基本工作单元。它代表了一个构建过程中需要执行的操作,比如编译源代码、运行测试、打包应用程序等。任务可以是简单的,也可以是复杂的,可以由其他任务组成,形成任务之间的依赖关系。 任务有以下几个主要特点:
- 动作:任务包含一个或多个动作,这些动作定义了任务的具体操作。在 Groovy DSL 中,动作是一个闭包;在 Kotlin DSL 中,动作是一个 lambda 表达式。
- 依赖:任务可以有依赖关系,一个任务的执行可以依赖于其他任务的完成。Gradle 会根据任务之间的依赖关系自动确定任务的执行顺序。
- 输入和输出:任务可以有输入和输出。Gradle 使用这些信息来决定任务是否需要被执行。如果任务的输出不存在或者输入自上次任务执行以来发生了变化,Gradle 将执行该任务。
- 类型:任务可以有类型,类型定义了任务的行为和属性。Gradle 提供了许多内置的任务类型,如
Copy
、Delete
、Jar
等,你也可以定义自己的任务类型。 - 属性:任务可以有各种属性,如描述、分组、是否启用等,这些属性可以在构建脚本中设置和访问。
Gradle Wrapper
Gradle Wrapper 是 Gradle 项目的一个特性,它允许你在不需要预先安装 Gradle 的环境下运行 Gradle 任务。这对于项目的构建和分发非常有用,因为它确保了项目能够在任何环境中以相同的方式构建,无论该环境中是否安装了 Gradle,以及安装了哪个版本的 Gradle。
Gradle Wrapper 的工作原理
Gradle Wrapper 实际上是一个脚本,它封装了 Gradle 的调用过程。当你运行 Wrapper 脚本时,它会检查本地是否已经安装了正确版本的 Gradle。如果没有,它会自动下载并使用存放在项目中的 Gradle 版本。这样,每个项目都可以有自己的 Gradle 版本,从而保证了构建的一致性。
Gradle Wrapper 的组成
Gradle Wrapper 由以下几个部分组成:
gradlew
和gradlew.bat
:这两个文件是可执行的脚本,分别用于 Unix/Linux 和 Windows 系统。它们是 Wrapper 的入口点,允许你像执行本地安装的 Gradle 一样执行 Gradle 任务。gradle/wrapper/gradle-wrapper.jar
:这是一个 Java JAR 文件,包含了 Wrapper 运行时所需的逻辑。gradle/wrapper/gradle-wrapper.properties
:这是一个配置文件,它指定了 Wrapper 将要使用的 Gradle 版本,以及下载 Gradle 发行版的 URL。
使用 Gradle Wrapper
要使用 Gradle Wrapper,你只需要在项目的根目录下运行相应的脚本。例如,在 Unix/Linux 系统中,你可以在命令行中执行以下命令:
./gradlew build
在 Windows 系统中,你可以执行以下命令:
gradlew.bat build
这些命令会执行项目的构建过程,如果本地没有安装对应的 Gradle 版本,Wrapper 会自动下载。
配置 Gradle Wrapper
你可以通过编辑 gradle/wrapper/gradle-wrapper.properties
文件来配置 Gradle Wrapper。例如,你可以指定 Gradle 的版本和分布类型(binaries 或 all),以及下载 Gradle 的 URL。
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Gradle Wrapper 的优势
- 一致性:确保了所有开发者、CI/CD 系统和构建服务器使用相同版本的 Gradle。
- 易用性:新加入项目的开发者不需要安装 Gradle,只需使用 Wrapper。
- 隔离性:每个项目可以使用不同版本的 Gradle,不会相互影响。
- 控制性:可以精确控制项目中使用的 Gradle 版本。
Gradle 生命周期
Gradle 的生命周期主要分为三个阶段:初始化阶段(Initialization)、配置阶段(Configuration)和执行阶段(Execution)。每个阶段都有其特定的任务和目的。
初始化阶段
在初始化阶段,Gradle 会确定哪些项目将参与构建,并为每个项目创建一个 Project
实例。这个阶段主要执行以下任务:
- 执行 settings 脚本:Gradle 会首先执行
settings.gradle
(Groovy DSL)或settings.gradle.kts
(Kotlin DSL)脚本,这个脚本定义了哪些项目以及哪些子项目将包含在构建中。 - 创建项目层次结构:基于
settings.gradle
脚本中的配置,Gradle 会创建一个项目层次结构,并为每个项目创建一个Project
实例。 - 评估构建脚本:Gradle 会评估每个项目的
build.gradle
(Groovy DSL)或build.gradle.kts
(Kotlin DSL)脚本,这些脚本定义了项目的构建逻辑、依赖和其他配置。
配置阶段
在配置阶段,Gradle 会处理项目的构建脚本,配置项目和任务的属性,并建立任务之间的依赖关系。这个阶段主要执行以下任务:
- 执行构建脚本:Gradle 会执行每个项目的构建脚本,这些脚本定义了项目的依赖、版本、插件和其他配置。
- 配置任务:Gradle 会配置项目中的所有任务,包括任务的属性、行为和依赖关系。
- 建立任务图:Gradle 会根据任务的依赖关系建立一个有向无环图(DAG),这个图决定了任务执行的顺序。
执行阶段
在执行阶段,Gradle 会根据配置阶段建立的任务图来执行任务。这个阶段主要执行以下任务:
- 执行任务:Gradle 会按照任务图中的依赖关系执行任务。每个任务都会执行其定义的操作,例如编译源代码、运行测试、打包应用程序等。
- 处理任务输出:Gradle 会处理任务的输出,例如生成的文件、报告和其他结果。
- 构建完成:当所有指定的任务都执行完成后,Gradle 会结束构建过程。
Gradle和Maven
Gradle 和 Maven 都是流行的自动化构建工具,它们用于自动化软件项目的构建、依赖管理和项目信息管理。尽管它们有相似的目标,但它们在设计理念、配置方式和性能等方面有所不同。
设计理念和语法
- Maven:Maven 使用 XML 来配置项目,其配置文件名为
pom.xml
。Maven 遵循“约定优于配置”的原则,这意味着它有一套标准的项目结构和生命周期,开发者需要遵循这些标准。 - Gradle:Gradle 使用 Groovy 或 Kotlin 的 DSL 来配置项目,其配置文件名为
build.gradle
(Groovy DSL)或build.gradle.kts
(Kotlin DSL)。Gradle 提供了更多的灵活性和自定义选项,同时支持约定优于配置和显式配置。
性能
- Maven:Maven 的构建过程是基于其严格的声明式生命周期,这可能导致在某些情况下构建速度较慢,尤其是在处理大型项目时。
- Gradle:Gradle 通过其增量构建支持和任务输出的缓存,通常可以提供更快的构建速度。Gradle 还支持并行项目构建,这可以进一步提高性能。
依赖管理
- Maven:Maven 依赖管理是基于项目对象模型(POM)的,它使用中央仓库来解析和下载依赖项。Maven 的依赖管理非常严格,支持传递依赖和依赖范围。
- Gradle:Gradle 也支持依赖管理,并且可以直接使用 Maven 仓库。Gradle 的依赖管理更加灵活,允许更复杂的依赖配置和动态版本解析。
多模块项目
- Maven:Maven 天生支持多模块项目,通过在
pom.xml
文件中声明模块来构建复杂的项目结构。 - Gradle:Gradle 同样支持多模块项目,并且提供了更灵活的模块配置选项,允许更细粒度的控制和更复杂的构建逻辑。
插件和扩展性
- Maven:Maven 有一个丰富的插件生态系统,允许开发者扩展 Maven 的功能。插件是用 Java 编写的,并且可以通过
pom.xml
文件轻松集成。 - Gradle:Gradle 也拥有一个强大的插件系统,支持用 Groovy 或 Kotlin 编写的插件。Gradle 的插件系统更加模块化,使得编写和集成自定义插件更加简单。
社区和支持
- Maven:由于 Maven 已经存在了很长时间,它有一个庞大且成熟的社区。许多 Java 项目和工具都支持 Maven,因此很容易找到相关的资源和文档。
- Gradle:Gradle 虽然相对较新,但它的社区也非常活跃,并且随着时间的推移不断增长。Gradle 的文档和社区支持也在不断改进。
总结
Maven 更适合那些喜欢严格约定和熟悉 XML 配置的开发者。
而 Gradle 提供了更高的灵活性和性能,特别适合需要复杂构建逻辑和高度自定义的项目。
Gradle 使用实战
创建 Gradle Kotlin 项目
使用 gradle init
命令创建一个新的 Gradle 项目时,可以选择使用 Kotlin DSL。这将创建一个包含 build.gradle.kts
文件的项目。
配置项目依赖
在 build.gradle.kts
文件中,使用 dependencies
块来配置项目的依赖。例如,对于一个 Kotlin 项目,你可以添加如下的依赖:
查看代码
dependencies {
// 添加编译时和运行时依赖
implementation("com.google.guava:guava:30.1-jre")
// 添加仅编译时依赖
compileOnly("javax.annotation:javax.annotation-api:1.3.2")
// 添加测试时依赖
testImplementation("junit:junit:4.13.2")
// 添加运行时依赖
runtimeOnly("com.fasterxml.jackson.core:jackson-databind:2.12.3")
// 使用库声明来配置依赖,例如排除传递依赖
implementation("org.springframework:spring-web:5.3.9") {
exclude(group = "org.springframework", module = "spring-core")
}
}
依赖的语法都包括依赖的配置名称(如 implementation
、testImplementation
等)和依赖坐标,依赖坐标由组(group)、名称(module)和版本(version)组成。在 Kotlin DSL 中,依赖坐标通常用字符串字面量表示,并用双引号括起来。
定义任务
在 build.gradle.kts
文件中,你可以使用 task
关键字来定义自定义任务。例如,创建一个任务来复制文件:
tasks.register<Copy>("copyFiles") {
from("src/main/resources")
into("build/resources/main")
}
使用插件
Gradle 支持两种类型的插件:
- 脚本插件:脚本插件是一个 Gradle 脚本,它可以被其他构建脚本应用。脚本插件通常用于共享构建逻辑 across multiple projects。
- 二进制插件:二进制插件是打包为 JAR 文件或包含在 Gradle 插件门户中的插件。它们可以通过
plugins
块或buildscript
dependencies 中的类路径来应用。
使用 plugins
块来应用插件
plugins {
kotlin("jvm") version "1.5.31"
}
这里,kotlin("jvm")
是 Kotlin 插件的 ID,version "1.5.31"
指定了插件的版本。Gradle 会自动从官方插件门户下载并应用这个插件。
使用 buildscript
应用插件
对于不在官方插件门户中的插件,或者当你需要更细粒度的控制时,你可以使用 buildscript
块来应用插件。例如:
查看代码
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.example:custom-plugin:1.0.0'
}
}
apply plugin: 'com.example.custom-plugin'
在这个例子中,我们首先在 buildscript
块中添加了 Maven Central 仓库和插件的依赖。然后,我们使用 apply plugin: 'com.example.custom-plugin'
来应用插件。
配置构建环境
在 build.gradle.kts
文件中配置项目的版本、源代码目录、编译选项等。例如,设置 Kotlin 项目的版本和源代码目录:
version = "1.0-SNAPSHOT"
sourceSets {
main {
kotlin {
srcDirs("src")
}
}
}
优化构建性能
使用 Gradle 的构建缓存来重用之前的构建结果。
构建缓存
Gradle 的构建缓存是一个优化特性,它可以显著减少构建时间,特别是在多台机器或持续集成环境中。构建缓存允许 Gradle 重用之前构建的结果,从而避免重复执行那些输入没有变化的任务。
构建缓存的工作原理
当 Gradle 执行一个任务时,它会计算一个输入哈希值,这个哈希值基于任务的输入(如源文件、资源、依赖等)。如果任务的输入哈希值与之前构建的哈希值相同,Gradle 会认为任务的结果是可缓存的,并且会从缓存中检索任务的输出,而不是重新执行任务。
启用构建缓存
要启用构建缓存,你可以在 gradle.properties
文件中添加以下配置:
org.gradle.caching=true
或者在命令行中添加 --build-cache
选项:
gradle build --build-cache
构建缓存的类型
Gradle 支持两种类型的构建缓存:
- 本地构建缓存:本地构建缓存存储在开发者机器上,它是默认启用的。它可以加速本地构建,特别是对于需要频繁重建的项目。
- 远程构建缓存:远程构建缓存存储在一个中央服务器上,可以被团队中的多个开发者共享。这可以加速持续集成和持续部署流程,因为它允许构建服务器重用之前构建的结果。
配置远程构建缓存
要配置远程构建缓存,你需要在 build.gradle
文件中添加远程缓存仓库的配置:
buildCache {
remote(HttpBuildCache) {
url = 'https://example.com/cache/'
// 配置认证等
}
}
部署和发布
部署和发布通常通过插件来完成,这些插件提供了与各种仓库和平台的集成。
使用 Maven Publish 插件发布到 Maven 仓库
Maven Publish 插件是 Gradle 的一个官方插件,它允许你将项目构件发布到 Maven 仓库。要使用这个插件,你首先需要在 build.gradle
或 build.gradle.kts
文件中应用它:
plugins {
id 'maven-publish'
}
或者在 Kotlin DSL 中:
plugins {
`maven-publish`
}
然后,你可以配置要发布的构件和仓库信息:
查看代码
publishing {
publications {
myPublication(MavenPublication) {
from components.java
artifactId = 'my-artifact'
version = '1.0.0'
}
}
repositories {
maven {
url = 'https://my-repository.com/repo'
// 配置认证等
}
}
}
在 Kotlin DSL 中:
查看代码
publishing {
publications {
create<MavenPublication>("myPublication") {
from(components["java"])
artifactId = "my-artifact"
version = "1.0.0"
}
}
repositories {
maven {
url = uri("https://my-repository.com/repo")
// 配置认证等
}
}
}
配置完成后,你可以使用 publish
任务来发布构件:
gradle publish
使用 Ivy Publish 插件发布到 Ivy 仓库
Ivy Publish 插件与 Maven Publish 插件类似,但它用于发布到 Ivy 仓库。要使用这个插件,你需要在构建脚本中应用它并配置发布信息:
plugins {
id 'ivy-publish'
}
然后在 publishing
块中配置 Ivy 发布细节:
查看代码
publishing {
publications {
ivy(IvyPublication) {
// 配置 Ivy 发布信息
}
}
repositories {
ivy {
// 配置 Ivy 仓库信息
}
}
}
发布时,同样使用 publish
任务:
gradle publish
部署到服务器
除了发布到仓库,你可能还需要将应用程序部署到服务器。这通常涉及到将应用程序打包成可执行的格式(如 JAR、WAR 或 ZIP 文件),然后使用 SSH、FTP 或其他方法将打包后的文件上传到服务器。 Gradle 提供了多种方式来执行部署任务,例如使用 exec
任务来运行 SSH 命令,或者使用第三方插件来简化部署过程。例如,可以使用 Gradle SSH 插件来通过 SSH 上传文件:
plugins {
id 'org.gradle.plugins.ssh'
}
然后配置 SSH 信息和上传任务:
ssh.run(delegateClosureOf<SSHServer> {
host = 'my-server.com'
user = 'deploy'
password = 'password'
put(file('build/libs/my-app.jar'), '/path/to/server/directory')
}
在 Kotlin DSL 中:
tasks.register<SSHExec>("deploy") {
host = "my-server.com"
user = "deploy"
password = "password"
command = "cp /path/to/server/directory/my-app.jar /path/to/server/directory/my-app.jar.old && " +
"mv /path/to/server/directory/my-app.jar.new /path/to/server/directory/my-app.jar"
}
然后执行部署任务:
gradle deploy