Maven 项目构建与依赖管理
本文系统讲解 Maven 项目构建与依赖管理,覆盖 Maven 安装、IDEA 集成、坐标、仓库、依赖范围、生命周期、插件、多模块、继承、聚合、版本锁定、私服和 Maven Helper 可视化依赖分析。文章面向真实 Java 项目开发,帮助读者理解 Maven 如何组织、构建、测试、打包和发布项目。
第一章 Maven 概述
Maven 是 Apache 提供的 Java 项目构建与依赖管理工具,通过 pom.xml 统一管理依赖、构建流程和项目结构。你可以把它理解成 Java 项目的构建标准和依赖管理中心。
Maven 的核心价值:
- 自动下载和管理依赖,减少手动维护 jar 包。
- 统一编译、测试、打包、安装、发布流程。
- 降低依赖冲突、版本不一致和环境差异问题。
- 支持多模块、继承、聚合等企业项目常见场景。
先认识 6 个高频关键词:
pom.xml:Maven 项目的核心配置文件。- 坐标:定位一个 jar 包或模块的唯一标识。
- 仓库:存放依赖包的位置,包括本地仓库和远程仓库。
- 依赖:当前项目需要使用的第三方库。
- 生命周期:Maven 内置的一套构建流程。
- 插件:真正执行编译、测试、打包等任务的工具。
第二章 Maven 安装
2.1 安装前准备
Maven 依赖 JDK,所以安装 Maven 前要先确认 JDK 配置成功:
java -version
javac -version
如果能看到版本号,说明 JDK 环境基本可用。
2.2 下载和解压 Maven
- 进入 Apache Maven 官网下载 Maven 压缩包。
- 解压到一个没有中文和空格的目录,例如:
D:\develop\apache-maven-3.9.6。 - 记住这个路径,后面配置环境变量和 IDEA 都会用到。
2.3 配置环境变量
Windows 中常见配置:
| 变量 | 示例 |
|---|---|
MAVEN_HOME | D:\develop\apache-maven-3.9.6 |
Path | %MAVEN_HOME%\bin |
配置完成后,重新打开命令行:
mvn -v
如果能看到 Maven 版本、Java 版本、系统信息,就说明安装成功。
2.4 配置本地仓库
Maven 下载的 jar 包默认会放到用户目录下的 .m2/repository。为了方便管理,可以在 conf/settings.xml 中配置本地仓库:
<localRepository>D:\maven-repository</localRepository>
本地仓库就是你电脑上的 jar 包缓存。项目第一次使用某个依赖时,Maven 会从远程仓库下载;以后再用相同依赖,就直接从本地仓库读取。
2.5 配置国内镜像
为了加快依赖下载速度,可以在 settings.xml 中配置镜像:
<mirrors>
<mirror>
<id>aliyunmaven</id>
<mirrorOf>central</mirrorOf>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
</mirrors>
镜像的作用是替代默认中央仓库下载依赖。网络不稳定时,这一步很重要。
第三章 IDEA 集成 Maven
3.1 IDEA 配置 Maven
在 IDEA 中进入:
Settings -> Build, Execution, Deployment -> Build Tools -> Maven
重点检查:
| 配置项 | 建议 |
|---|---|
| Maven home path | 选择自己安装的 Maven 路径 |
| User settings file | 选择 Maven 的 settings.xml |
| Local repository | 自动读取或手动选择本地仓库 |
配置完成后,IDEA 创建和导入 Maven 项目时就会按你的 Maven 环境工作。
3.2 创建 Maven 项目
在 IDEA 中创建普通 Maven 项目的基本步骤:
New Project。- 选择 Maven。
- 设置 JDK。
- 填写项目名称和保存位置。
- 创建完成后等待 Maven 加载。
普通 Maven 项目通常包含:
project-name
├── pom.xml
└── src
├── main
│ ├── java
│ └── resources
└── test
├── java
└── resources
目录含义:
| 目录 | 作用 |
|---|---|
src/main/java | 存放正式 Java 代码 |
src/main/resources | 存放正式配置文件 |
src/test/java | 存放测试 Java 代码 |
src/test/resources | 存放测试配置文件 |
pom.xml | Maven 项目配置文件 |
3.3 导入 Maven 项目
导入别人给你的 Maven 项目时,一般不要直接新建项目,而是打开包含 pom.xml 的目录。
常见步骤:
File->Open。- 选择项目根目录。
- 等待 IDEA 识别
pom.xml。 - 点击 Maven 面板中的刷新按钮。
- 等待依赖下载完成。
如果导入后代码大面积报红,优先检查 Maven 配置、JDK 配置、本地仓库、网络和 pom.xml 是否正确。
3.4 Maven 坐标
Maven 通过坐标唯一定位一个项目或 jar 包。坐标由三个核心元素组成:
<groupId>com.example</groupId>
<artifactId>maven-demo</artifactId>
<version>1.0.0</version>
含义如下:
| 元素 | 说明 | 示例 |
|---|---|---|
groupId | 组织或公司,一般使用域名倒写 | com.example |
artifactId | 项目名或模块名 | maven-demo |
version | 版本号 | 1.0.0 |
依赖配置也是使用坐标:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.14.0</version>
</dependency>
第四章 Maven 依赖管理
4.1 依赖配置
依赖统一写在 pom.xml 的 <dependencies> 标签中:
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.14.0</version>
</dependency>
</dependencies>
写完依赖后,IDEA 通常会自动刷新 Maven。如果没有自动刷新,可以手动点击 Maven 面板的刷新按钮。
4.2 传递依赖
如果 A 项目依赖 B,B 又依赖 C,那么 A 通常也能间接使用 C。这叫传递依赖。
传递依赖的好处是不用手动把所有相关 jar 都写出来。坏处是项目大了之后,可能出现版本冲突,所以要学会排除依赖和版本锁定。
4.3 排除依赖
如果某个传递依赖不想引入,可以使用 <exclusions>:
<dependency>
<groupId>com.example</groupId>
<artifactId>demo-service</artifactId>
<version>1.0.0</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
排除依赖常用于解决日志框架冲突、旧版本 jar 冲突等问题。
4.4 依赖范围 scope
scope 控制依赖在哪些阶段可用。
| scope | 编译 | 测试 | 运行 | 典型场景 |
|---|---|---|---|---|
compile | 是 | 是 | 是 | 默认范围,普通业务依赖 |
provided | 是 | 是 | 否 | Servlet API,运行时由容器提供 |
runtime | 否 | 是 | 是 | MySQL 驱动 |
test | 否 | 是 | 否 | JUnit、Mockito |
system | 是 | 是 | 否 | 本地系统 jar,不推荐 |
import | 否 | 否 | 否 | 导入 BOM,只用于 dependencyManagement |
测试框架只在测试阶段使用,不应该进入正式运行环境,所以 JUnit 等依赖通常使用 test 范围。
4.5 版本冲突处理
Maven 处理传递依赖版本冲突时,有两个常见原则:
| 原则 | 说明 |
|---|---|
| 路径最短优先 | 谁离当前项目更近,优先使用谁 |
| 声明顺序优先 | 路径一样长时,先声明的依赖优先 |
企业项目中不建议完全依赖 Maven 自动选择版本,更推荐使用父工程的 <dependencyManagement> 统一锁定版本。
第五章 Maven 生命周期
5.1 生命周期是什么
Maven 生命周期是一套固定的构建流程。你执行某个阶段时,Maven 会自动执行它之前的阶段。
常用生命周期阶段:
| 阶段 | 作用 |
|---|---|
clean | 清理构建输出 |
validate | 校验项目是否正确 |
compile | 编译主代码 |
test | 运行单元测试 |
package | 打包 |
verify | 检查包是否有效 |
install | 安装到本地仓库 |
deploy | 发布到远程仓库 |
5.2 常用命令
mvn clean
mvn compile
mvn test
mvn package
mvn install
执行 mvn package 时,Maven 会先编译,再测试,最后打包。执行 mvn install 时,会在打包后把构件安装到本地仓库,方便其他本地项目引用。
5.3 跳过测试
如果临时只想打包,不运行测试:
mvn package -DskipTests
如果连测试代码也不编译:
mvn package -Dmaven.test.skip=true
日常开发不建议长期跳过测试。测试失败往往说明代码存在问题,跳过只是临时手段。
5.4 Maven 插件
Maven 的生命周期只是流程,真正做事的是插件。比如:
| 插件 | 作用 |
|---|---|
maven-compiler-plugin | 编译 Java 代码 |
maven-surefire-plugin | 运行单元测试 |
maven-jar-plugin | 打 jar 包 |
maven-clean-plugin | 清理构建目录 |
配置 Java 编译版本示例:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<source>17</source>
<target>17</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
第六章 Maven 常见问题解决方案
6.1 依赖下载失败
常见原因:
- 网络不稳定。
- 镜像配置错误。
- 本地仓库中存在下载失败的残缺文件。
- 依赖坐标或版本写错。
解决思路:
- 检查
settings.xml镜像配置。 - 检查
pom.xml坐标是否正确。 - 删除本地仓库中对应依赖目录后重新刷新。
- 使用
mvn -U clean package强制更新快照和依赖。
6.2 IDEA 中依赖报红
可以按顺序检查:
- Maven home path 是否正确。
- User settings file 是否正确。
- JDK 是否正确。
- Maven 面板是否刷新。
- 本地仓库是否有损坏文件。
6.3 编译版本不匹配
如果出现 “不支持发行版本” 之类的错误,通常是 JDK 版本和 Maven 编译配置不一致。
建议在 pom.xml 中明确配置:
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
6.4 测试运行失败但想先打包
临时跳过测试:
mvn package -DskipTests
长期建议还是修复测试。测试失败不是 Maven 的问题,而是构建过程提醒你代码行为不符合预期。
6.5 依赖冲突排查
查看依赖树:
mvn dependency:tree
如果要搜索某个依赖:
mvn dependency:tree -Dincludes=org.slf4j
依赖树能帮助你看清楚某个 jar 是从哪里被传递进来的。
第七章 Maven 高级:分模块设计与开发
7.1 什么是分模块设计与为什么要分模块
什么是分模块设计? 将项目按照功能/模块拆分成若干个子模块。
为什么要分模块设计? 小项目可以只有一个模块,但企业项目通常会越来越大。如果所有 Controller、Service、Mapper、工具类都放在一个模块中,维护会越来越困难。 分模块可以带来:
- 方便项目的管理维护、扩展。
- 方便模块间的相互引用、资源共享。
- 职责清晰,构建边界明确,团队协作更方便。
注意事项:
- 分模块设计需要先针对模块功能进行设计,然后再进行编码。不会先将工程开发完毕,然后再进行拆分。
7.2 常见模块划分
一个简单后台项目可以这样划分:
shop-parent
├── shop-common
├── shop-pojo
├── shop-mapper
├── shop-service
└── shop-web
模块职责:
| 模块 | 作用 |
|---|---|
shop-common | 公共工具、通用异常、统一返回结果 |
shop-pojo | 实体类、DTO、VO |
shop-mapper | 数据访问层 |
shop-service | 业务逻辑层 |
shop-web | Controller、启动类、接口入口 |
7.3 模块之间的依赖关系
一般依赖方向是从上层依赖下层:
shop-web -> shop-service -> shop-mapper -> shop-pojo
shop-service -> shop-common
shop-web -> shop-common
不要让底层反过来依赖上层。比如 shop-common 不应该依赖 shop-web,否则公共模块就不再公共了。
7.4 子模块依赖示例
shop-web 依赖 shop-service:
<dependency>
<groupId>com.example</groupId>
<artifactId>shop-service</artifactId>
<version>1.0.0</version>
</dependency>
在同一个多模块工程中,执行父工程的 mvn install 时,Maven 会按模块依赖关系构建。
第八章 Maven 高级:继承关系
8.1 Maven 继承是什么
Maven 中子工程可以继承父工程配置。父工程通常负责统一:
- 项目版本。
- Java 编译版本。
- 依赖版本。
- 插件版本。
- 仓库配置。
- 公共属性。
子模块继承父工程后,就不需要每个模块重复写相同配置。
8.2 父工程配置与继承关系实现步骤
Maven 的继承关系实现步骤:
- 创建父工程,设置打包方式为
pom,并继承spring-boot-starter-parent(如果是 Spring Boot 项目)。 - 在子工程中配置继承关系。
- 在父工程中配置各个工程的共有依赖。
父工程 pom.xml 的打包方式通常是 pom:
<project>
<modelVersion>4.0.0</modelVersion>
<!-- 1. 继承 spring-boot-starter-parent -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>shop-parent</artifactId>
<version>1.0.0</version>
<!-- 1. 设置打包方式为 pom -->
<packaging>pom</packaging>
<properties>
<java.version>17</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!-- 3. 配置各个工程的共有依赖 -->
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>
8.3 子工程继承父工程
子模块中使用 <parent> 配置继承关系:
<project>
<modelVersion>4.0.0</modelVersion>
<!-- 2. 在子工程中配置继承关系 -->
<parent>
<groupId>com.example</groupId>
<artifactId>shop-parent</artifactId>
<version>1.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>shop-service</artifactId>
</project>
继承关系注意事项:
- 在子工程中,配置了继承关系之后,坐标中的
groupId是可以省略的,因为会自动继承父工程的。 <relativePath>指定父工程的pom.xml文件的相对位置(如果不指定,将从本地仓库/远程仓库查找)。- 若父子工程都配置了同一个依赖的不同版本,以子工程的为准。
第九章 Maven 高级:版本锁定
9.1 dependencyManagement 与自定义属性
在 Maven 中,可以在父工程的 pom 文件中通过 <dependencyManagement> 来统一管理依赖版本。为了更好维护,通常会结合自定义属性/引用属性来提取版本号。
父工程示例:
<!-- 自定义属性/引用属性 -->
<properties>
<lombok.version>1.18.32</lombok.version>
<jjwt.version>0.12.6</jjwt.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>${jjwt.version}</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>${jjwt.version}</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>${jjwt.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
子模块真正使用依赖时:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<scope>runtime</scope>
</dependency>
子模块不用写版本,版本由父工程统一决定。
说明:早期很多博客会直接写单体依赖io.jsonwebtoken:jjwt。在现代项目中,更常见的是按jjwt-api / jjwt-impl / jjwt-jackson拆分引入,和 Spring Boot 3.x、JDK 17 的主流写法更一致。
9.2 <dependencyManagement> 与 <dependencies> 的区别是什么?
<dependencies>是直接依赖,在父工程配置了依赖,子工程会直接继承下来。<dependencyManagement>是统一管理依赖版本,不会直接依赖,还需要在子工程中引入所需依赖(无需指定版本)。
初学时很容易把这两个标签混淆。记住一句话:dependencyManagement 只管版本,不代表真的使用。
9.3 BOM
BOM 是一种特殊的版本管理 POM,常用于统一一组依赖版本。例如 Spring Boot 父工程就帮我们管理了大量依赖版本。
导入 BOM 的写法:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>3.3.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
导入后,Spring Boot 常用依赖就可以省略版本号。
第十章 Maven 高级:聚合
10.1 聚合是什么
聚合用于一次性构建多个模块。父工程通过 <modules> 声明子模块:
<modules>
<module>shop-common</module>
<module>shop-pojo</module>
<module>shop-mapper</module>
<module>shop-service</module>
<module>shop-web</module>
</modules>
在父工程目录执行:
mvn clean install
Maven 会按照模块依赖关系自动决定构建顺序。
10.2 继承和聚合的区别
| 对比 | 继承 | 聚合 |
|---|---|---|
| 目的 | 复用父工程配置 | 一次构建多个模块 |
| 核心标签 | <parent> | <modules> |
| 关注点 | 配置统一 | 构建统一 |
| 是否必须一起使用 | 不必须 | 不必须 |
实际项目中,父工程经常同时承担继承和聚合两个角色。
10.3 聚合构建常见命令
mvn clean install
mvn clean package
mvn -pl shop-web -am package
其中:
-pl shop-web表示只构建指定模块。-am表示同时构建它依赖的模块。
这在大型项目中很有用,不用每次都完整构建所有模块。
第十一章 Maven 高级:私服介绍
11.1 Maven 私服的作用
Maven 私服是公司内部搭建的仓库服务器,常见产品有 Nexus、Artifactory。它的核心价值有三点:
- 缓存外部依赖,提升下载速度,降低对中央仓库的依赖。
- 管理公司内部构件,方便统一发布和复用。
- 统一依赖入口,便于控制依赖来源、版本和安全性。
私服相当于公司自己的 Maven 中转站。
11.2 常见仓库类型
| 类型 | 说明 |
|---|---|
| hosted | 存放公司内部发布的构件 |
| proxy | 代理中央仓库或第三方仓库 |
| group | 把多个仓库组合成一个统一入口 |
开发者通常只需要配置 group 仓库地址,私服内部再决定去 hosted 还是 proxy 查找依赖。
第十二章 Maven 高级:私服资源上传与下载
12.1 从私服下载依赖
通常在 settings.xml 中配置镜像或仓库认证,然后 Maven 就可以从私服下载依赖。
认证配置示例:
<servers>
<server>
<id>company-maven</id>
<username>developer</username>
<password>password</password>
</server>
</servers>
id 要和项目中仓库配置的 id 对应。
12.2 发布到私服
项目中配置发布地址:
<distributionManagement>
<!-- 发布正式版,对应不带 SNAPSHOT 的稳定版本 -->
<repository>
<id>company-maven</id>
<name>Company Releases</name>
<url>http://maven.example.com/repository/releases/</url>
</repository>
<!-- 发布开发版,对应带 -SNAPSHOT 的迭代版本 -->
<snapshotRepository>
<id>company-maven</id>
<name>Company Snapshots</name>
<url>http://maven.example.com/repository/snapshots/</url>
</snapshotRepository>
</distributionManagement>
发布命令:
mvn clean deploy
12.3 SNAPSHOT 和 RELEASE
| 类型 | 说明 |
|---|---|
SNAPSHOT | 快照版本,开发中,可能不断变化 |
RELEASE | 发布版本,稳定后不应再改变 |
示例:
<version>1.0.0-SNAPSHOT</version>
开发阶段使用 SNAPSHOT,正式发布使用 1.0.0 这样的稳定版本。
第十三章 Maven 可视化与依赖分析
Maven 虽然主要通过命令行工作,但开发者日常排查问题时,很少只盯着命令行输出。更常见的做法是借助 IDEA 的 Maven 面板、依赖图和 Maven Helper 插件,把依赖关系、生命周期和冲突信息直观地看出来。
这一章重点不是“好看”,而是让你知道遇到依赖冲突、jar 包重复、版本不生效时,应该从哪里下手。
13.1 IDEA 自带 Maven 面板
IDEA 右侧的 Maven 面板是最基础的 Maven 可视化入口。它会根据项目中的 pom.xml 展示 Maven 工程结构。
常用功能如下:
| 功能 | 作用 |
|---|---|
| Reload All Maven Projects | 重新加载 pom.xml,刷新依赖和插件配置 |
| Lifecycle | 双击执行 clean、compile、test、package、install 等阶段 |
| Plugins | 查看并执行 Maven 插件目标 |
| Dependencies | 查看当前模块依赖 |
| Profiles | 勾选或取消不同环境的 profile |
例如双击 package,效果类似于执行:
mvn package
如果修改了 pom.xml 后依赖仍然报红,第一步通常就是点击 Maven 面板里的刷新按钮。
13.2 开发者常用工具:Maven Helper
在 IDEA 中,开发者使用非常多的 Maven 可视化工具是 Maven Helper 插件。它最大的价值是依赖分析,尤其适合排查依赖冲突。
安装后,打开 pom.xml,通常会多出一个 Dependency Analyzer 视图。这个视图可以把依赖按不同方式展示出来:
| 视图 | 作用 |
|---|---|
| Conflicts | 只看有版本冲突的依赖 |
| All Dependencies as List | 以列表方式查看全部依赖 |
| All Dependencies as Tree | 以树形结构查看依赖来源 |
实际开发中,最常用的是 Conflicts。因为很多 Maven 问题不是“没有依赖”,而是“依赖存在,但版本不是你以为的那个”。
13.3 Maven Helper 怎么排查冲突
假设项目中出现了 jackson-databind 版本冲突,可以按下面步骤排查:
- 打开当前模块的
pom.xml。 - 切换到
Dependency Analyzer。 - 选择
Conflicts。 - 搜索
jackson-databind。 - 查看它分别是被哪些依赖传递引入的。
- 判断应该统一版本,还是排除某个传递依赖。
如果确认某个传递依赖不应该进来,可以在对应依赖下面添加排除:
<dependency>
<groupId>com.example</groupId>
<artifactId>demo-starter</artifactId>
<version>1.0.0</version>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</exclusion>
</exclusions>
</dependency>
Maven Helper 的好处是能快速告诉你“冲突从哪里来”。但是否真的要排除依赖,需要结合业务和框架版本判断,不能看到冲突就盲目删除。
13.4 命令行依赖树
可视化工具很好用,但命令行仍然是最稳定、最通用的排查方式。
查看完整依赖树:
mvn dependency:tree
只查看某个依赖:
mvn dependency:tree -Dincludes=org.slf4j:slf4j-api
输出中常见结构如下:
com.example:demo:jar:1.0.0
\- org.springframework.boot:spring-boot-starter-web:jar:3.2.0:compile
\- org.springframework:spring-webmvc:jar:6.1.1:compile
这表示 spring-webmvc 是通过 spring-boot-starter-web 传递进来的。
13.5 依赖图应该怎么看
无论使用 Maven Helper、IDEA Diagram,还是命令行依赖树,本质上都是在看三件事:
- 当前项目直接声明了哪些依赖。
- 每个直接依赖又传递引入了哪些依赖。
- 最终 Maven 选择了哪个版本。
示例:
当前项目
├── spring-boot-starter-web
│ ├── spring-web
│ └── spring-webmvc
└── mybatis-spring-boot-starter
├── mybatis
└── mybatis-spring
如果某个 jar 不是你在 pom.xml 里直接写的,那它大概率是被上层依赖传递引入的。排查时不要只找当前 pom.xml,还要看它的父级依赖。
13.6 有效 POM
项目最终生效的 Maven 配置,不一定只来自当前 pom.xml。它还可能来自父工程、默认超级 POM、dependencyManagement、pluginManagement 和 profile。
查看最终生效的 POM:
mvn help:effective-pom
输出到文件:
mvn help:effective-pom -Doutput=effective-pom.xml
当你不确定某个依赖版本、插件版本、仓库地址到底从哪里来的时候,可以查看有效 POM。
13.7 常见排查场景
| 场景 | 推荐方式 |
|---|---|
| IDEA 依赖报红 | Maven 面板刷新项目 |
| jar 包版本冲突 | Maven Helper 的 Conflicts |
| 不知道依赖从哪里来 | Maven Helper 树形依赖或 mvn dependency:tree |
| 不知道版本为什么生效 | mvn help:effective-pom |
| 多模块构建顺序不清楚 | IDEA Maven 面板或 mvn -pl 模块名 -am package |
| 插件执行失败 | Maven 面板、Run 日志、命令行错误信息 |
日常开发中可以记住一个顺序:先用 IDEA 或 Maven Helper 快速定位,再用 Maven 命令确认结果。这样既直观,也方便把问题复现给其他同事。
总结
Maven 是 Java 后端开发的基础设施。它本身不直接写业务,但它决定了项目能不能稳定地引入依赖、编译、测试、打包和发布。
你需要重点掌握:
- Maven 的作用:项目构建、依赖管理、项目管理。
- Maven 坐标:
groupId、artifactId、version。 - Maven 仓库:本地仓库、中央仓库、镜像、私服。
- Maven 依赖:依赖配置、传递依赖、排除依赖、依赖范围。
- Maven 生命周期:
clean、compile、test、package、install、deploy。 - Maven 高级:分模块、继承、版本锁定、聚合、私服上传下载。
- Maven 可视化:IDEA Maven 面板、Maven Helper、依赖树、依赖图和有效 POM。
掌握 Maven 后,再学习 Spring Boot、MyBatis、Spring Cloud 等后端技术会顺畅很多。因为你不只是会写代码,也知道一个 Java 项目是如何被组织、构建和交付的。