广告位

云服务器ecs阿里云Maven=Java构建世界的“事实标准”:从pom.xml到云原生CI—CD

频道: 日期: 浏览:0

广州云计算服务器

为什么 2025 年了你还是绕不开 Maven?

先问你一个直球问题:

你有多少次,在 IDEA 里点了一下绿色小三角,应用就跑起来了,

从来没认真想过:这一切到底是谁在背后打工?

再问三个更扎心一点的:

你知道项目里的这些 jar 包是从哪儿来的吗?你知道 Jenkins / GitHub Actions 里那条 mvn clean package -DskipTests,每个单词都在干嘛吗?你有没有遇到过我改了一个依赖版本,结果线上全挂了,但我也说不清楚为啥的场景?

如果你写的是 Java / Spring Boot,答案八成和一个东西有关:

Maven。

很多人对 Maven 的印象还停留在:

那不就是 IDEA 自动帮我生成的那个 pom.xml 吗?反正有报错就搜一下,把那段 复制进去就行了构建慢?那就 -DskipTests 呗

但现实是 —— 在 2025 年这个云原生 + CI/CD 已经成为默认配置的时代,只要你:

想搞清楚本地跑得好好的,为啥 CI 上就过不了想搞定多模块大项目 + 私服 + 环境划分的那堆坑想让自己的项目稍微有点工程味儿而不是到处 copy 的 demo

你就绕不过 Maven。

Maven 已经不只是一个老牌构建工具,而是 Java 世界的事实标准基础设施

所有依赖都用它的坐标系统来命名大多数开源框架的 README,第一个给你的就是 Maven 的 片段绝大部分企业 Java 项目的 CI/CD pipeline 底层,跑的都是 Maven 命令

所以,这篇文章的目标很简单:

不只是教你会写 pom.xml,

而是帮你建立一张清晰的 Maven 心智地图

1)它在 Java 生态里是什么角色?

2)它整体是怎么工作的?

3)pom.xml 每一块大概在干嘛?

这样以后你再遇到 Maven 问题,

就不是到处百度复制粘贴,而是真正知道自己在动哪根神经。

什么是构建工具?Maven 在 Java 生态里的位置

先别急着看 pom.xml,我们先把问题退回到最原始的那个:

写完代码,到底要经过哪些步骤,才能变成一个‘可以上线的东西’?

粗略列一下(不同语言略有差异,但套路差不多):

1)编译 / 转换

Java:javac 把 .java 编成 .class,再打成 .jar / .warTypeScript:tsc 把 .ts → .jsGo:go build 编成二进制

2)依赖管理

把各种三方库搞到本地把 classpath 配好

3)测试

单元测试集成测试

4)打包

把编译结果 + 资源文件,打成 jar / war / 镜像 / zip

5)发布 / 部署

上传到制品库(Nexus / Artifactory)部署到测试环境 / 生产环境 / K8s

如果完全靠你手动做,可能是这样:

javac-cp 一大坨路径 xxx.javajarcvf xxx.jar ...scpxxx.jar some-serversshsome-server java -jar xxx.jar

项目一旦稍微大一点,模块多一点,依赖多一点,环境多一点,就会变成:

命令很难记很难保证所有人、所有环境都用同一套命令CI/CD 上的脚本和本地完全是两套逻辑很难把构建过程变成可维护、可讨论的东西

构建工具的出现,就是为了解决这类问题。

一句话版定义

构建工具(Build Tool)= 把源代码 + 依赖 + 配置自动变成可运行 / 可发布产物的那套标准化流程和工具集合。

Maven 做的事,主要有三件:

1)构建自动化

把编译、测试、打包、安装、发布这整条链路标准化所有人都用同一套命令:mvn clean package、mvn install

2)依赖管理

通过 GAV 坐标(groupId/artifactId/version),从仓库拉取依赖处理传递依赖 & 冲突

3)项目结构与生命周期规范

统一目录结构:src/main/java、src/test/java …统一构建阶段的定义:compile、test、package、install、deploy用插件(Plugin)在不同阶段插入不同的行为

Maven 在 Java 生态里的地位

简单排个时间线:

Ant:早期 Java 构建工具,类似XML 版脚本语言Maven:引入 POM、生命周期、依赖管理,统一了一大堆大家各玩各的的东西Gradle:基于 Groovy/Kotlin DSL,更灵活,性能更好,Android 领域几乎一统天下

但如果聚焦在传统 Java / Spring Boot / 企业应用这个大盘上,会看到一个现实:

Maven 依然是事实上的标准:

1)会 Maven 是 Java 后端工程师的默认技能

2)Gradle 是加分项和某些团队的主选项

