在 Grade 中,我们常见的几个关键术语有 Project、Plugin 以及 Task。和 Maven 一样,Gradle 只是提供了构建项目的一个框架,真正起作用的是 Plugin。Gradle 在默认情况下为我们提供了许多常用的 Plugin,其中包括有构建 Java 项目的 Plugin,还有 War,Ear 等。与 Maven 不同的是,Gradle 不提供内建的项目生命周期管理,只是 Java Plugin 向 Project 中添加了许多 Task,这些 Task 依次执行,为我们营造了一种如同 Maven 般项目构建周期。换言之,Project 为 Task 提供了执行上下文,所有的 Plugin 要么向 Project 中添加用于配置的 Property,要么向 Project 中添加不同的 Task。一个 Task 表示一个逻辑上较为独立的执行过程,比如编译 Java 源代码,拷贝文件,打包 Jar 文件,甚至可以是执行一个系统命令或者调用 Ant。另外,一个 Task 可以读取和设置 Project 的 Property 以完成特定的操作。
Gradle 快速开始
- 首先,先 download 最新版本的 gradle,网址如下:download。然后将下载下来的 zip 包放在你要安装的路径上,我安装在
/usr/local/bin;Copy to clipboardErrorCopied
然后打开电脑上的.bash_profile 文件,输入以下命令:GRADLE_HOME=/usr/local/bin/gradle-1.8; export GRADLE_HOME export PATH=$PATH:$GRADLE_HOME/binCopy to clipboardErrorCopied
- 然后再在 console 上输入以下命令:
$ source ~/.bash_profileCopy to clipboardErrorCopied
- 这样就安装成功啦,可以通过以下命令来查看是否安装成功。
gradle -versionCopy to clipboardErrorCopied
- 如果提示没有 gradle 命令,则有可能是:
- GRADLE_HOME 路径可能不对;
- 没有执行 source ~/.bash_profile
初始化项目
init 初始化
Gradle 有一个内置的任务,叫做 init,可以在一个空的文件夹中初始化一个新的 Gradle 项目。init 任务使用(也是内置的)wrapper 任务来创建一个 Gradle 包装脚本,即 gradlew。
$ mkdir demo
$ cd demo
在新项目目录下,在终端使用以下命令运行 init 任务:gradle init
。当提示时,选择2:应用程序
项目类型和3:Java
作为实现语言。接下来你可以选择用于编写构建脚本的 DSL - 1: Groovy
或2: Kotlin
。对于其他问题,按回车键,使用默认值。
$ gradle init
Select type of project to generate:
1: basic
2: application
3: library
4: Gradle plugin
Enter selection (default: basic) [1..4] 2
Select implementation language:
1: C++
2: Groovy
3: Java
4: Kotlin
5: Scala
6: Swift
Enter selection (default: Java) [1..6] 3
Select build script DSL:
1: Groovy
2: Kotlin
Enter selection (default: Groovy) [1..2] 1
Select test framework:
1: JUnit 4
2: TestNG
3: Spock
4: JUnit Jupiter
Enter selection (default: JUnit 4) [1..4]
Project name (default: demo):
Source package (default: demo):
BUILD SUCCESSFUL
2 actionable tasks: 2 executed
init 任务生成的新项目的结构如下:
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── app
├── build.gradle
└── src
├── main
│ └── java
│ └── demo
│ └── App.java
└── test
└── java
└── demo
└── AppTest.java
其中,settings.gradle(.kts) 文件有两行有趣的内容。
rootProject.name
为构建指定了一个名称,它覆盖了以构建所在的目录命名的默认行为。建议设置一个固定的名字,因为如果项目是共享的--例如作为 Git 仓库的根目录,文件夹可能会改变。include("app")
定义构建由一个名为app
的子项目组成,包含实际的代码和构建逻辑。更多的子项目可以通过额外的`include(..)'语句添加。
我们的构建包含一个名为 app 的子项目,代表我们正在构建的 Java 应用程序。它被配置在 app/build.gradle(.kts)文件中。
plugins {
id 'application'
}
repositories {
mavenCentral()
}
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter:5.7.1'
implementation 'com.google.guava:guava:30.1-jre'
}
application {
mainClass = 'demo.App'
}
tasks.named('test') {
useJUnitPlatform()
}
运行与打包
src/main/java/demo/App.java 内容如下:
package demo;
public class App {
public String getGreeting() {
return "Hello World!";
}
public static void main(String[] args) {
System.out.println(new App().getGreeting());
}
}
// 对应的测试文件如下
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class AppTest {
@Test void appHasAGreeting() {
App classUnderTest = new App();
assertNotNull(classUnderTest.getGreeting(), "app should have a greeting");
}
}
多亏了 application 插件,你可以直接从命令行运行应用程序。运行任务告诉 Gradle 执行分配给 mainClass 属性的类中的主方法。
c194a9eg<!-- begin-inline-katex ./gradlew run
> Task :app:run
Hello world!
BUILD SUCCESSFUL
2 actionable tasks: 2 executed
第一次运行包装脚本 gradlew 时,可能会有延迟,因为该版本的 gradle 被下载并存储在本地的~/.gradle/wrapper/dists 文件夹中。该 application 插件还为你捆绑了应用程序及其所有的依赖性。归档文件还将包含一个脚本,可以用一个命令启动应用程序。
end-inline-katex--> ./gradlew build
BUILD SUCCESSFUL in 0s
7 actionable tasks: 7 executed
如果你如上所示运行一个完整的构建,Gradle 将为你生成两种格式的存档。app/build/distributions/app.tar
和app/build/distributions/app.zip
。了解你的构建在幕后做什么的最好方法是发布一个构建扫描。要做到这一点,只需用--scan
标记运行 Gradle。
$ ./gradlew build --scan
BUILD SUCCESSFUL in 0s
7 actionable tasks: 7 executed
Publishing a build scan to scans.gradle.com requires accepting the Gradle Terms of Service defined at https://gradle.com/terms-of-service.
Do you accept these terms? [yes, no] yes
Gradle Terms of Service accepted.
Publishing build scan...
https://gradle.com/s/5u4w3gxeurtd2
私有仓库
Gradle 构建的项目,发布到仓库中,也非常容易:
apply plugin: 'maven'
uploadArchives {
repositories {
ivy {
credentials {
username "username"
password "pw"
}
url "http://repo.mycompany.com"
}
}
}
Gradle Java 实践
Gradle 使用了一种约定俗成的方法来构建基于 JVM 的项目,它借用了 Apache Maven 的一些约定。特别是,它对源文件和资源使用相同的默认目录结构,并与 Maven 兼容的资源库一起工作。Java 中所谓的 Plugin 就是一个定义了一系列 Properties 与 Tasks 的集合。如果希望使用 Java plugin,只需要在 build.gradle 中加入这句话:
apply plugin: 'java'Copy to clipboardErrorCopiedCopy to clipboardErrorCopied
Gradle 和 Maven 一样,采用了约定优于配置的方式对 Java 项目布局,并且布局方式是和 Maven 一样的,此外,Gradle 还可以方便的自定义布局。在 Gradle 中,一般把这些目录叫做 source set:
。
这里要注意,每个 plugin 的 source set 可能都不一样。同样的,Java plugin 还定义好了一堆 task,让我们可以直接使用,比如:clean、test、build 等等。这些 task 都是围绕着 Java plugin 的构建生命周期的:
图中每一块都是一个 task,箭头表示 task 执行顺序/依赖,比如执行 task jar,那么必须先执行 task compileJava 和 task processResources。另外可以看到,Gradle 的 Java plugin 构建生命周期比较复杂,但是也表明了更加灵活,而且,在项目中,一般只使用其中常用的几个:clean test check build 等等。
Gradle 构建过程中,所有的依赖都表现为配置,比如说系统运行时的依赖是 runtime,Gradle 里有一个依赖配置叫 runtime,那么系统运行时会加载这个依赖配置以及它的相关依赖。这里说的有点绕,可以简单理解依赖和 maven 类似,只不过 Gradle 用 configuration 实现,所以更灵活,有更多选择。下图是依赖配置关系图以及和 task 调用的关系图:
可以看到,基本和 Maven 是一样的。其实 Gradle 里面这些依赖(scope)都是通过 configuration 来实现的。
下一节:要读懂 Gradle,我们首先需要了解 Groovy 语言中的两个概念,一个 Groovy 中的 Bean 概念,一个是 Groovy 闭包的 delegate 机制。