Maven:约定、生命周期与常用命令
更新时间:2026-09-20。工程里 javac/java 的 classpath 手动管理不可持续,Maven 用「约定目录 + 坐标依赖 + 生命周期」把构建标准化。对照的另一个主流工具见 Gradle。
一、坐标与 pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>com.demo</groupId> <!-- 组织/反向域名 -->
<artifactId>order-service</artifactId> <!-- 模块名 -->
<version>1.2.0-SNAPSHOT</version> <!-- 版本;SNAPSHOT 表示开发快照 -->
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.13</version>
<scope>compile</scope> <!-- 依赖范围,见下 -->
</dependency>
</dependencies>
</project>groupId+artifactId+version 唯一确定一个构件(jar),Maven 据此从中央仓库/私服下载到本地仓库(~/.m2/repository)。
二、标准目录约定
src/main/java 生产源码
src/main/resources 配置文件(打进 jar)
src/test/java 测试源码
src/test/resources 测试配置
target/ 构建产物(.class、jar),不提交
pom.xml遵守约定就不用写任何路径配置,这是 Maven「约定优于配置」的核心。
三、生命周期与常用命令
Maven 有三套相互独立的生命周期,命令是「执行到某阶段(phase),其前面阶段依次执行」:
mvn clean # 删除 target/
mvn compile # 编译 main
mvn test # 编译并跑单测(先 compile)
mvn package # 打包成 jar/war(先 test)
mvn verify # 跑集成测试/检查
mvn install # 装进本地 ~/.m2,供本机其他模块依赖
mvn deploy # 推到私服/远程仓库
# 常用组合与参数
mvn clean package -DskipTests # 打包但跳过测试
mvn test -Dtest=OrderServiceTest # 只跑指定测试类
mvn -o package # offline,只用本地仓库
mvn -pl order-api -am clean install # 多模块:只构建某模块及其依赖
clean属于独立的 clean 生命周期,所以常和 default 生命周期连着写clean package。
四、依赖范围 scope
| scope | 编译 | 测试 | 运行(打包) | 典型 |
|---|---|---|---|---|
compile(默认) | ✅ | ✅ | ✅ | slf4j-api、Spring 核心 |
provided | ✅ | ✅ | ❌(容器给) | servlet-api(Tomcat 提供) |
runtime | ❌ | ✅ | ✅ | JDBC 驱动、日志实现 |
test | ❌ | ✅ | ❌ | JUnit、Mockito |
scope 配错是「本地能跑、线上 ClassNotFoundException/NoClassDefFoundError」的常见原因。
五、依赖冲突怎么排
Maven 用最近优先裁决传递依赖版本,容易出现「我没引却被传递带进旧版本」:
mvn dependency:tree # 看依赖树与版本裁决
mvn dependency:tree -Dverbose # 显示被省略的冲突(旧版)
mvn dependency:analyze # 报「用了没声明/声明没用」解决:在引冲突的依赖里用 <exclusions> 排除,或在 <dependencyManagement> 统一锁定版本。报 NoSuchMethodError/ClassNotFoundException 多半就是版本被仲裁成不兼容的那个。镜像加速见 cargo 换源 的同类思路(Maven 配 settings.xml 的 mirror 为阿里云等)。
六、SNAPSHOT 与发布
1.2.0-SNAPSHOT 是可变的开发版,每次构建检查远程更新;正式发布用不带 SNAPSHOT 的不可变版本号。不要在生产长期依赖别人的 SNAPSHOT,可复现性会被破坏。
总结
Maven 靠 groupId/artifactId/version 坐标从仓库拉依赖、用标准目录约定和 phase 生命周期把 clean→compile→test→package→install→deploy 串成一条线;-DskipTests、-Dtest=、-pl ... -am 是高频参数,compile/provided/runtime/test 四种 scope 决定依赖在编译/测试/运行期是否可见,配错就线上类找不到;传递依赖按最近优先裁决,冲突用 dependency:tree 定位、exclusions 或 dependencyManagement 锁定版本;SNAPSHOT 可变、发布版不可变,底层构建仍是 javac/java,只是 classpath 与产物由 Maven 托管。