乐于分享
好东西不私藏

Maven 构建原理拆解:生命周期、插件与依赖一次讲透

Maven 构建原理拆解:生命周期、插件与依赖一次讲透

用 Maven 这么多年,说实话一直停留在会敲mvn clean package的阶段。生命周期、插件、依赖传递这些词都听过,但从没认真理过它们之间到底怎么串起来的。直到最近一次插件配置死活不对、依赖冲突排查半天没头绪,才下决心把原理补了一遍。

这篇把我捋明白的核心机制整理出来——生命周期、阶段与插件的关系、依赖管理的几条原则。懂了这些,再看 pom 配置和构建日志就不是天书了。下一篇讲maven常用命令和内网离线的配置和依赖管理。

生命周期:Maven 构建的六个阶段

Maven 把一次构建拆成几个固定的阶段,按顺序执行:

  • 清理:删掉上次编译的结果,为重新编译做准备
  • 编译:把 Java 源码编译成字节码
  • 测试:跑单元测试,记录展示结果
  • 打包:把工程封装成压缩包。Java 工程 jar,Web 工程 war
  • 安装:把打包结果装进本地仓库
  • 发布:把打包结果部署到远程仓库,或把 war 丢到服务器运行

关键在于:这些阶段是有顺序的,执行后面阶段会自动带上前面所有阶段。

三套标准生命周期

Maven 定义了三套独立的生命周期:

  • default(主构建):处理项目的构建、打包、部署。最常用,包含 validate → compile → test → package → install → deploy 等阶段
  • clean:清理项目,比如删 target 目录。独立于 default,要清理+构建就得 mvn clean package
  • site:生成项目文档站点。用得少,但它是独立的一套

ℹ️ 三套生命周期相互独立。mvn clean package 的意思是:先跑 clean 生命周期的 clean 阶段,再跑 default 生命周期的 package 阶段(及之前所有阶段)。不是"clean 是 package 的前置"。

阶段和插件:谁定步骤,谁干活

这是 Maven 最核心的机制,先记住三个角色:

  • 阶段(Phase):生命周期里的一个步骤,比如 compile、test、package
  • 插件(Plugin):实际干活的 Java 程序,比如编译代码、跑测试、打 jar
  • 目标(Goal):插件里的一个具体功能,比如 maven-compiler-plugin 的 compile 目标

一句话总结:Maven 把插件的 Goal 绑定到生命周期的 Phase 上,由插件去执行具体任务。

举个例子,mvn package 执行时,实际发生的是:

validate → compile → test → package

每个阶段触发它绑定的插件目标——compile 阶段触发 compiler 插件的 compile 目标,test 阶段触发 surefire 插件的 test 目标,依此类推。

常见插件和目标对应关系:

插件
目标
绑定阶段
maven-compiler-plugin
compile
compile
maven-surefire-plugin
test
test
maven-jar-plugin
jar
package
maven-install-plugin
install
install
maven-deploy-plugin
deploy
deploy

⚠️ 你不能直接"运行一个插件目标"——除非显式调用(mvn 插件:目标)。但你运行一个阶段时,Maven 会自动执行该阶段及之前所有阶段绑定的插件目标。这就是为什么 mvn package 会连带编译、测试一起跑。

Mojo:插件目标的实现

插件里每个目标都是一个 Mojo(Maven plain Old Java Object),就是插件目标的实现类。开发插件时,用 @Parameter 注解声明参数:

@Mojo(name = "run")publicclassAntrunMojoextendsAbstractMojo {@Parameterprivate Target target;publicvoidexecute() { ... }}

这里的 private Target target 对应 pom 里 <target> 配置项。Mojo 里 @Parameter 标注的字段名,就是 pom 配置标签的名字。 懂了这个,看到插件文档里一堆配置项就不懵了——每个都是 Mojo 里一个 @Parameter 字段。

常用插件配置:指定编译 JDK 版本

maven-compiler-plugin 最常用来配 JDK 版本。这里有个坑:3.8.0 之前的版本默认用 JDK 1.5 编译。不显式指定,编译出来的是低版本字节码,用了高版本语法直接报错。

除了改 settings.xml,最干净的方式是在 pom 里配:

<build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.2</version><configuration><source>1.8</source><target>1.8</target><encoding>UTF-8</encoding></configuration></plugin></plugins></build>

source 是源码语法版本,target 是编译字节码版本,encoding 管编码。三个一起配最稳。

Maven 依赖:坐标、排除与冲突原则

GAV 坐标和仓库路径

Maven 靠 GAV(groupId、artifactId、version)定位一个依赖,在仓库里的物理路径是:

仓库地址/{groupId点替换成斜杠}/{artifactId}/{version}/{artifactId}-{version}.jar

⚠️ 关键:groupId 里的点要全替换成斜杠。比如 com.example 在仓库里是 com/example/ 目录。第一次找依赖找不到,多半是这块没对应上。

完整坐标形式是 groupId:artifactId:version[:packaging[:classifier]]。packaging 默认 jar,classifier 用来区分同版本的不同产物(sources、javadoc 等)。

排除传递依赖

依赖 A 引进来,它会带一堆传递依赖。如果其中某个有冲突或不要,用 exclusions 排掉:

<dependency><groupId>xxx</groupId><artifactId>xxx</artifactId><exclusions><exclusion><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId></exclusion></exclusions></dependency>

依赖冲突的两条原则

多个依赖间接引入同一个 jar 的不同版本,Maven 按这两条规则选:

  • 原则一:路径最短者优先。 A→B→C→commons 1.1,A→D→commons 1.2,选 1.2(路径短)
  • 原则二:路径相同时,先声明者优先。 在 pom 里谁写前面就用谁的版本

ℹ️ 排查冲突用 mvn dependency:tree,能打出来完整依赖树,一眼看清某个 jar 是从哪条链路引进来的、最终用了哪个版本。这条命令下一篇会详细说。

推荐使用 IDEA 插件:Maven Helper,应该是标配了。

依赖继承:dependencyManagement

多模块项目里,子模块要统一依赖版本,靠父 pom 的 dependencyManagement:

<dependencyManagement><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.9</version><scope>test</scope></dependency></dependencies></dependencyManagement>

子模块声明依赖时,只写 groupId 和 artifactId,省掉 version 和 scope:

<dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId></dependency></dependencies>

⚠️ 注意区分:dependencyManagement 只是声明版本,不会真正引入依赖。子模块不写,就拉不进来。真正引入依赖的还是 <dependencies>。这个区别搞混了,就会出现"父 pom 配了但子模块没生效"的问题。

小结

这一篇把 Maven 构建的核心原理捋了一遍:

  • 生命周期:三套(default/clean/site),每套多个阶段,阶段有顺序
  • 插件:Goal 绑定到 Phase,插件是干活的,阶段是指挥的
  • Mojo:@Parameter 字段名 = pom 配置标签名
  • 依赖:GAV 定位、exclusions 排除、最短路径和先声明两条冲突原则
  • 继承:dependencyManagement 统一版本,dependencies 真正引入

懂了这些,看 pom 配置和构建日志就有底了。下一篇讲日常命令大全、内网离线环境搭建和变量属性,更偏实操。


你是怎么学的 Maven?是一开始就系统啃了原理,还是跟我一样先会敲命令、后来才补的课?留言区聊聊。

觉得有用点个在看,转发给同样在用 Maven 的同事。想第一时间收后续更新,关注「扶锐随笔」。

下篇见。

相关学习资料