在 Java 项目中,有两个主要的构建系统:Gradle 和 Maven。构建系统主要管理潜在的复杂依赖关系并正确编译项目。还可以将已编译的项目以及所有资源和源文件打包到.war
或.jar
文件中。对于简单的构建,Maven 和 Gradle 之间的选择几乎是个人喜好之一,或者也许是公司 CTO 或技术经理的偏好。他们俩都是非常好的构建工具。但是,对于更复杂的项目,Gradle 比 Maven 更胜一筹。
个人喜欢 Gradle;我讨厌 XML,复杂的 Java/Groovy 项目,如果没有 Gradle,几乎是寸步难行的。除了没有复杂的 XML 以外,Gradle 还使用 Groovy 或 Kotlin 编写的构建脚本提供了灵活性和更快的构建速度。借助 Kotlin 或 Groovy 的全部功能以及 Gradle API 库,您可以创建功能强大且复杂的构建脚本。这肯定是提升效率的工具。
对于 DSL(特定于域的语言)需要一些时间来适应,并且 Gradle 以难以学习而著称。但是,我认为这主要是因为人们已经习惯了 Maven。使用 Gradle,您实质上可以学习一种构建语言,而不只是简单地学习 XML。与仅在 Maven 中添加依赖项相比,充分利用 Gradle 无疑具有更陡峭的学习曲线。但是向 Gradle 文件添加依赖项实际上并不比在 Maven 中困难。扩展和自定义 Gradle 构建比编写 Maven 插件和自定义构建步骤要简单得多。
Gradle 还极大地缩短了构建时间,尤其是在大型项目中,因为 Gradle 仅处理已更改的任务和文件就可以很好地完成工作。此外,它提供了构建缓存和构建守护进程,使重复构建的性能更高。而且,像 Maven 一样,它使用并行线程进行依赖关系解析和项目构建。同样,对于小型,简单的构建,这种性能提升可能并不明显。但是对于较大的项目,这种性能提升是巨大的。
因此,总结一下。Gradle 是:
==
更陡峭的学习曲线而 Maven 是:
简要介绍一下 Groovy。Groovy 是一种 JVM 语言,它可以编译为与 Java 相同的字节码,并且可以与 Java 类无缝地互操作。Groovy 是 Java 的向后兼容超集,这意味着 Groovy 可以透明地与 Java 库和代码交互。但是,它还增加了许多新功能:可选的键入,函数式编程,运行时灵活性以及许多元编程内容。它还极大地清理了 Java 中许多冗长的代码格式。Groovy 尚未成为主流的开发语言,但是它已经在测试(由于其简化的语法和元编程功能)和构建系统中占据了一席之地。
您需要为本教程安装一些内容:
Java:您可能已经安装了 Java。本教程至少需要 Java 1.8。如果不是,请转到官网下载并安装它。
Gradle:但是,由于本教程是有关 Gradle 的教程,因此在本教程中,您可以继续进行安装。
build.gradle
build.gradle
文件是 Gradle 项目的核心,是构建配置必不可少的一项。就比如pom.xml
对于 Maven 来说,这是等效的(没有所有令人讨厌的尖括号)
让我们来看一个。
// 配置运行构建脚本的要求
buildscript {
// 设置自定义属性
ext {
springBootVersion = '2.1.6.RELEASE'
}
// 解决buildscript块中的依赖项时,检查Maven Central中的依赖项
repositories {
mavenCentral()
}
// 我们需要spring boot插件来运行构建脚本
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
// 添加构建插件
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
// 设置全局变量
group = 'com.okta.springboottokenauth'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
// 用于搜索以解决项目依赖关系的仓库地址
repositories {
mavenCentral()
}
// 项目依赖
dependencies {
implementation( 'com.okta.spring:okta-spring-boot-starter:1.2.1' )
implementation('org.springframework.boot:spring-boot-starter-security')
implementation('org.springframework.boot:spring-boot-starter-web')
testImplementation('org.springframework.boot:spring-boot-starter-test')
testImplementation('org.springframework.security:spring-security-test')
}
理解 Gradle 构建文件的关键是要意识到它是一个脚本,内置在 Groovy DSL 中。粗略地讲,它是一个配置脚本,它调用定义了配置选项的一系列闭包(考虑函数)。它看起来像 JSON 或 propertiy 文件,尽管从技术上来说这是错误的。
但是,真正的有趣的来自build.gradle
Groovy 脚本。因为它可以执行任意代码并访问任何 Java 库,特定于构建的 Gradle DSL 和 Gradle API。
buildscript
让我们从上至下查看脚本:
buildscript
闭包配置构建脚本本身(与应用程序相对)所需的属性,依赖项和源仓库。
接下来,apply plugin
以非常好友的方式应用了插件。这些扩展了 Gradle-Groovy DSL 框架的基本功能:将该 java 插件与 Spring Boot 和 Spring 依赖项管理一起应用。Java 插件提供配置 Gradle 的期望标准的 Java 项目的目录结构:src/main/java
,src/main/resources
,src/test/java
等,这些可以被配置为改变默认的目录或添加新的目录。
接下来,将一些标准属性应用于构建。
repositories
块定义了构建脚本将在哪里寻找依赖关系。Maven Central 是最常见的(mavenCentral()
),但也可以配置其他仓库,包括自定义仓库和本地仓库。可以使用来将本地 Maven 缓存配置为仓库mavenLocal()
。如果团队希望协调项目之间的构建,但又不想将项目构建文件实际捆绑在一起,这将很有帮助。
最后,定义项目依赖项。
其中每个模块定义闭包的顺序无关紧要,因为大多数build.gradle
文件仅定义依赖项,设置项目属性并使用预定义的任务,因此文件中元素的顺序无关紧要。例如,没有理由repositories
块必须走在该dependencies
块之前。您可以将build.gradle
文件视为 Gradle 在执行调用它的 shell 命令分配的任何任务之前读取的配置文件。
但是,当您开始使用 Gradle 的功能来定义自定义任务并执行任意代码时,它将变得更加复杂。Gradle 将以build.gradle
自上而下的方式读取文件,并执行在其中找到的所有代码块;根据此代码的作用,它可以在脚本中创建强制排序。此外,当您定义自定义任务和属性(在 Gradle API 中找不到)时,排序很重要,因为这些符号不会被预先定义,因此必须在构建脚本中定义它们才能使用它们。
回到 Groovy 刚问世时,函数式编程是相当小众的领域,将诸如闭包之类的东西带入 JVM 感觉很疯狂。如今,它变得更加普遍:Javascript 中的每个函数都是闭包。一般来说,闭包是具有范围的一流函数。
这意味着两件事:
Java 版本的闭包称为 lambda。这些是在 1.8 版中引入 Java 的,顺便说一句,这并不是在 Groovy 获得最初的流行和函数式编程开始发展的同时发生的。
为了演示 lambda,请看一下名为的 JUnit 测试LambdaTest.java
。
src/test/java/com/okta/springboottokenauth/LambdaTest.java
interface SimpleLambda {
public int sum(int x, int y);
}
public class LambdaTest {
// 创建一个lambda函数
public SimpleLambda getTheLambda(int offset) {
int scopedVar = offset;
return (int x, int y) -> x + y + scopedVar;
}
@Test
public void testClosure() {
// 测试lambda方法,当offset=1
SimpleLambda lambda1 = getTheLambda(1);
assertEquals(lambda1.sum(2,2), 5);
// 测试lambda方法,当offset=2
SimpleLambda lambda2 = getTheLambda(2);
assertEquals(lambda2.sum(2,2), 6);
}
}
这个示例很有代表性,演示了 lambda 的两个基本属性。在闭包或 lambda 函数中,实现是在getTheLambda(int offset)
方法中定义的。创建 lambda 时,将 offset 变量封装在闭包范围中并返回。该 lambda 被分配给变量。可以重复调用它,并且它将引用相同的作用域。此外,可以使用封装在单独作用域中并分配给其他变量的新变量来创建新的 lambda。
来自强大的面向对象的背景,封闭最初感觉就像虫洞在严格的对象范围连续体上打穿透孔一样,奇怪地将对象的各个部分在空间和时间上连接在一起。
采取build.gradle
文件的依赖项部分:
dependencies {
implementation( 'com.okta.spring:okta-spring-boot-starter:1.2.1' )
implementation('org.springframework.boot:spring-boot-starter-security')
...
}
没有 Groovy DSL 速记,实际上是:
project.dependencies({
implementation( 'com.okta.spring:okta-spring-boot-starter:1.2.1' )
implementation('org.springframework.boot:spring-boot-starter-security')
...
})
括号中的所有内容实际上都是传递给该project.dependencies()
方法的闭包。该project
对象是Project
该类的实例,该类是构建的主要 API 父类。
如您所见,这些函数将一系列依赖项作为字符串传递。那么,为什么不使用更传统的静态数据结构(如 JSON,属性或 XML)呢?原因是这些重载函数也可以使用闭包代码块,因此可以进行深度自定义。
依赖关系块内部是一系列配置和名称。
dependencies {
configurationName dependencyNotation
}
我们的build.gradle
文件使用两种配置:implementation
和testImplementation
。
implementation()
定义编译时所需的依赖项。此配置方法称为compile
。testImplementation()
并定义了仅用于测试(旧testCompile
)所需的依赖项。
您可能会看到的另一个依赖项配置是runtimeOnly
和testRuntimeOnly
。这声明了运行时提供的不需要对其进行编译的依赖项。
定义依赖关系的方法比对本文的范围有用的方法更多。几乎可以说任何东西都可以是依赖项:本地文件,jar 的目录,另一个 Gradle 项目等等,并且可以将依赖项配置为执行某些操作,例如排除某些子依赖项。
值得注意的是:Gradle 和 Maven 以完全相同的方式解决依赖关系。例如,假设我们想从Spring Boot Starter
中排除Log4j
依赖关系,我们可以这样做:
dependencies {
implementation( 'com.okta.spring:okta-spring-boot-starter:1.2.1' ) {
exclude group: 'org.apache.logging.log4j', module: 'log4j-api'
}
}
或者说我们想将目录中的所有文件都包含libs
为依赖项:
dependencies {
implementation fileTree('libs')
}
关于 Gradle 的一件很棒的事情是 Gradle 包装器。Gradle 命令行为gradle
。但是,您会注意到在网上的许多地方,您都会看到./gradlew
或gradlew.bat
。这些是调用包装程序的命令。
包装器允许项目捆绑在项目本身内部构建项目所需的 Gradle 版本。这样可以确保对 Gradle 的更改不会中断构建。它还可以确保即使没有安装 Gradle 的人也可以运行构建。
它将以下文件添加到您的项目:
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
└── gradlew.bat
gradlew
和gradlew.bat
是用于 Linux/OSX 和 Window(分别)执行脚本。他们运行build.gradle
使用捆绑的摇篮文件.jar
的gradle/wrapper
子目录。
任务是 Gradle 的核心。Java 插件增加了十几个任务,包括:clean
,compile
,test
,jar
,和uploadArchives
。Spring Boot 插件添加了bootRun
任务,该任务运行 Spring Boot 应用程序。
通常,任务是这样运行的:gradle taskName otherTaskName
,或使用包装器:./gradlew taskName otherTaskName
。
如果打开终端并 cd 进入示例项目的基本目录,则可以使用gradle tasks
列出build.gradle
文件定义的所有任务。tasks
当然,它本身是由基本 Gradle API 定义的任务。
> Task :tasks
------------------------------------------------------------
Tasks runnable from root project
------------------------------------------------------------
Build tasks
-----------
assemble - Assembles the outputs of this project.
build - Assembles and tests this project.
buildDependents - Assembles and tests this project and all projects that depend on it.
buildNeeded - Assembles and tests this project and all projects it depends on.
classes - Assembles main classes.
clean - Deletes the build directory.
jar - Assembles a jar archive containing the main classes.
testClasses - Assembles test classes.
Build Setup tasks
-----------------
init - Initializes a new Gradle build.
wrapper - Generates Gradle wrapper files.
Distribution tasks
------------------
assembleDist - Assembles the main distributions
assembleMonitorDist - Assembles the monitor distributions
distTar - Bundles the project as a distribution.
distZip - Bundles the project as a distribution.
installDist - Installs the project as a distribution as-is.
installMonitorDist - Installs the project as a distribution as-is.
monitorDistTar - Bundles the project as a distribution.
monitorDistZip - Bundles the project as a distribution.
Documentation tasks
-------------------
groovydoc - Generates Groovydoc API documentation for the main source code.
javadoc - Generates Javadoc API documentation for the main source code.
Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in root project 'fun'.
components - Displays the components produced by root project 'fun'. [incubating]
dependencies - Displays all dependencies declared in root project 'fun'.
dependencyInsight - Displays the insight into a specific dependency in root project 'fun'.
dependentComponents - Displays the dependent components of components in root project 'fun'. [incubating]
help - Displays a help message.
model - Displays the configuration model of root project 'fun'. [incubating]
projects - Displays the sub-projects of root project 'fun'.
properties - Displays the properties of root project 'fun'.
tasks - Displays the tasks runnable from root project 'fun'.
IDE tasks
---------
cleanIdea - Cleans IDEA project files (IML, IPR)
idea - Generates IDEA project files (IML, IPR, IWS)
openIdea - Opens the IDEA project
Verification tasks
------------------
check - Runs all checks.
test - Runs the unit tests.
Rules
-----
Pattern: clean<TaskName>: Cleans the output files of a task.
Pattern: build<ConfigurationName>: Assembles the artifacts of a configuration.
Pattern: upload<ConfigurationName>: Assembles and uploads the artifacts belonging to a configuration.
To see all tasks and more detail, run gradle tasks --all
To see more detail about a task, run gradle help --task <task>
我想指出dependencies
任务。它将列出一棵树,其中包含项目所需的所有依赖关系(包括子依赖关系)。尝试gradle dependencies
在项目根目录中运行。您可以使用该dependencyInsight
任务来深入了解特定的子依赖项。
另一个有助于解决问题的properties
任务是该任务,该任务列出了在根项目对象实例上定义的所有属性。
当然,在开发 Spring Boot 项目时,可以使用命令:./gradlew bootJar
,该任务将项目及其依赖项打包在一个 jar 文件中。
到此,基础篇完事儿,提高篇中将会实践一下自定义任务和 Groovy 闭包在 Gradle 配置文件build.gradle
文件中如何使用。