← 返回 构建与部署图解 目录
☕ Maven全链路图解
3套生命周期 + 依赖解析 + 冲突解决 + 面试速答 · SVG可视化
① 三套生命周期
Maven生命周期 = 工厂的生产流程 :一个工厂有三条独立的生产线——clean 是拆除旧厂房的流水线(清理上次构建的产物),default 是主生产流水线(从原材料到成品出库),site 是写产品说明书的流水线(生成项目文档)。你可以说"执行到包装环节"(mvn package),Maven就自动从第一步流水线开头一路执行到包装——前面的步骤自动执行,不需要你手动一步步叫 。
🧹 clean 生命周期
拆除旧厂房
pre-clean
🔥 clean
删除target/目录
post-clean
常用命令
mvn clean
🏭 default 生命周期
主生产流水线
validate 验证
initialize 初始化
⚙️ compile 编译
🧪 test 测试
📦 package 打包
verify 验证
install 安装到本地
deploy 部署到远程
📝 site 生命周期
写产品说明书
pre-site
📄 site
生成项目文档站点
post-site
site-deploy
mvn site
图1:Maven三套生命周期 — clean拆旧/default生产/site写文档,互不干扰
核心概念:阶段(Phase) vs 插件目标(Goal) :阶段是流水线上的"工位名称"(compile、test),但真正干活的是插件的"目标"(compiler:compile、surefire:test)。mvn compile实际执行的是compiler插件的compile目标——阶段只是个名字,插件目标才是真正干活的人。
② default生命周期详解
validate
验证项目
⚙️ compile
src/main/java
→ target/classes
🧪 test
src/test/java
→ 运行单元测试
📦 package
target/classes
→ target/*.jar
或 target/*.war
verify
集成测试检查
💾 install
复制到本地仓库
~/.m2/repository/
其他项目可用
🚀 deploy
上传到远程仓库
私服/Nexus
团队共享
🔑 命令→执行阶段映射
mvn compile
→
validate → initialize → compile
mvn test
→
validate → ... → compile → test
mvn package
→
validate → ... → compile → test → package
mvn install
→
validate→...→package→verify→install
mvn deploy
→
validate→...→install→deploy
mvn clean package
→
先clean再package(最常用!)
图2:default生命周期9个阶段 — 执行某个阶段时,前面所有阶段自动执行
pom.xml — 基础结构
<!-- Maven项目坐标 = 人的身份证 -->
<groupId> com.example</groupId> <!-- 公司/组织 -->
<artifactId> my-project</artifactId> <!-- 项目名 -->
<version> 1.0.0</version> <!-- 版本号 -->
<packaging> jar</packaging> <!-- 打包类型: jar/war/pom -->
<!-- 最重要的两个属性 -->
<properties>
<maven.compiler.source> 17</maven.compiler.source>
<maven.compiler.target> 17</maven.compiler.target>
<java.version> 17</java.version>
<spring-boot.version> 3.2.0</spring-boot.version>
</properties>
<!-- 继承Spring Boot父POM — 不用写版本号 -->
<parent>
<groupId> org.springframework.boot</groupId>
<artifactId> spring-boot-starter-parent</artifactId>
<version> 3.2.0</version>
</parent>
③ 依赖解析机制
Maven依赖解析 = 找书过程 :你要一本《Spring实战》(你的项目依赖spring-boot),先看自己书架 (本地仓库 ~/.m2/repository/),没有就上公司资料室 找(私服Nexus),还没有就去国家图书馆 (Maven中央仓库)。找到后复制一本放自己书架上,下次就不用再跑了。而且Spring实战里面还引用了其他书(传递依赖),Maven会自动帮你把引用的书也找齐 。
👤 你的项目
需要 spring-boot
① 先找这里
📚 本地仓库
~/.m2/repository/
✅ 找到了!
② 没有→去私服
🏢 私服(Nexus)
公司内部仓库 + 中央仓库代理
✅ 找到→下载→存入本地仓库
③ 还没有→去中央仓库
🏛 Maven中央仓库
repo.maven.apache.org — 全球最大的Java库
图3:Maven依赖解析路径 — 本地→私服→中央仓库,找到后缓存到本地
3.1 依赖范围(Scope)
Scope = 借书的权限 :compile(默认)= 你随时都能看这本书,打包也带进去;provided = 这本书是公共的(如Servlet API),JDK/服务器已经提供了,打包不用带;runtime = 写代码时不用看,运行时才需要(如JDBC驱动);test = 只在考试时用(JUnit),打包不带。
Scope
编译时
测试时
运行时
打包
典型例子
compile
✅
✅
✅
✅ 带进去
spring-boot, lombok
provided
✅
✅
❌
❌ 不带
servlet-api, JDK自带
runtime
❌
✅
✅
✅ 带进去
mysql-connector, HikariCP
test
❌
✅
❌
❌ 不带
junit, mockito
system
✅
✅
❌
❌ 不带
本地jar(不推荐)
pom.xml — 依赖配置
<dependencies>
<!-- compile(默认)— 编译+运行+打包都带 -->
<dependency>
<groupId> org.springframework.boot</groupId>
<artifactId> spring-boot-starter-web</artifactId>
<!-- 版本号由parent管理,不用写 -->
</dependency>
<!-- provided — 编译时用,运行时由容器提供 -->
<dependency>
<groupId> javax.servlet</groupId>
<artifactId> javax.servlet-api</artifactId>
<scope> provided</scope>
</dependency>
<!-- runtime — 编译不用,运行时才需要 -->
<dependency>
<groupId> com.mysql</groupId>
<artifactId> mysql-connector-j</artifactId>
<scope> runtime</scope>
</dependency>
<!-- test — 只在测试时用 -->
<dependency>
<groupId> org.junit.jupiter</groupId>
<artifactId> junit-jupiter</artifactId>
<scope> test</scope>
</dependency>
</dependencies>
3.2 传递依赖
你的项目
spring-boot-starter-web
spring-boot-starter
spring-boot-starter-tomcat
spring-webmvc
spring-boot-autoconfigure
spring-aop
spring-core → spring-jcl → ...
🔑 传递依赖
你只声明了1个依赖
Maven自动把引用链全找齐
图4:传递依赖 — 你只声明1个starter,Maven自动拉取整个依赖树
Terminal — 查看依赖树
# 查看完整依赖树(包括传递依赖)
$ mvn dependency:tree
# 输出示例:
com.example:my-project:jar:1.0.0
├── org.springframework.boot:spring-boot-starter-web:3.2.0
│ ├── org.springframework.boot:spring-boot-starter:3.2.0
│ │ ├── org.springframework.boot:spring-boot-autoconfigure:3.2.0
│ │ └── spring-core:6.1.1
│ ├── org.springframework:spring-webmvc:6.1.1
│ │ └── org.springframework:spring-aop:6.1.1
│ └── tomcat-embed-core:10.1.x
├── com.mysql:mysql-connector-j:8.0.33
└── org.junit.jupiter:junit-jupiter:5.10.1 (test)
# 只看冲突的依赖
$ mvn dependency:tree -Dverbose | grep "omitted for conflict"
# 分析用了/没用到的依赖
$ mvn dependency:analyze
④ 依赖冲突与解决
依赖冲突 = 两个人给你不同版本的书 :A朋友送你《Spring 5.0》,B朋友送你《Spring 6.0》,你到底看哪本?Maven有两条规则:① 最短路径优先 ——A朋友是直接给你的(1层),B朋友是通过C转给你的(2层),听A的!② 先声明优先 ——如果路径一样长,谁先在pom.xml里声明就听谁的。但!Spring Boot的<dependencyManagement>就像老师统一规定了全班用哪个版本,不管谁送的,一律按老师说的来。
规则1:最短路径优先
你的项目
路径长度=1
✅ spring-core 6.1
直接依赖,胜出!
路径长度=2
spring-beans 6.1
(传递依赖)
❌ spring-core 5.3
路径更长,被忽略!
最终使用 spring-core 6.1(最短路径)
规则2:先声明优先
(路径长度相同时)
你的项目
路径=2
✅ 库A v2.0
先声明 → 胜出!
路径=2
❌ 库A v1.0
后声明 → 被忽略
<dependencies>
<dependency> <!-- ✅ 先声明 -->
artifactId: lib-a-v2
<dependency> <!-- ❌ 后声明 -->
artifactId: lib-b-v1
图5:依赖冲突解决规则 — 最短路径优先,路径相同则先声明优先
4.1 手动解决冲突
pom.xml — 解决依赖冲突
<!-- 方法1:exclusion排除传递依赖 -->
<dependency>
<groupId> com.example</groupId>
<artifactId> lib-b</artifactId>
<exclusions>
<exclusion>
<!-- 排除lib-b带来的旧版spring-core -->
<groupId> org.springframework</groupId>
<artifactId> spring-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 方法2:直接声明想要的版本(最短路径=1)-->
<dependency>
<groupId> org.springframework</groupId>
<artifactId> spring-core</artifactId>
<version> 6.1.1</version>
<!-- 直接声明→路径最短→一定胜出!-->
</dependency>
<!-- 方法3:dependencyManagement统一版本(Spring Boot做法)-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId> org.springframework</groupId>
<artifactId> spring-core</artifactId>
<version> 6.1.1</version>
<!-- 所有spring-core统一用这个版本 -->
</dependency>
</dependencies>
</dependencyManagement>
依赖冲突的典型症状 :ClassNotFoundException、NoSuchMethodError、AbstractMethodError——代码编译通过(编译时用的是正确的版本),但运行时报错(运行时加载了冲突的旧版本)。排查步骤:mvn dependency:tree -Dverbose → 找到冲突 → exclusion或直接声明。
⑤ 插件与面试速答
5.1 核心插件速查
插件
绑定阶段
作用
常用配置
maven-compiler-plugin
compile
编译Java源码
source/target版本
maven-surefire-plugin
test
运行单元测试
skipTests, 并行测试
maven-jar-plugin
package
打jar包
Main-Class配置
maven-war-plugin
package
打war包
webResources过滤
maven-install-plugin
install
安装到本地仓库
一般不需要配置
maven-deploy-plugin
deploy
部署到远程仓库
repository URL
maven-clean-plugin
clean
删除target目录
自定义删除目录
spring-boot-maven-plugin
package
打可执行jar
mainClass, profiles
pom.xml — Spring Boot打包插件
<build>
<plugins>
<!-- Spring Boot打包插件 — 生成可执行jar -->
<plugin>
<groupId> org.springframework.boot</groupId>
<artifactId> spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass> com.example.Application</mainClass>
<!-- 指定启动类 -->
<excludes>
<exclude>
<groupId> org.projectlombok</groupId>
<!-- Lombok编译后不需要 -->
</exclude>
</excludes>
</configuration>
</plugin>
<!-- 跳过测试(紧急时) -->
<plugin>
<groupId> org.apache.maven.plugins</groupId>
<artifactId> maven-surefire-plugin</artifactId>
<configuration>
<skipTests> true</skipTests>
<!-- ⚠️ 仅CI/紧急时使用,平时不要跳!-->
</configuration>
</plugin>
</plugins>
</build>
5.2 settings.xml 关键配置
⚙️ 全局 settings.xml
${MAVEN_HOME}/conf/settings.xml
① localRepository — 本地仓库路径
② mirrors — 镜像源(如阿里云)
③ profiles — JDK版本等
所有项目生效
👤 用户 settings.xml
~/.m2/settings.xml
① servers — 私服账号密码
② mirrors — 阿里云镜像加速
③ profiles — 本地JDK/编码
优先级:用户 > 全局
图6:settings.xml配置位置 — 用户级覆盖全局级
~/.m2/settings.xml — 阿里云镜像
<settings>
<!-- 本地仓库位置(默认 ~/.m2/repository)-->
<localRepository> /Users/you/.m2/repository</localRepository>
<mirrors>
<!-- 阿里云镜像 — 国内下载快10倍+ -->
<mirror>
<id> aliyun</id>
<mirrorOf> central</mirrorOf>
<name> Aliyun Maven</name>
<url> https://maven.aliyun.com/repository/central</url>
</mirror>
</mirrors>
<profiles>
<profile>
<id> jdk-17</id>
<activation>
<activeByDefault> true</activeByDefault>
<jdk> 17</jdk>
</activation>
<properties>
<maven.compiler.source> 17</maven.compiler.source>
<maven.compiler.target> 17</maven.compiler.target>
</properties>
</profile>
</profiles>
</settings>
5.3 Maven vs Gradle
对比维度
Maven
Gradle
配置格式
XML(pom.xml)冗长
Groovy/Kotlin DSL简洁
构建速度
每次全量构建较慢
增量构建+构建缓存快2~10倍
灵活性
约定优于配置标准化
脚本可编程灵活
学习曲线
低,XML易懂入门快
中,DSL需学习稍陡
生态
最成熟,插件最多企业标配
快速成长,Android标配追赶中
Spring Boot
官方首选完善支持
也支持可用
5.4 面试高频问题
Maven的构建生命周期是什么?
Maven有3套相互独立的生命周期 :
clean :pre-clean → clean → post-clean(清理上次构建产物)
default :validate → compile → test → package → verify → install → deploy(核心构建流程)
site :pre-site → site → post-site → site-deploy(生成项目文档)
核心机制 :执行某个阶段时,该阶段之前的所有阶段会自动按顺序执行。如mvn package会依次执行validate→compile→test→package。
Maven如何解决依赖冲突?
两条核心规则:
① 最短路径优先 :A→B→C(v1) 和 A→D(v2),D的路径更短,选v2。
② 先声明优先 :路径长度相同时,pom.xml中先声明的依赖胜出。
解决方式 :
1. <exclusion>排除不需要的传递依赖
2. 直接在pom.xml中声明想要的版本(路径最短=1,一定胜出)
3. <dependencyManagement>统一管理版本(Spring Boot的做法)
Maven的Scope有哪些?
5种scope,决定了依赖在什么阶段可用、是否打包进最终产物:
compile (默认):编译+测试+运行都可用,打包带进去
provided :编译+测试可用,运行时由JDK/容器提供,打包不带(如servlet-api)
runtime :测试+运行可用,编译不用,打包带进去(如JDBC驱动)
test :只有测试可用,打包不带(如JUnit)
system :类似provided,但需指定本地jar路径(不推荐)
mvn clean package和mvn package有什么区别?
mvn package :只执行default生命周期(validate→compile→test→package)。如果target目录里有上次的旧class文件,可能混用新旧class,导致诡异bug。
mvn clean package :先执行clean生命周期(删除target/),再执行package。确保从零开始编译,不会有残留。
最佳实践 :开发时可以用mvn package(快),正式构建/CI一定要用mvn clean package(干净)。
Spring Boot的spring-boot-starter-parent做了什么?
它是一个POM类型的父项目 ,帮你做了这些事:
① 统一版本管理 :通过<dependencyManagement>预定义了所有Spring Boot相关依赖的版本号,你不需要写version
② 统一插件配置 :预配置了spring-boot-maven-plugin、compiler、surefire等插件
③ 统一属性 :预定义了Java版本、编码格式(UTF-8)、资源过滤等
④ 依赖版本对齐 :确保所有Spring组件版本兼容,不会出现A用6.0 B用5.3的冲突
dependencyManagement和dependencies的区别?
<dependencies> :直接引入依赖,子模块会自动继承。
<dependencyManagement> :只声明版本号,不实际引入!子模块需要自己写<dependency>,但不用写version(由父POM统一管理)。
类比 :dependencies像妈妈直接把菜端到你面前;dependencyManagement像妈妈在菜单上标注了每道菜用多少盐,你自己点菜,但盐的量按妈妈的标准来。
📌 速记口诀
三套生命周期 :clean拆旧/default生产/site写文档
default核心阶段 :validate→compile→test→package→verify→install→deploy
依赖解析路径 :本地仓库→私服→中央仓库,找到就缓存
依赖范围Scope :compile全带/provided不带/runtime运行才用/test测试才用
冲突解决 :最短路径优先→路径相同先声明优先→dependencyManagement定版本
常用命令 :mvn clean package(最常用)/ mvn install / mvn deploy
Maven vs Gradle :XML标准化 vs DSL灵活,Gradle增量构建更快