Maven 全面学习笔记:从基础到实战
1. Maven 简介
1.1 Maven 是什么
-
Maven 的本质是一个项目管理工具,将项目开发和管理过程抽象成项目对象模型(POM,Project Object Model)。
-
POM 通过
pom.xml
文件描述项目的结构、依赖、构建规则等核心信息,是 Maven 管理项目的基础。
1.2 Maven 的作用
- 项目构建:提供标准的跨平台自动化构建方式(编译、测试、打包、部署等),无需手动维护构建脚本。
- 依赖管理:自动管理项目依赖的资源(如 JAR 包),解决依赖下载、版本冲突等问题。
- 统一开发结构:规定标准的项目目录结构(如
src/main/java
存放主代码、src/test/java
存放测试代码),减少团队协作成本。
2. Maven 基础概念
2.1 Maven 仓库
Maven 仓库是存储项目依赖(JAR 包、插件等)的地方,分为三类:
-
本地仓库:位于开发者本地的仓库(默认路径:
~/.m2/repository
),用于缓存下载的依赖,避免重复下载。 -
远程仓库:位于网络中的仓库(如 Maven 中央仓库、阿里云仓库、公司私有仓库),提供依赖的下载源。
-
中央仓库:Maven 官方维护的远程仓库(默认地址:https://repo.maven.apache.org/maven2),包含绝大多数开源依赖。
注意:Maven 查找依赖的顺序为:本地仓库 → 远程仓库(按配置顺序)→ 中央仓库。
2.2 远程仓库配置
远程仓库配置用于指定依赖的下载来源,可在项目级(pom.xml
)或全局级(settings.xml
)配置,优先级:项目级 > 用户级settings.xml
(~/.m2/
)> 全局级settings.xml
(Maven 安装目录conf/
)。
<!-- pom.xml 或 settings.xml 中配置 -->
<repositories>
<repository>
<id>aliyun-public</id> <!-- 仓库唯一标识(必须唯一) -->
<name>Aliyun Public Repository</name> <!-- 仓库名称(自定义) -->
<url>https://maven.aliyun.com/repository/public</url> <!-- 仓库地址 -->
<!-- 发布版本(RELEASE)配置 -->
<releases>
<enabled>true</enabled> <!-- 是否允许下载发布版本 -->
<updatePolicy>daily</updatePolicy> <!-- 更新策略:always/daily/never -->
</releases>
<!-- 快照版本(SNAPSHOT)配置 -->
<snapshots>
<enabled>false</enabled> <!-- 生产环境建议关闭快照版本下载 -->
<updatePolicy>always</updatePolicy>
</snapshots>
</repository>
</repositories>
注意:releases
和snapshots
的enabled
需根据环境调整:开发环境可开启快照版本获取最新依赖,生产环境建议关闭以保证稳定性。
2.3 镜像仓库配置
镜像仓库用于替代指定的远程仓库(通常用于加速下载,如国内镜像替代中央仓库),仅能在settings.xml
中配置。
<settings>
<mirrors>
<mirror>
<id>aliyun-central</id> <!-- 镜像唯一标识 -->
<name>Aliyun Mirror for Central</name> <!-- 镜像名称 -->
<url>https://maven.aliyun.com/repository/central</url> <!-- 镜像地址 -->
<mirrorOf>central</mirrorOf> <!-- 替代的仓库ID(central为默认中央仓库) -->
</mirror>
</mirrors>
</settings>
关键说明:
mirrorOf
支持通配符:*
表示替代所有仓库,repo1,repo2
表示替代指定仓库,*,!private-repo
表示替代所有仓库但排除私有仓库(!
为排除符)。- 谨慎使用
mirrorOf=*
:可能覆盖公司私有仓库,导致私有依赖无法下载。
3. 依赖管理
3.1 依赖传递
- 定义:当项目 A 依赖 B,B 依赖 C 时,A 会自动继承 B 对 C 的依赖(无需手动声明 C)。
- 示例:A→B(依赖 C),则 A 间接依赖 C。
- 优势:简化依赖声明;风险:可能引入同一依赖的不同版本,导致冲突。
3.2 可选依赖(optional)
- 作用:控制依赖是否传递给间接引用的项目。
- 配置:通过
optional=true
阻止依赖传递(默认false
,允许传递)。
<!-- 项目B中配置:log4j为可选依赖 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
<optional>true</optional> <!-- 依赖B的项目A不会自动继承log4j -->
</dependency>
场景:项目提供多种可选实现(如支持 MySQL 和 Oracle 两种数据库驱动),由引用方手动选择所需依赖。
3.3 依赖排除(exclusion)
- 作用:主动移除传递依赖中的指定依赖(解决版本冲突或移除无用依赖)。
- 配置:通过
<exclusions>
标签排除,无需指定版本(仅需 groupId 和 artifactId)。
<!-- 项目A依赖B,B传递依赖C:1.0,A需排除C:1.0并使用C:2.0 -->
<dependency>
<groupId>com.example</groupId>
<artifactId>B</artifactId>
<version>1.0</version>
<exclusions>
<exclusion>
<groupId>com.example</groupId>
<artifactId>C</artifactId> <!-- 排除B传递的所有版本的C -->
</exclusion>
</exclusions>
</dependency>
<!-- 手动声明C:2.0 -->
<dependency>
<groupId>com.example</groupId>
<artifactId>C</artifactId>
<version>2.0</version>
</dependency>
3.4 依赖范围(scope)
依赖范围控制依赖在项目生命周期(编译、测试、运行、打包)中的生效范围,常见范围如下:
范围 | 主代码编译 | 测试代码编译 | 测试运行 | 项目运行 | 打包包含 | 传递性 | 典型示例 |
---|---|---|---|---|---|---|---|
compile | ✅ | ✅ | ✅ | ✅ | ✅ | 完全传递 | Spring、MyBatis |
test | ❌ | ✅ | ✅ | ❌ | ❌ | 不传递 | JUnit、Mockito |
provided | ✅ | ✅ | ✅ | ✅* | ❌ | 不传递 | Servlet API、JSP API |
runtime | ❌ | ❌ | ✅ | ✅ | ✅ | 部分传递 | JDBC 驱动、JSON 解析库 |
system | ✅ | ✅ | ✅ | ✅* | ❌ | 不传递 | 本地特定路径的 JAR |
3.4.1 各范围详解
-
compile(默认):全生命周期生效,打包时包含,适用于项目运行必需的依赖(如框架核心库)。
-
test:仅在测试阶段生效(编译和运行测试代码),不参与打包,适用于测试工具(如 JUnit)。
-
provided:编译和测试阶段生效,但运行时由环境提供(如 Tomcat 提供 Servlet API),打包时不包含,避免冲突。
-
runtime:编译阶段不生效(仅依赖接口),运行和测试阶段生效(需具体实现),适用于 “接口 - 实现” 分离的依赖(如 JDBC 驱动)。
-
system:与
provided
类似,但依赖需手动指定本地路径(通过systemPath
),不推荐使用(移植性差)。<dependency> <groupId>com.example</groupId> <artifactId>local-jar</artifactId> <version>1.0</version> <scope>system</scope> <systemPath>${project.basedir}/lib/local-jar.jar</systemPath> <!-- 本地路径 --> </dependency>
3.5 依赖冲突解决
当同一依赖的多个版本被传递引入时,Maven 通过以下规则解决冲突:
- 路径最近原则:层级越近的依赖优先级越高。
示例:A 直接依赖 C:1.0,同时 A→B→C:2.0,则 A 使用 C:1.0(直接依赖层级更近)。 - 声明顺序优先:路径长度相同时,
pom.xml
中先声明的依赖版本生效。
示例:A 依赖 B 和 C,两者都传递依赖 C:1.0 和 C:2.0,若 B 在 C 之前声明,则使用 B 传递的版本。 - 手动排除:通过
<exclusions>
主动移除冲突版本(见 3.3 节)。 - 版本锁定(dependencyManagement):在父工程的
dependencyManagement
中声明版本,子工程引用时无需指定版本,统一管控。
<!-- 父工程 pom.xml -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>C</artifactId>
<version>1.0</version> <!-- 锁定版本 -->
</dependency>
</dependencies>
</dependencyManagement>
<!-- 子工程 pom.xml -->
<dependency>
<groupId>com.example</groupId>
<artifactId>C</artifactId> <!-- 无需指定版本,继承父工程 -->
</dependency>
工具:通过mvn dependency:tree
命令查看依赖树,快速定位冲突版本。
4. 生命周期与插件
4.1 三大生命周期
Maven 的生命周期是一系列有序的阶段(Phase),用于定义项目构建的完整流程,每个生命周期内的阶段按顺序执行(执行后续阶段会自动触发前置阶段)。
4.1.1 clean 生命周期(清理构建产物)
pre-clean
:清理前的准备工作clean
:删除target
目录(核心阶段)post-clean
:清理后的收尾工作
4.1.2 default 生命周期(核心构建流程)
validate
:校验项目配置是否完整compile
:编译主代码(src/main/java
→target/classes
)test-compile
:编译测试代码(src/test/java
→target/test-classes
)test
:运行测试代码(如 JUnit 测试)package
:将编译后的代码打包(如 JAR、WAR)install
:将包安装到本地仓库(供本地其他项目依赖)deploy
:将包部署到远程仓库(供团队共享)
4.1.3 site 生命周期(生成项目文档)
pre-site
:生成文档前的准备site
:生成 HTML 格式的项目文档(如 API 文档、说明文档)post-site
:生成文档后的处理site-deploy
:将文档部署到服务器
4.2 Maven 插件
Maven 核心仅定义生命周期,具体功能由插件(Plugin) 实现。每个插件包含多个目标(Goal),对应具体操作(如编译、测试)。
4.2.1 插件与生命周期的绑定
插件目标可绑定到生命周期的阶段,当执行阶段时,自动触发对应的目标。例如:
maven-compiler-plugin:compile
绑定到compile
阶段(负责编译主代码)maven-surefire-plugin:test
绑定到test
阶段(负责运行测试)maven-jar-plugin:jar
绑定到package
阶段(负责生成 JAR 包)
4.2.2 常用插件及配置
-
编译插件(maven-compiler-plugin):指定 Java 版本
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>1.8</source> <!-- 源代码版本 --> <target>1.8</target> <!-- 编译后字节码版本 --> </configuration> </plugin> </plugins> </build>
-
测试插件(maven-surefire-plugin):配置测试用例执行规则(如跳过测试)
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.0.0-M5</version> <configuration> <skipTests>true</skipTests> <!-- 跳过测试(打包时常用) --> </configuration> </plugin>
注意:插件需指定版本,避免使用默认版本(可能过时);可通过mvn 插件前缀:目标
直接执行插件目标(如mvn compiler:compile
)。
5. 聚合与继承
聚合与继承是多模块项目的核心管理方式,两者常结合使用但目标不同。
5.1 聚合(Aggregation)
-
作用:将多个子模块聚合到父工程,实现 “一键构建”(如
mvn package
可同时构建所有子模块)。 -
配置:父工程
pom.xml
中通过<modules>
声明子模块路径,父工程打包类型必须为pom
。<!-- 父工程 pom.xml --> <project> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>parent</artifactId> <version>1.0.0</version> <packaging>pom</packaging> <!-- 聚合工程必须为pom类型 --> <modules> <module>module-common</module> <!-- 子模块1(相对路径) --> <module>module-service</module> <!-- 子模块2 --> </modules> </project>
注意:父工程通常不包含业务代码,仅作为构建入口;子模块路径为相对路径(如../module-common
)。
5.2 继承(Inheritance)
-
作用:子工程继承父工程的配置(依赖、插件、属性等),减少重复配置。
-
配置:
- 父工程在
dependencyManagement
中声明可继承的依赖(仅声明,不实际引入); - 子工程通过
<parent>
标签指定父工程,引用依赖时无需指定版本。
<!-- 父工程 pom.xml --> <project> <packaging>pom</packaging> <dependencyManagement> <dependencies> <dependency> <!-- 声明可继承的依赖 --> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>2.7.0</version> </dependency> </dependencies> </dependencyManagement> </project> <!-- 子工程 pom.xml --> <project> <parent> <groupId>com.example</groupId> <artifactId>parent</artifactId> <version>1.0.0</version> <relativePath>../pom.xml</relativePath> <!-- 父工程pom路径 --> </parent> <artifactId>module-service</artifactId> <dependencies> <dependency> <!-- 引用时无需版本,继承父工程 --> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> </dependencies> </project>
- 父工程在
聚合与继承的区别:
- 聚合:解决 “多模块批量构建” 问题(父管构建);
- 继承:解决 “配置复用” 问题(子用父配置)。
6. 属性
Maven 属性用于定义可复用的常量(如版本号、路径),支持动态引用,简化配置维护。
6.1 属性类型
- 内置属性:Maven 预定义,可直接使用。
${project.groupId}
/${groupId}
:项目 groupId${project.artifactId}
/${artifactId}
:项目 artifactId${project.version}
/${version}
:项目版本${project.basedir}
:项目根目录路径
- 自定义属性:在
pom.xml
的<properties>
中定义,格式为${属性名}
。 - Settings 属性:引用
settings.xml
中的配置,如${settings.localRepository}
(本地仓库路径)。 - 环境变量属性:引用系统环境变量,如
${env.JAVA_HOME}
(Java 安装路径)。
6.2 自定义属性示例
<project>
<properties>
<spring.boot.version>2.7.0</spring.boot.version> <!-- 框架版本 -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <!-- 编码格式 -->
<java.version>1.8</java.version> <!-- Java版本 -->
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>${spring.boot.version}</version> <!-- 引用自定义属性 -->
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source> <!-- 引用Java版本 -->
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
优势:版本升级时只需修改<properties>
中的值,无需逐个修改依赖,减少出错风险。
7. 版本管理
7.1 工程版本类型
Maven 通过版本号区分项目的开发阶段,核心分为两类:
- SNAPSHOT(快照版本)
- 格式:
x.y.z-SNAPSHOT
(如1.0.0-SNAPSHOT
) - 特性:开发中的临时版本,每次构建会自动从远程仓库拉取最新快照(即使本地已存在)。
- 场景:团队内部开发时,模块间的临时依赖(确保使用最新开发成果)。
- 格式:
- RELEASE(发布版本)
- 格式:
x.y.z
(如1.0.0
) - 特性:正式发布的稳定版本,版本固定,Maven 不会主动更新(本地存在则直接使用)。
- 场景:对外发布的正式版本(如生产环境部署)。
- 格式:
7.2 版本号约定
版本号遵循 “主版本。次版本。增量版本 - 里程碑版本” 规则,格式:主版本.次版本.增量版本-里程碑版本
。
- 主版本:重大功能迭代,不兼容旧版本(如
2.0.0
相比1.0.0
有架构级变更)。 - 次版本:新增功能,兼容主版本(如
1.1.0
相比1.0.0
新增功能但兼容)。 - 增量版本:仅修复 bug,无新功能(如
1.0.1
相比1.0.0
修复漏洞)。 - 里程碑版本:标注开发阶段(如
1.0.0-BETA
、1.0.0-RC1
(候选发布版))。
示例:
3.2.1
:主版本 3,次版本 2,增量版本 1(正式发布)。2.0.0-SNAPSHOT
:主版本 2 的开发中快照。1.3.0-RC2
:次版本 3 的第二个候选发布版。
8. 资源配置
Maven 默认将src/main/resources
(主资源)和src/test/resources
(测试资源)下的文件视为资源,构建时复制到target/classes
或target/test-classes
。实际开发中常需自定义资源管理。
8.1 资源配置多文件维护
当资源文件较多时,可按功能拆分到不同目录(如src/main/config/db
、src/main/config/log
),通过<resources>
标签指定加载路径。
<build>
<resources>
<!-- 加载默认资源目录 -->
<resource>
<directory>src/main/resources</directory>
</resource>
<!-- 加载自定义资源目录 -->
<resource>
<directory>src/main/config</directory> <!-- 自定义目录 -->
<includes> <!-- 仅包含指定文件 -->
<include>*.properties</include>
<include>*.xml</include>
</includes>
<excludes> <!-- 排除不需要的文件 -->
<exclude>dev-*.properties</exclude>
</excludes>
</resource>
</resources>
</build>
8.2 配置文件引用 POM 属性
资源文件(如.properties
、.xml
)可引用 POM 中定义的属性(需开启资源过滤),实现配置动态化。
步骤:
- 在 POM 中定义属性;
- 在资源文件中通过
${属性名}
引用; - 开启资源过滤(
filtering=true
)。
<!-- 1. POM中定义属性 -->
<properties>
<app.name>user-service</app.name>
<app.version>1.0.0</app.version>
</properties>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering> <!-- 2. 开启过滤,解析属性 -->
</resource>
</resources>
</build>
<!-- 3. 资源文件 src/main/resources/application.properties -->
app.name=${app.name}
app.version=${app.version}
server.port=8080
构建后效果:target/classes/application.properties
中自动替换为:
app.name=user-service
app.version=1.0.0
server.port=8080
9. 多环境开发配置
同一项目在开发(dev)、测试(test)、生产(prod)环境的配置(如数据库地址、端口)不同,需通过多环境配置实现隔离。
9.1 多环境配置(profiles)
通过pom.xml
的<profiles>
标签定义不同环境的属性,每个环境对应唯一id
。
<profiles>
<!-- 开发环境 -->
<profile>
<id>dev</id>
<properties>
<db.url>jdbc:mysql://localhost:3306/dev_db</db.url>
<server.port>8081</server.port>
</properties>
<activation>
<activeByDefault>true</activeByDefault> <!-- 默认激活开发环境 -->
</activation>
</profile>
<!-- 测试环境 -->
<profile>
<id>test</id>
<properties>
<db.url>jdbc:mysql://test-server:3306/test_db</db.url>
<server.port>8082</server.port>
</properties>
</profile>
<!-- 生产环境 -->
<profile>
<id>prod</id>
<properties>
<db.url>jdbc:mysql://prod-server:3306/prod_db</db.url>
<server.port>80</server.port>
</properties>
</profile>
</profiles>
9.2 加载指定环境
方式 1:资源文件关联环境
在资源文件中引用profiles
的属性,结合资源过滤动态生成配置:
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering> <!-- 允许资源文件引用profile属性 -->
</resource>
</resources>
</build>
资源文件application.properties
中引用:
spring.datasource.url=${db.url}
server.port=${server.port}
方式 2:命令行激活环境
通过-P
参数指定环境id
,覆盖默认激活的环境:
# 打包测试环境(使用test profile的配置)
mvn package -Ptest
# 打包生产环境
mvn package -Pprod
优势:实现 “一次打包,多环境部署”,避免因环境配置不同重复打包。
10. 应用实践:
10.1 Spring Boot 与 Cloud 的三个核心依赖管理组件
-
Spring Boot Starter Parent
-
作用:Spring Boot 项目的基础父工程,定义了默认 Java 版本、编码格式、依赖版本等,子工程继承后可简化配置。
-
配置:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.0</version> </parent>
-
-
Spring Cloud Dependencies
-
作用:Spring Cloud 官方组件的版本管理 BOM(Bill of Materials),统一微服务组件版本(如 Eureka、Feign),避免版本冲突。
-
配置:
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>2021.0.3</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
-
-
Spring Cloud Alibaba Dependencies
- 作用:管理阿里系微服务组件(如 Nacos、Sentinel)的版本,用法与 Spring Cloud Dependencies 类似。