第36章 Maven 与 Gradle——项目构建工具
17 分钟阅读
第三十六章 Maven 与 Gradle——项目构建工具
“写代码一时爽,依赖地狱火葬场。"——每个被 JAR 包折磨过的 Java 程序员
各位好,欢迎来到 Java 世界中两个最"卷"的工具——Maven 和 Gradle 的主场。
话说有一天,小明写了一个 Java 程序,兴奋地编译运行,结果报错:NoClassDefFoundError。小明懵了:我明明写了这个类啊!旁边老程序员拍了拍他的肩膀:“欢迎来到依赖管理地狱,年轻人。”
这就是为什么我们需要 构建工具(Build Tool)。它们就像是 Java 项目的大管家,帮我们处理依赖管理、编译打包、自动化测试、发布部署这些脏活累活。没有它们,一个稍微大点的项目光配置依赖就能让你怀疑人生。
本章我们就来聊聊 Java 生态中两代最具代表性的构建工具——Maven 和 Gradle。
36.1 Maven
36.1.1 Maven 的前世今生
Maven 是 Apache 软件基金会的开源项目,最早诞生于 2004 年左右。它的名字很有意思——中文意思是"专家"或"内行”,而它的 Logo 是一只可爱的海狸( Beaver)。为什么是海狸?因为海狸是"建筑大师",擅长筑坝,这正好隐喻 Maven 在项目中扮演的"构建者"角色。
Maven 的核心贡献是引入了 项目对象模型(Project Object Model,简称 POM) 的概念。简单来说,你只需要在一个 pom.xml 文件里声明"我需要什么依赖"、“我要怎么编译”,Maven 就会自动帮你搞定一切。
36.1.2 Maven 的核心概念
在学习 Maven 之前,我们需要理解几个核心概念:
- POM(Project Object Model):项目对象模型,就是那个
pom.xml文件。它是 Maven 工作的核心配置文件,定义了项目的基本信息、依赖、插件、构建配置等。 - 坐标(Coordinates):Maven 用
groupId、artifactId、version三个坐标来唯一标识一个构件(artifact)。就像 GPS 坐标能精确定位地球上的任意位置一样,Maven 坐标能精确定位仓库中的任意 JAR 包。 - 仓库(Repository):存储 JAR 包和其他构建产物的地方。包括本地仓库(在你电脑的
.m2目录下)和远程仓库(最著名的是 Maven Central)。 - 依赖传递(Transitive Dependencies):你依赖的库可能又依赖其他库,Maven 会自动把这些传递依赖也下载下来。当然,这也可能是"依赖地狱"的开始。
36.1.3 目录结构—— Maven 的"强迫症"
Maven 是一个有洁癖的工具,它要求项目严格遵循标准化的目录结构:
my-project/
├── pom.xml # Maven 配置文件(必须放根目录)
├── src/
│ ├── main/
│ │ ├── java/ # Java 源代码
│ │ └── resources/ # 资源文件(配置文件、图片等)
│ └── test/
│ ├── java/ # 测试源代码
│ └── resources/ # 测试资源文件
├── target/ # 编译输出目录(Maven 自动生成)
│ └── classes/ # 编译后的 .class 文件
└── LICENSE
这种"约定优于配置(Convention Over Configuration)“的设计哲学,是 Maven 区别于 Ant 的重要特征。简单来说,Maven 已经帮你定好了最好的规矩,你最好乖乖遵守。
💡 小贴士:如果你不喜欢 Maven 的默认目录结构,也可以在
pom.xml中自定义。但一般情况下,没事儿别改,保持和团队一致最重要。
36.1.4 pom.xml 详解
让我们来看一个典型的 pom.xml 文件:
| |
36.1.5 Maven 常用命令
Maven 的命令是在命令行中执行的,基本格式是 mvn 命令,后面可以跟各种参数。常见命令如下:
| 命令 | 说明 |
|---|---|
mvn clean | 清理 target 目录,删除所有编译产物 |
mvn compile | 编译项目,生成 .class 文件到 target/classes |
mvn test | 运行测试用例 |
mvn package | 打包项目,生成 JAR 或 WAR 文件 |
mvn install | 将打包产物安装到本地仓库,供其他项目引用 |
mvn deploy | 将打包产物发布到远程仓库 |
mvn dependency:tree | 查看项目的依赖树(排依赖冲突神器) |
mvn clean package -DskipTests | 清理并打包,跳过测试(赶时间专用) |
| |
36.1.6 依赖作用域(Dependency Scope)
Maven 的依赖是有"作用域"的,不同作用域的依赖在不同场景下生效:
- compile(默认):编译、测试、运行都有效
- test:仅在测试时有效(如 JUnit)
- provided:编译和测试有效,但运行时由容器提供(如 Servlet API)
- runtime:编译时不需要,运行时才需要(如 JDBC 驱动)
- system:使用系统提供的 JAR,不从仓库获取(不推荐)
| |
36.1.7 Maven 的优点与局限
Maven 作为一个"老前辈”,有其不可替代的优势:
优点:
- 约定大于配置,简单易用
- 依赖管理非常成熟,仓库生态丰富
- 生命周期清晰,命令标准化
- 插件生态丰富
- 几乎所有 Java 项目都认识它
局限:
- XML 配置有时候过于冗长
- 依赖冲突排查有时比较麻烦
- 编译速度相对于 Gradle 较慢
- 扩展性不如 Gradle 灵活
36.1.8 Maven 仓库结构图
┌─────────────────────────────────────────────────────────────────┐
│ Maven 仓库体系 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ 本地仓库 │ │ 远程仓库 │ │
│ │ (~/.m2/repository) │◄───►│ (Maven Central) │ │
│ │ │ │ 或公司私有仓库 │ │
│ └────────┬────────┘ └────────┬────────┘ │
│ │ │ │
│ │ 依赖查找顺序 │ │
│ ▼ ▼ │
│ ┌────────────────────────────────────────────┐ │
│ │ 1. 先查本地仓库,有则直接使用 │ │
│ │ 2. 本地没有则查远程仓库,下载到本地 │ │
│ │ 3. 远程也没有?报错了事 :) │ │
│ └────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
36.2 Gradle
36.2.1 Gradle 的诞生——青出于蓝
如果说 Maven 是一个成熟稳重的老大哥,那 Gradle 就是一个年轻有为的后起之秀。
Gradle 诞生于 2007 年,2009 年开源,2015 年成为 Android 官方构建工具。它巧妙地结合了 Maven 的约定优于配置理念和 Ant 的灵活性,同时引入了 Groovy(后来是 Kotlin)作为 DSL(领域特定语言),让构建脚本变得像写代码一样灵活。
Gradle 的 Logo 是一只大象——“Elephant”(因为 Gradle 发音类似… 好吧这是强行关联)。但大象负重能力强、记忆力好,这倒也挺符合 Gradle 处理复杂构建任务的能力。
36.2.2 Groovy 与 Kotlin DSL
Gradle 使用 Groovy 或 Kotlin 来编写构建脚本。相比 Maven 的 XML,代码式的 DSL 更加灵活、表达力更强。
| |
| |
36.2.3 Gradle 项目目录结构
Gradle 的目录结构与 Maven 非常相似(毕竟都是"约定优于配置"):
my-gradle-project/
├── build.gradle # 构建脚本(根项目)
├── build.gradle.kts # 或 Kotlin DSL 版本
├── settings.gradle # 项目设置(定义包含哪些子项目)
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties # Gradle 版本配置
├── gradlew # Linux/Mac 启动脚本
├── gradlew.bat # Windows 启动脚本
├── src/
│ ├── main/
│ │ ├── java/ # Java 源代码
│ │ └── resources/ # 资源文件
│ └── test/
│ ├── java/ # 测试源代码
│ └── resources/ # 测试资源文件
└── app/ # 可以有多个子项目
└── build.gradle
36.2.4 Gradle 任务(Tasks)
Gradle 的核心是 Task(任务)。你可以把 Task 理解为一系列操作,比如编译、测试、打包等。你也可以定义自己的 Task。
| |
36.2.5 Gradle 依赖配置(Configuration)
Gradle 使用不同的 Configuration 来区分依赖的使用范围,这比 Maven 的 scope 更灵活:
| |
36.2.6 Gradle 常用命令
| 命令 | 说明 |
|---|---|
gradle build | 构建项目 |
gradle clean build | 清理并构建 |
gradle run | 运行应用程序(需要配置 Application 插件) |
gradle test | 运行测试 |
gradle tasks --all | 列出所有可用任务 |
gradle dependencies | 查看依赖树 |
gradle build --scan | 构建并上传构建扫描报告到 Gradle Cloud |
gradle wrapper | 生成 Gradle Wrapper 脚本 |
| |
36.2.7 Gradle 多模块项目
Gradle 非常擅长管理 多模块项目。想象一下,你的项目有 web、service、dao 三个模块,Gradle 可以轻松管理它们之间的依赖关系。
| |
| |
| |
多模块项目结构示例:
┌─────────────────────────────────────────┐
│ my-multi-module-project │
├─────────────────────────────────────────┤
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ web │ │ service │ │ dao │ │
│ │ 模块 │──│ 模块 │──│ 模块 │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │ │ │ │
│ └────────────┴────────────┘ │
│ │ │
│ 所有模块依赖 │
│ 公共配置 │
└─────────────────────────────────────────┘
36.2.8 Gradle 与 Maven 对决
让我们来一个全面的对比:
| 特性 | Maven | Gradle |
|---|---|---|
| 配置语言 | XML | Groovy/Kotlin DSL |
| 学习曲线 | 较平缓 | 稍陡 |
| 构建速度 | 较慢 | 快(增量构建、并行构建) |
| 灵活性 | 一般 | 高 |
| 依赖管理 | 成熟稳定 | 同样强大 |
| IDE 支持 | 非常好 | 非常好 |
| 生态 | 非常成熟 | 快速发展中 |
| XML 冗长 | 是 | 否(DSL 更简洁) |
| 增量构建 | 支持但较简单 | 智能增量构建 |
| 构建缓存 | 有限 | 强大的构建缓存 |
| Android 支持 | 官方不支持 | 官方首选 |
| 社区 | 超大 | 大 |
36.2.9 Gradle 的独门绝技
Gradle 有几个 Maven 难以比拟的优势:
1. 增量构建(Incremental Build) Gradle 只会重新构建有变化的部分,而不是像 Maven 那样每次都重新编译所有内容。这在大型项目中可以节省大量时间。
2. 构建缓存与共享缓存 Gradle 可以缓存构建结果,甚至可以在不同机器间共享缓存。配合 CI/CD 使用,效果拔群。
3. 配置缓存(Configuration Cache) Gradle 可以缓存任务配置的结果,使得后续构建启动更快。
4. 并行执行 Gradle 可以并行执行相互独立的任务,充分利用多核 CPU。
| |
36.2.10 从 Maven 迁移到 Gradle
如果你有一个 Maven 项目,想迁移到 Gradle,Gradle 提供了一个便捷的命令:
| |
迁移时需要注意以下几点:
- Maven 的
<properties>映射为 Gradle 的ext或直接在根作用域定义 - Maven 的
<dependencies>直接映射为dependencies块 - Maven 的
<plugins>映射为plugins块 - Maven 的
<profile>(不同环境不同配置)需要用 Gradle 的 Task 或 Source Set 重构
36.3 Maven 与 Gradle 结构图对比
┌────────────────────────────────────────────────────────────────────┐
│ 构建工具全景对比图 │
├────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Maven │ │
│ │ ┌─────────────────────────────────────────────────────┐ │ │
│ │ │ pom.xml │ │ │
│ │ │ ┌───────────┐ ┌───────────┐ ┌───────────────┐ │ │ │
│ │ │ │ <groupId> │ │<artifactId>│ │ <version> │ │ │ │
│ │ │ └───────────┘ └───────────┘ └───────────────┘ │ │ │
│ │ │ ▲ ▲ ▲ │ │ │
│ │ │ └──────────────┴───────────────┘ │ │ │
│ │ │ 坐标系统 │ │ │
│ │ └─────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌─────────────────────────────────────────────────────┐ │ │
│ │ │ 生命周期(Lifecycle) │ │ │
│ │ │ compile → test → package → install → deploy │ │ │
│ │ └─────────────────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ VS │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Gradle │ │
│ │ ┌─────────────────────────────────────────────────────┐ │ │
│ │ │ build.gradle / .kts │ │ │
│ │ │ plugins { java } │ │ │
│ │ │ dependencies { implementation '...' } │ │ │
│ │ │ tasks.register('hello') { │ │ │
│ │ │ doLast { println 'Hello!' } │ │ │
│ │ │ } │ │ │
│ │ └─────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌─────────────────────────────────────────────────────┐ │ │
│ │ │ Task 图(DAG) │ │ │
│ │ │ [clean] ──► [compile] ──► [test] ──► [jar] │ │ │
│ │ │ └──► [build] ◄───────────────────────── │ │ │
│ │ └─────────────────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
└────────────────────────────────────────────────────────────────────┘
36.4 实战:创建一个 Spring Boot 项目
36.4.1 使用 Maven 创建
| |
或者手动创建 pom.xml:
| |
| |
| |
36.4.2 使用 Gradle 创建
| |
| |
| |
本章小结
本章我们学习了 Java 项目构建工具的核心知识:
| 概念 | 说明 |
|---|---|
| Maven | Apache 出品的经典构建工具,使用 pom.xml 和 XML 配置,约定优于配置,生态成熟 |
| Gradle | 新一代构建工具,使用 Groovy/Kotlin DSL,更加灵活,构建速度更快 |
| POM | Maven 的项目对象模型,定义项目坐标、依赖、插件等 |
| 坐标(Coordinates) | groupId:artifactId:version,唯一标识 Maven 构件 |
| 仓库(Repository) | 存储依赖的地方,分本地仓库和远程仓库 |
| 依赖作用域(Scope) | Maven 区分依赖使用范围的方式:compile、test、provided 等 |
| Configuration | Gradle 区分依赖使用范围的方式:implementation、api、testImplementation 等 |
| Task | Gradle 的基本工作单元,类似 Maven 的 goal |
| 增量构建 | Gradle 的核心优势,只构建变化的部分,节省时间 |
实战要点:
- 选择建议:新项目推荐 Gradle,老项目或团队熟悉 Maven 则继续用 Maven
- 依赖冲突:使用
mvn dependency:tree或gradle dependencies排查 - 构建速度:Gradle 的增量构建和并行执行在大项目中优势明显
- 团队协作:使用 Maven Wrapper 或 Gradle Wrapper 确保版本一致
“工具没有最好,只有最适合。” 理解 Maven 和 Gradle 的设计哲学,才能在不同的项目中做出正确的选择。下一章我们将学习 Java 自动化测试,敬请期待!