很多 Java 项目第一次能跑起来,靠的都是一句 mvn clean package。这条命令很常见,但如果不知道它背后做了什么,遇到依赖冲突、插件目标、多模块构建和私服发布时,就很容易只会反复清缓存。
这篇把 Maven 里最常用的几个概念串一下:坐标、依赖范围、生命周期、插件和多模块。
Maven 坐标解决“我依赖谁”
Maven 用坐标标识一个构件。最常见的是:
groupId:组织或项目组。artifactId:具体模块名。version:版本。packaging:打包类型,比如jar、war、pom。
有时还会看到 classifier,用来区分附属构件,比如 sources、javadoc 或某个平台专用包。
坐标的价值是让依赖变成可声明、可解析、可传递的东西。你不用手动下载一堆 jar 放进项目,Maven 会根据 pom.xml 去本地仓库、私服或远程仓库解析。
依赖范围决定什么时候生效
依赖不是只要写进 pom.xml 就在所有阶段生效。常见 scope 有几个:
compile 是默认范围。编译、测试、运行都有效。业务代码常用依赖大多是它。
test 只在测试编译和测试运行时有效,比如 JUnit。
provided 编译和测试有效,但不会打进发布包。典型例子是传统 Web 工程里的 servlet-api,运行时由容器提供。
runtime 编译时不需要,运行时需要。比如某些 JDBC 驱动。
system 通过本地路径指定 jar,不从仓库解析。它破坏可移植性,通常不推荐。
import 常用于 dependencyManagement 中导入 BOM,让一组依赖版本统一管理。
依赖范围的核心问题是:这个 jar 在编译、测试、运行、打包时到底需不需要。想清楚这个问题,scope 就不难选。
Maven 有三套生命周期
Maven 生命周期不是一个命令,而是一组阶段。常见有三套:
第一套是 clean,用于清理上一次构建产物。最常见阶段就是 clean,通常会删除 target。
第二套是 default,也就是构建主流程。常见阶段包括 compile、test、package、install、deploy。
第三套是 site,用于生成项目站点文档,常见阶段是 site 和 site-deploy。
当你执行某个阶段时,Maven 会把这个阶段之前的相关阶段也跑完。比如执行 package,并不是只打包,它会先处理资源、编译源码、编译测试、运行测试,然后再打包。
clean package 到底做了什么
mvn clean package 可以拆成两部分:
clean 来自 clean 生命周期,主要清理旧构建产物。
package 来自 default 生命周期,会走到打包阶段。
常见插件任务可以粗略理解为:
maven-clean-plugin:clean
maven-resources-plugin:resources
maven-compiler-plugin:compile
maven-resources-plugin:testResources
maven-compiler-plugin:testCompile
maven-surefire-plugin:test
maven-jar-plugin:jar
具体执行哪些插件,取决于项目类型、插件配置、打包方式和生命周期绑定。理解这条链路后,排查“为什么测试没跑”“为什么资源没进包”“为什么 jar 里少文件”会容易很多。
插件和 goal 的关系
Maven 很多能力都是插件提供的。比如编译、测试、打包、部署、生成源码、生成站点,背后都有插件。
当你执行:
mvn dependency:tree
这里的 dependency 是插件前缀,tree 是 goal。Maven 会解析这个插件前缀对应的插件坐标,然后执行目标。
如果不知道某个插件有哪些目标,可以查插件文档,也可以用 help 相关命令查看。理解插件和 goal,能帮你看懂很多构建脚本里看似奇怪的命令。
依赖解析和版本冲突
Maven 依赖是可传递的。你依赖 A,A 又依赖 B,B 也会进入你的依赖树。这带来了便利,也带来了版本冲突。
常见处理方式是:
- 用
mvn dependency:tree看真实依赖树。 - 用
dependencyManagement统一版本。 - 用
exclusions排除不想要的传递依赖。 - 避免使用不稳定的
LATEST、RELEASE。 - 多模块项目用父 POM 管理公共版本。
排查依赖冲突不要只看自己写的直接依赖。很多问题来自传递依赖版本不一致,比如日志框架、JSON 库、Netty、Spring 组件等。
多模块项目怎么理解
多模块项目通常会有一个父工程,packaging 是 pom,下面声明多个 modules。父工程负责聚合构建,也常常负责依赖版本、插件版本和公共属性管理。
多模块的好处是:
- 模块边界清楚。
- 公共版本集中管理。
- 可以一次构建整组模块。
- 子模块之间依赖更可控。
但不要为了“看起来规范”过度拆模块。个人项目或小服务如果模块边界不清,拆太细只会增加构建和理解成本。
一句话收束
Maven 的核心是:用坐标描述构件,用依赖范围描述使用阶段,用生命周期组织构建过程,用插件完成具体动作,用父 POM 和多模块管理复杂项目。
真正理解 mvn clean package 做了什么以后,Maven 就不再只是一个神秘命令,而是一套可以排查、可以约束、可以复用的构建系统。




