Maven进阶
Maven 是一个 项目管理工具,主要解决三件事:
- 依赖管理
- 项目构建
- 项目信息管理
帮 Java 项目统一"怎么下依赖、怎么编译、怎么测试、怎么打包、怎么发布"的工具。
约定大于配置
Maven 非常强调这个思想。
比如它约定:
- 主代码放
src/main/java - 资源文件放
src/main/resources - 测试代码放
src/test/java - 测试资源放
src/test/resources - 打包输出到
target
所以你只要按规范来,很多配置根本不用写。
这就是为什么 Maven 项目结构几乎长得都一样。
依赖坐标唯一标识
Maven 里的一个 jar,不是靠文件名识别,而是靠一组坐标:
groupIdartifactIdversion
例如:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.30</version>
</dependency>
这三个东西共同确定一个依赖。
面试里经常问:
Maven 如何唯一确定一个构件?
答案就是:
GAV 坐标:groupId + artifactId + version
Maven 的安装与目录结构
Maven 本身是 Java 写的,所以运行 Maven 需要 JDK。
一般先配:
JAVA_HOMEMAVEN_HOMEPATH
验证命令:
mvn -v
bin:启动脚本conf:全局配置文件,比如settings.xmllib:Maven 自己依赖的 jar
Maven 项目的标准目录结构
java
project
├── pom.xml
├── src
│ ├── main
│ │ ├── java
│ │ ├── resources
│ │ └── webapp
│ └── test
│ ├── java
│ └── resources
└── target
pom.xml 是什么
pom 全称:
Project Object Model
xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.demo</groupId>
<artifactId>maven-demo</artifactId>
<version>1.0-SNAPSHOT</version>
</project>
modelVersion
POM 模型版本,通常固定 4.0.0。
groupId
组织、公司、团队、项目域名反写。
比如:
<groupId>com.company.project</groupId>
artifactId
模块名、项目名。
比如:
<artifactId>user-service</artifactId>
version
当前项目版本。
例如:
1.0.01.0-SNAPSHOT
packaging
表示打包方式。
常见值:
jarwarpom
例子:
<packaging>jar</packaging>
jar、war、pom 区别?
jar:普通 Java 项目war:Web 项目pom:父工程/聚合工程,不直接放业务代码打包运行
依赖管理 dependencies
基本写法
xml
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.30</version>
</dependency>
</dependencies>
依赖就是:
当前项目在编译、测试、运行过程中需要用到的外部构件。
依赖的关键属性
| 属性名 | 是否必须 | 作用 | 说明/特点 | 面试关键点 |
|---|---|---|---|---|
| groupId | ✅ | 标识组织/公司 | 类似包名(反域名) | GAV 三要素之一 |
| artifactId | ✅ | 标识模块/项目 | jar 名(不含版本) | 在 group 内唯一 |
| version | ✅ | 指定版本 | SNAPSHOT / RELEASE | GAV 唯一定位 |
| scope | ❌ | 控制依赖生效范围 | compile/test/provided/runtime | 控制 classpath |
| optional | ❌ | 是否可选依赖 | true 时不向下传递 | 阻断依赖传递 |
| exclusions | ❌ | 排除传递依赖 | 精确移除某个依赖 | 解决冲突常用 |
| type | ❌ | 指定依赖类型 | 默认 jar | 可为 war、pom 等 |
| classifier | ❌ | 构件分类标识 | 如 sources、javadoc | 区分同一构件变体 |
| systemPath | ❌ | 本地依赖路径 | 配合 system scope 使用 | ⚠️不推荐 |
| optional(传递影响) | ❌ | 控制传递行为,自己用 | 不会传递给依赖方 | 面试加分点 |
依赖作用域 scope
| scope | 编译(main) | 测试(test) | 运行(runtime) | 是否打包 | 典型场景 |
|---|---|---|---|---|---|
| compile(默认) | ✅ | ✅ | ✅ | ✅ | Spring、工具类 |
| test | ❌ | ✅ | ❌ | ❌ | JUnit、Mockito |
| provided | ✅ | ✅ | ❌ | ❌ | Servlet API(容器提供) |
| runtime | ❌ | ❌ | ✅ | ✅ | JDBC 驱动 |
| scope | 什么时候用 | |
|---|---|---|
| compile | 全流程都需要 | 业务核心依赖 |
| test | 只在测试用 | 单元测试 |
| provided | 编译需要,运行环境提供 | Web 容器依赖 |
| runtime | 编译不需要,运行需要 | 数据库驱动 |
依赖传递
如果你依赖 A,而 A 又依赖 B,那么通常你也会自动得到 B。
这就叫:
Transitive Dependency,传递依赖。
看依赖树
mvn dependency:tree
依赖冲突
冲突怎么来的,比如
项目 -> A -> B 1.0
项目 -> C -> B 2.0
Maven 的冲突解决策略
第一条:最近优先
谁离当前项目更近,优先用谁。
例如:
项目 -> A -> B 1.0
项目 -> B 2.0
最终一般选 B 2.0,因为直接依赖路径更短。
第二条:路径相同,先声明优先
如果路径长度相同,谁先写在 POM 里,谁优先。
怎么解决冲突
常见手段:
- 显式指定版本
- 使用
dependencyManagement - 使用
exclusions排除不想要的依赖 - 用
dependency:tree分析来源
仓库机制
本地仓库
默认在用户目录下:
~/.m2/repository
作用:
- 缓存下载过的依赖
- 避免每次联网下载
- 支持安装自己构建的 jar
中央仓库
Maven 官方默认远程仓库。
如果本地没有依赖,就去远程仓库下载。
私服
公司内部一般会搭私服,比如 Nexus、Artifactory。
作用:
- 缓存外部依赖
- 存储公司内部 jar
- 控制依赖下载来源
- 提高下载速度和安全性
生命周期
validate
检查项目是否正确,配置是否完整。
compile
编译主代码。
test
执行测试。
package
打包,生成 jar/war。
install
安装到本地仓库。
deploy
发布到远程仓库。