Gradle 引人注目的功能集
让我们仔细看看 Gradle 与其竞争对手的区别:它引人注目的功能集(见图 2.3)。 总而言之,Gradle 是一个企业级构建系统,由声明性且富有表现力的 Groovy DSL 提供支持。 它将灵活性和轻松的可扩展性与约定优于配置的思想以及对传统依赖关系管理的支持结合起来。 在专业服务公司 (Gradleware) 和强大的社区参与的支持下,Gradle 正在成为许多开源项目和企业的首选构建解决方案。

富有表现力的构建语言和深入的 API
在构建脚本中解锁 Gradle 强大功能的关键在于发现并应用其领域模型,如图 2.4 所示。
如图所示,构建脚本直接映射到 Gradle API 中 Project 类型的实例。 反过来,构建脚本中的依赖项配置块会调用项目实例的方法 dependency() 。 与 Java 世界中的大多数 API 一样,它可以在 Gradle 网站上以 HTML Javadoc 文档形式提供: http://www.gradle.org/docs/current/javadoc/index.html 。 谁会知道呢? 你实际上是在处理代码。 在不知不觉中,您在内存中生成了构建逻辑的对象表示。 在第 4 章中,我们将探讨 Gradle 的许多 API 类以及它们在构建脚本中的表示方式。