原因也很现实:

大量老项目和框架都是 Maven 起步的企业内部的私服、CI/CD、规范文档全部是 Maven 思维绝大多数开源 Java 库,文档里的第一段依赖示例代码就是 Maven

所以,如果你把 Java 技术栈想象成一座城市:

JDK / JVM 是地下基础设施;

Spring 全家桶是城里的建筑群;

Maven 更像是城市的物流系统

负责把各种依赖和构建产物运来运去,把这些东西规范地摆放在正确的位置,CI/CD 工具(Jenkins / GitHub Actions / GitLab CI)都要跟它打交道。

先画一张心智图:Maven 整体工作流程长什么样?

很多人一开始学 Maven,是从各种 XML 配置、命令、插件开始的,结果越学越乱。

其实最好的方式,是先在脑子里画一张Maven 怎么工作的总流程图

我们先给一个简化版:

源代码+ pom.xml解析POM(parent + super POM + profiles + properties)解析依赖(构建依赖树,解决版本& 冲突)组装生命周期(Lifecycle)和每个阶段要执行的插件Goal执行你输入的命令(比如mvn clean package):clean:删除 targetcompile:编译 Javatest:执行单元测试package:打包成 jar/warinstall:安装到本地仓库 (~/.m2/repository)deploy:部署到远程仓库 (Nexus/Artifactory)得到产物(jar/war/docker镜像等),交给后面的部署系统

把它拆开说,就是这么几步:

第一步:解析 POM(Project Object Model)

Maven 看任何项目,第一件事就是读 pom.xml:

1)读当前项目的 pom.xml → 生成一个 Project Model

2)看是否有 ,如果有:

找到父 POM(本地或仓库里)把父 POM 的配置和当前 POM 合并(类似继承 + 覆盖)

3)在最顶上,还有一个 Maven 内置的Super POM

里面有默认的仓库(比如 Maven Central)默认的插件绑定等

4)最终得到一个合并后的 POM,也就是:effective POM

你可以用:

mvnhelp:effective-pom

来看看 Maven 最终理解到的那份 POM长什么样。

第二步:解析依赖,生成依赖树

拿到 effective POM 之后,Maven 会:

1)根据 中的条目,一个个去仓库查找 POM

2)每个依赖也有自己的 dependencies,于是递归展开,形成一棵依赖树

3)在这棵树上应用各种规则:

scope:compile/test/provided/runtimeoptional:要不要被传递exclusions:排除某些依赖冲突时用就近优先 / 先声明优先等策略解决版本

最终,Maven 得到:

编译时 classpath测试时 classpath运行时 classpath

这些 classpath,后面都会被各种插件(compile/test/package)使用。

第三步:组装生命周期(Lifecycle)和插件执行计划

Maven 有三套内置生命周期:

cleandefault(最重要)site

我们最常用的是 default 这条链,它内部有一串 Phase:

validate → compile →test→ package → verify → install → deploy

然后,Maven 会做这么一件事:

1)看当前项目的 是 jar、war 还是 pom 等

2)根据 packaging 类型,加载默认的插件绑定表

比如:compile 阶段默认会绑定 maven-compiler-plugin:compiletest 阶段绑定 maven-surefire-plugin:test

3)再把你在 / 里写的配置叠加进去

4)得到一张执行计划表:

每个 Phase,要调用哪些插件、这些插件的哪些 Goal,以及这些 Goal 的配置参数

所以,当你敲下 mvn package 的时候,其实是在说:

请执行从 validate 到 package 的每一个 Phase,

每个 Phase 把你已经计划好的所有插件 Goal 都跑一遍。

第四步:执行命令,产生构建产物

举个常见命令:

mvn cleanpackage

Maven 会:

1)执行 clean 生命周期:删除 target 目录

2)执行 default 生命周期中,直到 package 为止的所有 Phase:

validate:校验项目结构compile:编译 src/main/javatest:编译 & 运行测试(使用 surefire 插件)package:打包 jar 或 war(使用 jar/war 插件或 spring-boot 插件重打包)

如果你再加一个:

mvnclean install

还会多做一步:

install:把打出来的构件放入本地仓库 ~/.m2/repository

方便后续被其他项目 / 模块直接通过 GAV 坐标来依赖

再进一步 mvn deploy,就是把构建好的产物发到远程仓库(比如公司私服),给别的项目/环境用。

GAV 身份证:Maven 如何唯一标识一个构件?

前面我们说到,依赖管理的核心在于:你得有一套能唯一标识每个构件的命名系统

在 Maven 里,这套系统就是 GAV:

<groupId>com.example

关键词: