paint-brush
Gradle 真的比 Maven 更好吗? - 我的最终判决by@nfrankel
5,465
5,465

Gradle 真的比 Maven 更好吗? - 我的最终判决

Nicolas Fränkel8m2023/08/09
Read on Terminal Reader

在这篇文章中,我想对 Gradle 进行一些阐述,这样我就可以引导人们了解它,而不是反复揭穿相同的“推理”。
featured image - Gradle 真的比 Maven 更好吗? - 我的最终判决
Nicolas Fränkel HackerNoon profile picture
0-item

我会发布我认为有趣的技术内容,但有趣的推文才是参与度最高的。我参加了三月份的 JavaLand 会议,偶然发现了 Gradle 展位,并发现了这个宝石:

https://twitter.com/nicolas_frankel/status/1638549568957861889


当然,在某个时候,有一个粉丝劫持了该线程并声称Gradle 具有所谓的优越性。在这篇文章中,我想阐明我的立场,这样我就可以引导人们了解它,而不是反复揭穿相同的“推理”。


为了解决这个问题,我需要及时返回。软件开发是一个快速变化的领域,我们的大部分理解都是基于个人经验。这是我的。

我的第一个构建工具:Ant


我从 2002 年开始使用 Java 进行开发。当时没有构建工具:我们通过 IDE 进行编译和构建。根据记录,我首先使用 Visual Age for Java;然后,我转向 Borland JBuilder。


使用 IDE 进行构建有一个巨大的问题:每个开发人员都有专用的设置,因此工件的生成取决于开发人员与机器的组合。


不可重复构建是一个古老的问题。我对可重复构建的第一次体验是 Apache Ant:


Apache Ant 是一个 Java 库和命令行工具,其任务是将构建文件中描述的进程驱动为相互依赖的目标和扩展点。 Ant 的主要已知用途是构建 Java 应用程序。 Ant 提供了许多内置任务,允许编译、组装、测试和运行 Java 应用程序。 Ant 还可以有效地用于构建非 Java 应用程序,例如 C 或 C++ 应用程序。更一般地说,Ant 可用于试验可以用目标和任务来描述的任何类型的流程。


-- https://ant.apache.org/


Ant 基于三个主要抽象:


  • 任务是工作的原子单元,例如javac编译Java 文件, war组装Web Archive 等。Ant 提供了许多开箱即用的任务,但允许添加自定义任务。
  • 目标是任务列表。
  • 您可以定义任务之间的依赖关系,例如package依赖于compile 。在这方面,您可以将Ant视为工作流执行引擎。


我很快就变得“流利”使用 Ant。作为一名顾问,我从一家公司到另一家公司,从一个项目到另一个项目。最初,我主要设置 Ant,但随着时间的推移,Ant 变得越来越普遍,我遇到了现有的 Ant 设置。我的项目是一致的,但其他项目彼此之间有很大不同。


每次到达一个新项目时,您都必须仔细阅读 Ant 设置以了解自定义构建。此外,每个项目的结构都不同。有些将源代码放在src中,有些将源代码放在sources中,有些放在嵌套结构中,等等。


我记得曾经有一个通用构建文件试图满足整个组织的项目需求。它在 2,000 多行 XML 中定义了 80 多个目标。我花了相当多的时间来了解如何在帮助下使用它,甚至花了更多的时间才能在不破坏项目的情况下对其进行调整。

我的第二个构建工具:Maven

上述项目让我思考了很多。我想改善这种情况,因为维护者已经突破了 Ant 的极限。当时,我正在和我的朋友Freddy Mallet (因声纳而闻名)一起工作。我们聊了聊,他向我推荐了 Maven。我曾经使用 Maven 构建过一个项目,但之前没有其他经验。我研究了文档几个小时,并在 Freddy 的指导下通过反复试验,将整个 Ant 构建文件迁移到一个简单的父 POM。


在 Ant 中,您需要定义每个项目中的所有内容。例如,Ant需要配置Java文件的位置进行编译; Maven 假设它们位于src/main/java下,尽管可以覆盖它。 Maven 确实以其约定优于配置的方法彻底改变了 Java 构建领域。如今,许多软件默认提供合理的配置。


对于像我一样从一个项目转到另一个项目的开发人员来说,这意味着加入新项目时的认知负担要少得多。我希望 Java 源代码位于src/main/java下。 Maven 约定继续超出项目的结构。他们还定义项目的生命周期,从编译到通过单元和集成测试将工件上传到远程注册表。


最后,初级开发人员往往会忽视这一点,但 Maven 定义了术语“依赖管理” 。它引入了工件注册表的概念,人们可以从中下载不可变的依赖项并将工件推送到其中。在此之前,每个项目都必须将依赖项存储在其专用存储库中。


根据记录,上述项目存储了一些依赖项。当我从 Ant 迁移到 Maven 时,我必须找到确切的依赖版本。对于大多数人来说,它很简单,因为它位于文件名或 JAR 清单中。然而,其中一个已经更新了额外的课程。


关于不变性就这么多了。


Maven 对后来的所有构建工具产生了深远的影响:它们参考 Maven 定义了自己。

我没有构建工具:Gradle


Gradle 的主要主张是修复 Maven 的缺点,或者至少是它认为的缺点。虽然 Maven 也难免受到指责,但 Gradle 认为最重要的问题是它缺乏灵活性。这是一个令人惊讶的假设,因为这正是 Maven 相对于 Ant 的改进之处。 Maven 项目具有相似的结构并使用相同的生命周期:有效的最小意外原则。相反,Gradle 允许自定义几乎每个构建方面,包括生命周期。