Gradle 脚本中的每个元素都与 Java 类一一对应; 然而,一些元素被涂上了一些 Groovy 语法的糖衣。 在许多情况下,拥有 Groovy 版本的类可以使代码比 Java 版本更紧凑,并且允许使用闭包等新的语言功能。
Gradle 无法了解您的企业构建的所有特定要求。 通过将钩子暴露到生命周期阶段,Gradle 允许监视和配置构建脚本的执行行为。 假设您有一个非常独特的要求,即每当发生单元测试失败时就向开发团队发送电子邮件。 您想要发送电子邮件的方式(例如,通过 SMTP 或第三方电子邮件服务提供商)和收件人列表对于您的构建来说是非常特定的。 使用 Gradle 的其他构建可能对此功能根本不感兴趣。 通过编写在测试执行生命周期事件后收到通知的自定义测试侦听器,您可以轻松地将此功能合并到您的构建中。
Gradle 通过公开在 Groovy 中实现的 DSL 为其模型建立词汇表。 当处理复杂的问题域时,在本例中是构建软件的任务,能够使用通用语言来表达逻辑可能是一个强大的工具。 让我们看一些例子。 构建中最常见的是您想要执行的工作单元的表示法。 Gradle 将此工作单元描述为任务。 Gradle 标准 DSL 的一部分是能够定义专门用于编译和打包 Java 源代码的任务。 它是一种使用自己的词汇构建 Java 项目的语言,不需要与其他上下文相关。
另一个例子是表达对外部库的依赖关系的方式,这是构建工具解决的一个非常常见的问题。 开箱即用的 Gradle 为您的构建脚本提供了两个配置块,允许您定义依赖项和要从中检索它们的存储库。 如果标准的 DSL 元素不能满足您的需求,您甚至可以通过 Gradle 的扩展机制引入自己的词汇表。
一开始这听起来有点模糊,但是一旦您克服了学习构建语言的最初障碍,创建可维护和声明性的构建就变得很容易。 一个不错的起点是 http://www.gradle.org/docs/current/dsl/index.html 上的 Gradle 构建语言参考指南。 Gradle 的 DSL 是可以扩展的。 您可能想要更改现有任务的行为或添加您自己的习惯用法来描述您的业务领域。 Gradle 为您提供了很多选择来做到这一点。
Gradle 是 Groovy
Ant 和 Maven 等著名构建工具通过 XML 定义其构建逻辑。 众所周知,XML 易于读写,但如果大量使用,可能会成为维护的噩梦。 XML 的表达能力不是很强。 这使得定义复杂的自定义逻辑变得困难。 Gradle 采用了不同的方法。 在底层,Gradle 的 DSL 是用 Groovy 编写的,在 Java 之上提供语法糖。 结果是一种可读且富有表现力的构建语言。 您的所有脚本也是用 Groovy 编写的。 能够使用编程语言来表达您的构建需求是一个主要优点。 您无需成为 Groovy 专家即可开始使用。 由于 Groovy 是在 Java 之上编写的,因此您可以通过尝试其语言功能来逐步迁移。 您甚至可以用纯 Java 编写自定义逻辑——Gradle 不在乎。 身经百战的 Groovy 老手将向您保证,使用 Groovy 而不是 Java 将使您的工作效率提高几个数量级。 Dirk Konig 等人编写的 《Groovy in Action, Second Edition》 是一本很好的参考指南。 (Manning,2009)有关 Groovy 的入门知识,请参阅附录 B。
灵活的约定
Gradle 的伟大想法之一是为您的项目提供指导方针和合理的默认值。 Gradle 中的每个 Java 项目都确切地知道源代码和测试类文件应该存放在哪里,以及如何编译代码、运行单元测试、生成 Javadoc 报告以及创建代码的分发。 所有这些任务都完全集成到构建生命周期中。 如果您遵守约定,则只需最少的配置工作。 事实上,您的构建脚本只是一句话。严重地!您想了解有关使用 Gradle 构建 Java 项目的更多信息吗? 好吧,你可以——我们将在第 3 章中介绍它。图 2.5 说明了 Gradle 如何为 Java 项目引入约定和生命周期任务。
提供了在 Java 项目上下文中有意义的默认任务。 例如,您可以编译 Java 生产源代码、运行测试并组装 JAR 文件。 每个 Java 项目都以标准目录布局开始。 它定义了在哪里可以找到生产源代码、资源文件和测试代码。 约定属性用于更改默认值。
同样的概念也适用于其他项目原型,例如 Scala、Groovy、Web 项目等等。 Gradle 将此概念称为 “按惯例构建”。 构建脚本开发人员不需要知道它在幕后是如何工作的。 相反,您可以专注于需要配置的内容。 Gradle 的约定与 Maven 提供的约定类似,但它们不会让您感到受限制。Maven 非常固执己见; 它建议一个项目仅包含一个 Java 源目录,并且仅生成一个 JAR 文件。 对于许多企业项目来说,这不一定是现实。 Gradle 可以让您轻松打破惯例。 另一方面,Ant 从未就如何构建构建脚本提供大量指导,从而实现了最大程度的灵活性。 Gradle 通过提供约定以及轻松更改约定的能力来采取中间立场。 Gradle 的核心工程师之一 Szczepan Faber 在他的博客中这样说道:“Gradle 是一个建立在一个不固定的工具包之上的一个固定的框架。” (猴岛,“无论是否有意见”,2012 年 6 月 2 日, http://monkeyisland.pl/2012/06/02/opinionated-or-not/ 。)

健壮而强大的依赖管理
软件项目通常不是独立的。 通常,您的应用程序代码使用提供现有功能的第三方库来解决特定问题。 如果 Hibernate 已经存在,为什么要通过实现持久性框架来重新发明轮子呢? 在组织内,您可能是由不同团队实现的组件或模块的使用者。 外部依赖项可以通过存储库访问,并且存储库的类型高度依赖于您的公司的偏好。 选项范围从普通文件系统到成熟的企业存储库。 外部依赖项可能引用其他库或资源。 我们称这些为传递依赖。
Gradle 提供了一个基础架构来管理解析、检索和存储依赖项的复杂性。 下载它们并将其放入本地缓存后,您的项目就可以使用它们。 企业构建的一个关键要求是可重复性。 回想一下第一章中汤姆和乔的故事。您还记得您的同事上次说“但它在我的盒子上有效”吗? 构建必须在不同的计算机上产生相同的结果,而与本地缓存的内容无关。 像 Ivy 和 Maven 这样的依赖管理器在当前的实现中无法完全保证可重复性。 这是为什么? 每当下载依赖项并将其存储在本地缓存中时,它都不会考虑工件的来源。 在项目的存储库发生更改的情况下,缓存的依赖关系被视为已解决,即使工件的内容可能略有不同。 最坏的情况是,这会导致构建失败,并且极难调试。 Ivy 的另一个常见抱怨是,依赖快照版本(当前正在开发的命名约定 –SNAPSHOT 的工件)没有在本地缓存中正确更新,即使它在存储库上发生了更改并被标记为更改。 目前的解决方案还存在很多不足之处。 Gradle 提供了自己的可配置、可靠且高效的依赖关系管理解决方案。 我们将在第 5 章中仔细研究它的功能。
大型企业项目通常由多个模块组成以分离功能。 在 Gradle 世界中,每个子模块都被视为一个项目,可以定义对外部库或其他模块的依赖关系。 此外,每个子项目都可以单独运行。 Gradle 会为您确定哪些子项目依赖项需要重建,而无需将子项目的工件存储在本地缓存中。
可扩展的构建
对于一些公司来说,拥有数百个模块的大型项目是现实的。 构建和测试较小的代码更改可能会消耗大量时间。 您可能从个人经验中知道,通过运行清理任务来删除旧的类和资源是一种自然反应。 很多时候,您会因为构建工具没有接受更改及其依赖项而感到烦恼。 您需要的是一个足够智能的工具,可以只重建软件中实际更改的部分。 Gradle 通过指定任务输入和输出来支持增量构建。 它可靠地为您计算出哪些任务需要跳过、构建或部分重建。 相同的概念也适用于多模块项目,称为部分构建。 由于您的构建明确定义了子模块之间的依赖关系,因此 Gradle 只负责重建必要的部分。 默认情况下不再运行干净!
自动化单元、集成和功能测试是构建过程的一部分。 将短期运行类型的测试与需要设置资源或外部依赖项才能运行的测试分开是有意义的。 Gradle 支持并行测试执行。 此功能是完全可配置的,可确保您真正利用处理器的内核。 责任并不止于此。 Gradle 将在未来版本中支持将测试执行分发到多台机器。 我很遗憾地告诉你,在长时间构建之间阅读你的 Twitter feed 的日子已经一去不复返了。
开发人员在开发过程中多次运行构建。 这意味着每次启动一个新的 Gradle 进程,加载其所有内部依赖项,并运行构建逻辑。 您会注意到,脚本实际开始执行之前通常需要几秒钟的时间。 为了提高启动性能,Gradle 可以以守护进程模式运行。 在实践中,Gradle 命令会分叉一个守护进程,它不仅执行您的构建,而且还在后台保持运行。 后续的构建调用将搭载现有的守护进程,以避免启动成本。 因此,您会注意到初始构建执行速度要快得多。
轻松扩展
大多数企业构建并不相似,也不能解决相同的问题。 一旦您完成了设置基本脚本的初始阶段,您将需要实现自定义逻辑。 Gradle 对于实现该代码的方式并不固执己见。 相反,它为您提供了多种选择,具体取决于您的具体用例。 实现自定义逻辑的最简单方法是编写任务。 任务可以直接在构建脚本中定义,无需特殊仪式。 如果您觉得复杂性占据了主导地位,您可能想要探索自定义任务的选项,该选项允许在类定义中编写逻辑,从而使代码的结构变得简单且可维护。 如果您想在构建和项目之间共享可重用的代码,插件是您最好的朋友。 插件代表了 Gradle 最强大的扩展机制,让您可以完全访问 Gradle 的 API,并且可以像任何其他软件一样进行编写、测试和分发。 编写插件非常简单,并且不需要很多额外的描述符。
与其他构建工具集成
能够与现有的构建工具集成不是可以节省大量时间吗? Gradle 与其前辈 Ant、Maven 和 Ivy 配合得很好,如图 2.6 所示。
如果您来自 Ant,Gradle 不会强迫您完全迁移构建基础设施。 相反,它允许您导入现有的构建逻辑并重用标准 Ant 任务。 Gradle 构建与 Maven 和 Ivy 存储库 100% 兼容。 您可以检索依赖项并发布您自己的工件。 Gradle 为现有 Maven 构建提供了一个转换器,可以将构建逻辑转换为 Gradle 构建脚本。
现有的 Ant 脚本可以无缝导入到您的 Gradle 构建中,并像使用任何其他外部 Gradle 脚本一样使用。 Ant 目标在运行时直接映射到 Gradle 任务。 Gradle 附带 Ant 库,并向您的脚本公开一个名为 AntBuilder 的帮助程序类,该类完全融入 Gradle 的 DSL。 它看起来和感觉上仍然像 Ant 的 XML,但没有尖括号。 Ant 用户会感到宾至如归,因为他们不必立即转换到 Gradle 语法。 从 Ant 迁移到 Gradle 也是理所当然的事情。 您可以通过重用现有的 Ant 逻辑来逐步采取行动,同时利用 Gradle 的优势。

Gradle 的目标是达到与 Maven 类似的集成深度。 在撰写本文时,这一点尚未实现。 从长远来看,Maven POM 和插件将被视为 Gradle 原生。 Maven 和 Ivy 存储库已成为当今构建基础设施的重要组成部分。 想象一下一个没有 Maven Central 来帮助访问您喜爱的项目依赖项的特定版本的世界。 从存储库检索依赖项只是故事的一部分; 向他们发布同样重要。 只需进行少量配置,Gradle 就可以上传项目的工件以供全公司或公众使用。
社区驱动、公司支持
Gradle 可免费使用,并附带 Apache License 2.0。 2008 年 4 月首次发布后,一个充满活力的社区很快开始围绕它形成。 在过去的五年里,开源开发人员为 Gradle 的核心代码库做出了重大贡献。 事实证明,托管在 GitHub 上对 Gradle 非常有利。 代码更改可以作为拉取请求提交,并在进入代码库之前经过核心提交者的仔细审查过程。 如果您使用过 Maven 等其他构建工具,您可能会习惯各种可重用插件。 除了运行时附带的标准插件之外,Gradle 社区几乎每天都会发布新功能。 在整本书中,您将使用 Gradle 附带的许多标准插件。 附录 A 提供了更广泛的标准插件和第三方插件。 每个社区驱动的软件项目都需要一个论坛来立即回答问题。 Gradle 通过 Gradle 论坛 (http://forums.gradle.org/gradle) 与社区建立联系。 您可以确信您会在同一天得到对您的问题有帮助的答复。
Gradleware 是 Gradle 背后的技术服务和支持公司。 它不仅为 Gradle 本身提供专业建议,而且旨在为广泛的企业自动化咨询提供服务。 该公司得到了该领域经验丰富的高素质工程师的支持。 最近,Gradleware 开始举办免费网络研讨会,以激发新手的兴趣并加深经验丰富的 Gradle 用户的知识。
锦上添花:附加功能
您不讨厌必须为不同的项目安装新的运行时吗? Gradle Wrapper 来救援! 它允许从运行构建的任何计算机上的指定存储库下载并安装 Gradle 运行时的新副本。 该过程在第一次执行构建时自动触发。 Wrapper 对于与分布式团队共享构建或在 CI 平台上运行它们特别有用。
Gradle 还配备了丰富的命令行界面。 使用命令行选项,您可以控制从指定日志级别到排除测试再到显示帮助消息的所有内容。 这没什么特别的; 其他工具也提供了这一点。 不过,有些功能很突出。 Gradle 允许以缩写的驼峰格式运行命令。 实际上,可以使用缩写 rMAT 调用名为 runMyAwesomeTask 的命令。 方便,不是吗? 尽管本书通过在 shell 中运行命令来展示大部分示例,但请记住,Gradle 提供了开箱即用的图形用户界面。