第 45 章:Maven 实战
学习目标
- 掌握 Maven 坐标与依赖管理
- 理解依赖传递与冲突解决
- 掌握多模块项目结构
- 学会配置国内镜像加速
一、Maven 是什么?
三大核心能力:
| 能力 | 解决的问题 |
|---|---|
| 依赖管理 | 不用手动下 jar 包,声明坐标自动下载(含依赖的依赖) |
| 项目构建 | 标准化的编译、测试、打包、部署流程 |
| 项目结构规范 | 所有 Java 项目长得一样,换项目零学习成本 |
二、标准目录结构
my-project/
├── pom.xml # 项目描述文件
├── src/
│ ├── main/
│ │ ├── java/ # 主代码
│ │ └── resources/ # 配置文件(打包进 jar 根目录)
│ └── test/
│ ├── java/ # 测试代码(不打包)
│ └── resources/ # 测试配置
└── target/ # 构建产物(git 忽略)约定优于配置:Maven 默认就知道去这些位置找文件,不用配置。
三、坐标:GAV
xml
<groupId>com.taskflow</groupId> <!-- ① 组织标识,通常是倒写域名 -->
<artifactId>taskflow-api</artifactId> <!-- ② 项目名 -->
<version>1.0.0-SNAPSHOT</version> <!-- ③ 版本号 -->
<packaging>jar</packaging> <!-- ④ jar / war / pom -->版本号规范(语义化版本):
主版本号.次版本号.修订号[-限定符]
1 . 2 . 3 -SNAPSHOT
主版本号:不兼容的 API 变更
次版本号:向下兼容的功能新增
修订号 :向下兼容的 bug 修复
SNAPSHOT:开发中版本,每次构建都会重新拉取最新的
RELEASE :正式版,不可变四、依赖范围 scope
xml
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>| scope | 编译期 | 测试期 | 运行期 | 打包 | 典型场景 |
|---|---|---|---|---|---|
compile(默认) | ✅ | ✅ | ✅ | ✅ | 绝大多数依赖 |
provided | ✅ | ✅ | ❌ | ❌ | Lombok、Servlet API(容器提供) |
runtime | ❌ | ✅ | ✅ | ✅ | JDBC 驱动(编译时用不到) |
test | ❌ | ✅ | ❌ | ❌ | JUnit、Mockito |
import | - | - | - | - | 只用于 dependencyManagement 导入 BOM |
五、依赖传递与冲突
仲裁规则
- 路径最短优先:
A→B→C(1.0)和A→D→E→C(2.0),选 1.0 - 路径相同则先声明优先:pom 中写在前面的赢
⚠️ 注意规则 1 的坑:版本高低不影响选择,很可能选到旧版本导致
NoSuchMethodError。
排查与解决
bash
# ① 看依赖树,找出冲突来源
mvn dependency:tree
# ② 只看某个依赖的引入路径
mvn dependency:tree -Dincludes=com.fasterxml.jackson.core:jackson-databind
# ③ 分析未使用/缺失的依赖
mvn dependency:analyzexml
<!-- 方案 1:排除传递依赖 -->
<dependency>
<groupId>com.example</groupId>
<artifactId>some-lib</artifactId>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 方案 2:直接声明(路径最短,一定生效)—— 推荐 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.3</version>
</dependency>六、依赖管理与 BOM
xml
<!-- 父 pom:只声明版本,不引入依赖 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.5</version>
</dependency>
</dependencies>
</dependencyManagement>
<!-- 子模块:不写版本号,继承父 pom 的版本 -->
<dependencies>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
</dependencies>好处:升级版本只改父 pom 一处,所有子模块统一。避免同一个库在不同模块用不同版本。
Spring Boot 的 BOM:
xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
</parent>
<!-- 引入 Spring 相关依赖时都不用写版本号,Spring Boot 已经测试过兼容性 -->七、生命周期与常用命令
重要:执行后面的阶段会自动执行前面所有阶段。
mvn package会先 compile 和 test。
bash
mvn clean # 删除 target
mvn compile # 编译
mvn test # 跑测试
mvn package # 打 jar/war
mvn install # 安装到本地仓库 ~/.m2
mvn deploy # 发布到远程仓库
# 常用组合
mvn clean package -DskipTests # 打包跳过测试(-DskipTests 编译测试代码但不跑)
mvn clean install -Dmaven.test.skip=true # 连测试代码都不编译
# 指定环境打包
mvn clean package -Pprod
# 并行构建(多模块项目提速明显)
mvn clean package -T 4八、多模块项目
taskflow/
├── pom.xml # 父 pom,packaging = pom
├── taskflow-common/ # 公共工具、常量、异常
├── taskflow-domain/ # 实体、DTO、VO
├── taskflow-dao/ # Mapper、数据访问
├── taskflow-service/ # 业务逻辑
└── taskflow-web/ # Controller、启动类父 pom:
xml
<groupId>com.taskflow</groupId>
<artifactId>taskflow</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging> <!-- ① 父模块必须是 pom -->
<modules> <!-- ② 声明子模块 -->
<module>taskflow-common</module>
<module>taskflow-domain</module>
<module>taskflow-dao</module>
<module>taskflow-service</module>
<module>taskflow-web</module>
</modules>
<properties>
<java.version>17</java.version>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<mybatis-plus.version>3.5.5</mybatis-plus.version>
</properties>子模块:
xml
<parent>
<groupId>com.taskflow</groupId>
<artifactId>taskflow</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>taskflow-service</artifactId> <!-- groupId 和 version 继承父 pom -->
<dependencies>
<dependency>
<groupId>com.taskflow</groupId>
<artifactId>taskflow-dao</artifactId>
<version>${project.version}</version> <!-- ③ 引用兄弟模块 -->
</dependency>
</dependencies>依赖只能单向。如果出现 service 依赖 web,说明分层错了。
九、多环境 profile
xml
<profiles>
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault> <!-- ① 默认激活 -->
</activation>
<properties>
<env>dev</env>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<env>prod</env>
</properties>
</profile>
</profiles>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering> <!-- ② 开启变量替换 -->
</resource>
</resources>
</build>application.yml 中就能用 @env@(Spring Boot 把 Maven 占位符改成了 @,避免和 ${} 冲突):
yaml
spring:
profiles:
active: @env@十、配置国内镜像(必做)
编辑 ~/.m2/settings.xml(Windows:C:\Users\你的用户名\.m2\settings.xml):
xml
<settings>
<!-- ① 本地仓库位置(可选,默认 ~/.m2/repository) -->
<localRepository>D:/maven-repo</localRepository>
<mirrors>
<mirror>
<id>aliyun</id>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
<mirrorOf>central</mirrorOf> <!-- ② 只镜像中央仓库 -->
</mirror>
</mirrors>
<profiles>
<profile>
<id>jdk17</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>17</jdk>
</activation>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
</profile>
</profiles>
</settings>⚠️
<mirrorOf>不要写*!写*会把所有仓库(包括公司私服)都指向阿里云,导致私服上的包下不下来。
十一、常见问题
bash
# 问题 1:依赖下不下来 / 下了一半损坏
# 清理 .lastUpdated 文件后重试
find ~/.m2 -name "*.lastUpdated" -delete
mvn clean install -U # -U 强制更新 SNAPSHOT
# 问题 2:中文乱码
# 在 pom 的 properties 里加:
# <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
# 问题 3:IDEA 改了 pom 不生效
# 右键项目 → Maven → Reload Project
# 问题 4:想看最终生效的完整 pom(含继承)
mvn help:effective-pom十二、本章小结
| 要点 | 关键 |
|---|---|
| 坐标 GAV | groupId + artifactId + version |
| scope | Lombok 用 provided,测试用 test |
| 依赖冲突 | 路径最短优先,用 dependency:tree 排查 |
dependencyManagement | 统一管版本,子模块不写版本号 |
| 多模块 | 依赖单向,父 pom 用 packaging=pom |
| 镜像 | 阿里云,mirrorOf 不要写 * |
| 提速 | -T 4 并行构建 |
动手练习
练习 1:基础题
创建一个三模块 Maven 项目(common / service / web),在 web 中调用 service 的方法,用 mvn clean package 打包成功。
练习 2:进阶题
用 mvn dependency:tree 分析一个 Spring Boot 项目,找出被引入了两个不同版本的依赖,并用 <exclusion> 解决冲突。
下一章:第 46 章:项目结构规范 →