在面对灵活性争论之前,让我承认 Maven 后来实现的两个伟大的原始 Gradle 功能:Gradle 守护程序和 Gradle 包装器。


Maven 和 Gradle 都是运行在 JVM 上的 Java 应用程序。启动 JVM 在时间和资源方面都非常昂贵。这样做的好处是,长时间运行的 JVM 将随着时间的推移优化 JIT 代码。对于短期任务,如果考虑 JVM 启动时间,好处为零,甚至有害。 Gradle 提出了 Gradle 守护进程。当你运行 Gradle 时,它会寻找正在运行的守护进程。如果没有,它将开始一个新的。命令行应用程序会将所有内容委托给守护进程。顾名思义,当命令行完成时,守护进程不会停止。该守护进程利用了 JVM 的优势。


您的应用程序很可能比您当前的构建工具寿命更长。如果五年后您需要修复错误,却发现该项目的构建工具无法在线使用,会发生什么情况? Gradle 包装器背后的想法是保留确切的 Gradle 版本以及项目以及足够的代码来通过 Internet 下载完整版本。副作用是,开发人员不需要在本地安装 Gradle;全部使用相同的版本,避免任何差异。

揭秘 Gradle 的灵活性

Gradle 带来了 Maven 集成的上述两个强大功能,证明竞争是好的。尽管如此,我仍然发现 Gradle 没有任何好处。


我会试着把情绪化的一面推开。一开始,Gradle 营销试图在一切可能的场合打压 Maven,发布疯狂的比较图表,并且在沟通中通常非常积极。假设这个阶段的持续时间远远超过了一家试图在市场上找到自己位置的年轻公司所能接受的时间。你可以说 Gradle 的做法非常俄狄浦斯式:试图杀死它的 Maven “父亲”。终于,这么多年过去了,它似乎已经明智起来,现在“喜欢 Maven”。


请记住,在 Maven 接管之前,每个 Ant 项目都是临时的。 Maven 确实结束了这种情况。它为狂野西部的定制项目带来了法律。你可以不同意法律,但无论如何这就是法律,每个人都需要遵守它。 Maven 标准是如此根深蒂固,即使可以覆盖某些参数(例如源位置),也没有人这样做。


我确实经历了 Gradle 灵活性的两个症状。我怀疑还存在更多。

自定义生命周期阶段

Maven 分四个阶段管理集成测试,按顺序运行:


  1. pre-integration-test :设置测试所需的任何内容
  2. integration-test :执行测试
  3. post-integration-test :清理资源(如果有)
  4. verify :根据测试结果采取行动


我从未使用过前阶段和后阶段,因为每个测试都有专用的设置和拆卸逻辑。


另一方面, Gradle 没有任何集成测试的概念。然而,Gradle 粉丝会很高兴地解释说你可以添加你想要的阶段。事实上,Gradle 允许生命周期“定制”:您可以根据需要在常规生命周期中添加任意数量的额外阶段。


这是一团糟,因为每个项目都需要提出所需的阶段数量及其名称: integration-testintegration-testsintegration-testingit (对于懒惰的人来说)等等。选项是无穷无尽的。

雪花综合症

Maven 将每个项目都视为常规的标准项目。如果您有特定需求,可以为此编写一个插件。编写 Maven 插件绝对不好玩;因此,您只在必要时才写一条,而不仅仅是因为您认为该法律不适用于您。


Gradle 声称缺乏灵活性是一个问题;因此,它想要修复它。我持相反的观点:我的构建工具缺乏灵活性是一个功能,而不是一个错误。 Gradle 可以轻松破解构建。因此,任何认为自己的项目是特殊的雪花并且值得定制的人都会很乐意这样做。现实检验:这种情况很少发生;当它是时,它是针对框架的,而不是常规项目。 Gradle 支持者表示,它仍然提供标准,同时允许轻松配置。问题的核心在于,如果它可以随任何人的心血来潮改变,那么它就不是一个标准。


Gradle 是Android 项目事实上的构建工具。在我工作的一家公司中,有人在 Gradle 构建中编写了自定义 Groovy 代码来运行 Sonar 并将指标发送到内部 Sonar 实例。当时还没有现成的声纳插件,或者我认为它没有解决这个问题。到目前为止,一切都很好。


当另一个团队创建公司的第二个 Android 项目时,他们复制粘贴了第一个项目的结构和构建文件。此时明智的做法是用 Sonar 特定的代码创建一个内部 Gradle 插件。但他们没有这样做,因为 Gradle 使得破解构建变得如此容易。而我,这个 Gradle 讨厌者,亲自创建了这个插件。至少可以说,这可能会带来更好的开发人员体验。由于缺乏高质量的文档并使用非类型化语言(Groovy),我使用控制台打印出对象的结构以进行操作。

结论

竞争是好的,Gradle 带来了 Maven 集成、包装器和守护进程的新想法。然而,Gradle 建立在灵活性良好的前提下,而我的经验告诉我事实恰恰相反。 Ant 非常灵活,从一个项目到下一个项目的认知负荷很高。


我们,开发人员,也是人类:我们喜欢认为我们的项目与其他项目不同。大多数时候,情况并非如此。定制只是满足我们自我的一种方式。灵活的构建工具使我们能够实现此类定制,无论是否有保证。


不相关的定制不会带来任何好处,并且易于开发但维护成本昂贵。如果管理软件资产是我职责的一部分,那么我将始终选择构建工具的稳定性而不是灵活性。


最初于 2023 年 8 月 6 日发表于A Java Geek