Maven 概述
Maven 是一个基于 POM(Project Object Model,项目对象模型) 的项目管理和构建工具,主要用于 Java 项目。它通过一个中央信息管理模型(POM 文件)来管理项目的构建、依赖、文档、报告等。Maven 的目标是简化构建过程,提供统一的构建系统,并促进最佳实践的采用,使用 Maven 可以自动化构建、测试、打包和发布项目,大大提高了开发效率和质量。
Maven 的核心特点包括:
- 依赖管理:自动下载和管理项目所需的依赖库。
- 标准化项目结构:提供统一的项目目录结构。
- 构建生命周期:定义了明确的构建阶段(如编译、测试、打包、部署等)。
- 插件机制:通过插件扩展功能,支持自定义构建过程。
Maven 主要作用理解
-
场景概念:
- **场景1:**例如我们项目需要第三方库(依赖),如 Druid 连接池、MySQL 数据库驱动和 Jackson 等。那么我们可以将需要的依赖项的信息编写到 Maven 工程的配置文件,Maven 软件就会自动下载并复制这些依赖项到项目中,也会自动下载依赖需要的依赖!确保依赖版本正确无冲突和依赖完整!
- **场景2:**项目开发完成后,想要将项目打成
.war
文件,并部署到服务器中运行,使用 Maven 软件,我们可以通过一行构建命令(mvn package
)快速项目构建和打包!节省大量时间!
-
依赖管理:
Maven 可以管理项目的依赖,包括自动下载所需依赖库、自动下载依赖需要的依赖并且保证版本没有冲突、依赖版本管理等。通过 Maven,我们可以方便地维护项目所依赖的外部库,而我们仅仅需要编写配置即可。
-
构建管理:
项目构建是指将源代码、配置文件、资源文件等转化为能够运行或部署的应用程序或库的过程!
Maven 可以管理项目的编译、测试、打包、部署等构建过程。通过实现标准的构建生命周期,Maven 可以确保每一个构建过程都遵循同样的规则和最佳实践。同时,Maven 的插件机制也使得开发者可以对构建过程进行扩展和定制。主动触发构建,只需要简单的命令操作即可。
Maven 的优势
- 标准化:提供统一的项目结构和构建流程,减少配置的复杂性。
- 依赖管理:自动解决依赖冲突,简化依赖管理。
- 可扩展性:通过插件机制支持自定义构建过程。
- 社区支持:Maven 拥有庞大的社区和丰富的插件生态。
- 跨平台:基于 Java,可以在任何支持 Java 的平台上运行。
Maven 的适用场景
- Java 项目的构建和管理。
- 多模块项目的构建。
- 需要自动化构建、测试和部署的项目。
- 需要统一依赖管理的团队协作项目。
Maven 与其他构建工具的比较
- Maven vs Ant
Ant 是一个基于任务的构建工具,灵活性高,但配置复杂且缺乏依赖管理。Maven 提供了更高级的依赖管理和标准化构建流程。 - Maven vs Gradle
Gradle 结合了 Maven 的依赖管理和 Ant 的灵活性,使用 Groovy 或 Kotlin DSL 进行配置,构建速度更快,配置更简洁。Gradle 逐渐成为现代 Java 项目的首选构建工具。
Maven 的核心概念
-
POM(Project Object Model)
POM 是 Maven 的核心配置文件,通常是一个名为
pom.xml
的文件。它定义了项目的基本信息(如项目名称、版本、依赖等),并描述了如何构建项目。 -
坐标(Coordinates)
Maven 使用坐标来唯一标识一个项目或依赖。坐标由以下部分组成:
- groupId :组织或项目的唯一标识(如
com.example
)。 - artifactId :项目的唯一标识(如
my-project
)。 - version :项目的版本号(如
1.0.0
)。 - packaging :项目的打包方式(如
jar
、war
等)。
示例:
xml<groupId>com.example</groupId> <artifactId>my-project</artifactId> <version>1.0.0</version> <packaging>jar</packaging>
- groupId :组织或项目的唯一标识(如
-
依赖管理(Dependency Management)
Maven 通过 POM 文件中的
<dependencies>
标签管理项目的依赖。它会自动从远程仓库下载所需的依赖,并解决依赖之间的冲突。 -
仓库(Repository)
Maven 使用仓库来存储项目依赖和插件。仓库分为:
- 本地仓库 :位于用户本地的目录(默认是
~/.m2/repository
),用于缓存下载的依赖。 - 远程仓库:包括中央仓库(Maven Central)和私有仓库(如公司内部仓库)。
- 本地仓库 :位于用户本地的目录(默认是
-
生命周期(Lifecycle)
Maven 定义了构建的生命周期,分为多个阶段(phase),如:
validate
:验证项目是否正确。compile
:编译源代码。test
:运行单元测试。package
:打包编译后的代码(如生成 JAR 文件)。install
:将构建结果安装到本地仓库。deploy
:将构建结果部署到远程仓库。
-
插件(Plugin)
Maven 的功能通过插件实现。每个插件可以绑定到生命周期的特定阶段,以执行特定的任务(如编译代码、生成文档等)。
梳理 Maven 坐标 GAVP 属性
Maven 中的 GAVP 是指 GroupId
、ArtifactId
、Version
、Packaging
等四个属性的缩写,其中前三个是必要的,而 Packaging
属性为可选项。这四个属性主要为每个项目在 maven 仓库总做一个标识,类似人的《姓-名》。有了具体标识,方便 maven 软件对项目进行管理和互相引用。
GAV 遵循以下规则:
- GroupID 格式:com.{公司/BU }.业务线.[子业务线],最多 4 级。
txt
说明:{公司/BU} 例如:alibaba/taobao/tmall/aliexpress 等 BU 一级;子业务线可选。
正例:com.taobao.tddl 或 com.alibaba.sourcing.multilang com.atguigu.java
- ArtifactID 格式:产品线名-模块名。语义不重复不遗漏,先到仓库中心去查证一下。
txt
正例:tc-client / uic-api / tair-tool / bookstore
- Version版本号格式推荐:主版本号.次版本号.修订号 1.0.0
txt
1) 主版本号:当做了不兼容的 API 修改,或者增加了能改变产品方向的新功能。
2) 次版本号:当做了向下兼容的功能性新增(新增类、接口等)。
3) 修订号:修复 bug,没有修改方法签名的功能加强,保持 API 兼容性。
例如: 初始→1.0.0 修改bug → 1.0.1 功能调整 → 1.1.1等
- Packaging定义规则:
指示将项目打包为什么类型的文件,idea 根据 packaging
值,识别 maven 项目类型。
packaging
属性为 jar
(默认值),代表普通的 Java 工程,打包以后是 .jar
结尾的文件。
packaging
属性为 war
,代表 Java 的 web 工程,打包以后 .war
结尾的文件。
packaging
属性为 pom
,代表不会打包,用来做继承的父工程。
Maven 工程项目结构说明
Maven 是一个强大的构建工具,它提供一种标准化的项目结构,可以帮助开发者更容易地管理项目的依赖、构建、测试和发布等任务。以下是 Maven Web 程序的文件结构及每个文件的作用:
xml
|-- pom.xml # Maven 项目管理文件
|-- src
|-- main # 项目主要代码
| |-- java # Java 源代码目录
| | `-- com/example/myapp # 开发者代码主目录
| | |-- controller # 存放 Controller 层代码的目录
| | |-- service # 存放 Service 层代码的目录
| | |-- dao # 存放 DAO 层代码的目录
| | `-- model # 存放数据模型的目录
| |-- resources # 资源目录,存放配置文件、静态资源等
| | |-- log4j.properties # 日志配置文件
| | |-- spring-mybatis.xml # Spring Mybatis 配置文件
| | `-- static # 存放静态资源的目录
| | |-- css # 存放 CSS 文件的目录
| | |-- js # 存放 JavaScript 文件的目录
| | `-- images # 存放图片资源的目录
| `-- webapp # 存放 WEB 相关配置和资源
| |-- WEB-INF # 存放 WEB 应用配置文件
| | |-- web.xml # Web 应用的部署描述文件
| | `-- classes # 存放编译后的 class 文件
| `-- index.html # Web 应用入口页面
`-- test # 项目测试代码
|-- java # 单元测试目录
`-- resources # 测试资源目录
pom.xml
:Maven 项目管理文件,用于描述项目的依赖和构建配置等信息。src/main/java
:存放项目的 Java 源代码。src/main/resources
:存放项目的资源文件,如配置文件、静态资源等。src/main/webapp/WEB-INF
:存放 Web 应用的配置文件。src/main/webapp/index.html
:Web 应用的入口页面。src/test/java
:存放项目的测试代码。src/test/resources
:存放测试相关的资源文件,如测试配置文件等。
Maven 依赖管理
Maven 的依赖管理是其最强大的功能之一,它能够自动下载和管理项目所需的依赖库,使得开发人员能够轻松地将其他开发人员开发的模块或第三方框架集成到自己的应用程序或模块中,避免出现版本冲突和依赖缺失等问题。
我们通过定义 POM 文件,Maven 能够自动解析项目的依赖关系,并通过 Maven 仓库自动下载和管理依赖,从而避免了手动下载和管理依赖的繁琐工作和可能引发的版本冲突问题。
重点:编写 pom.xml
文件。
Maven 项目信息属性配置和读取:
xml
<!-- 模型版本 -->
<modelVersion>4.0.0</modelVersion>
<!-- 公司或者组织的唯一标志,并且配置时生成的路径也是由此生成, 如com.companyname.project-group,maven会将该项目打成的jar包放本地路径:/com/companyname/project-group -->
<groupId>com.companyname.project-group</groupId>
<!-- 项目的唯一ID,一个groupId下面可能多个项目,就是靠artifactId来区分的 -->
<artifactId>project</artifactId>
<!-- 版本号 -->
<version>1.0.0</version>
<!--打包方式
默认:jar
jar指的是普通的java项目打包方式! 项目打成jar包!
war指的是web项目打包方式!项目打成war包!
pom不会讲项目打包!这个项目作为父工程,被其他工程聚合或者继承!后面会讲解两个概念
-->
<packaging>jar/pom/war</packaging>
pom.xml 文件核心标签介绍
pom.xml
是 Maven 项目的核心配置文件,它包含了项目构建过程中的所有必要信息。以下是 pom.xml
中一些核心标签的介绍:
-
project :这是 POM 文件的根元素,所有的其他配置项都必须在
<project>
标签内。 -
modelVersion:指定了 POM 模型的版本。对于 Maven 2 及以后的版本,这个值永远是4.0.0。
-
groupId :定义了项目所属的组或组织。通常是公司域名的反写加上项目的路径(如:
com.example.projectname
)。 -
artifactId :定义了当前 Maven 项目的名称标识符,通常是一个没有版本号的 jar 包名(如:
my-project
)。 -
version:指定项目当前的版本。Maven 使用此标签来区分不同版本的项目。
-
packaging :描述了项目的打包方式,可以是
jar
,war
,pom
等,默认为jar
。 -
dependencies :包含项目所依赖的所有外部库的信息。每个依赖通过
dependency
标签定义,并包括groupId
、artifactId
和version
等子标签。 -
build:用于配置项目构建过程中的一些参数,比如编译目录、输出目录等。常用的子标签有:
- plugins :列出所有需要使用的插件。例如,
maven-compiler-plugin
用来配置 Java 源代码和目标字节码的版本。
- plugins :列出所有需要使用的插件。例如,
-
properties:定义了一些键值对作为属性,可以在整个 POM 中使用。这使得管理像版本号这样的变量变得容易。
-
repositories:如果项目依赖不在中央仓库中,你可以在这里添加私有或其他公共仓库的位置。
-
profiles:允许根据不同的环境或条件定制构建过程。例如,你可能有一个开发环境的 profile 和一个生产环境的 profile。
-
parent :如果项目是一个多模块项目的一部分,可以使用
<parent>
标签来指定父项目的坐标。 -
Modules :如果项目是一个多模块项目,可以使用
<modules>
标签列出所有的子模块。xml<modules> <module>module1</module> <module>module2</module> </modules>
-
distributionManagement:定义项目的发布配置,例如将构建的工件部署到远程仓库。
这些是 pom.xml
中最基础和常用的配置项。通过组合这些配置,你可以高度定制化你的Maven项目构建流程。
添加依赖
Maven 使用 pom.xml
文件中的 <dependencies>
标签来管理项目的依赖。通过定义依赖项,Maven 可以自动下载所需的库,并确保这些库在构建、测试和运行时可用。
如果想要添加第三方的依赖,我们需要在 pom.xml
中声明依赖 :需要在 pom.xml
文件中的 <dependencies>
标签内添加一个新的 <dependency>
元素。每个依赖至少需要三个元素:<groupId>
, <artifactId>
和 <version>
。
示例:
xml
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
如果要导入多个依赖,就定义多个 <dependency>
标签,放在 <dependencys>
标签内。
第三方依赖的寻找方法:
- maven仓库信息官网 https://mvnrepository.com/
- idea 插件
maven-search

导入依赖后,刷新 Maven 项目,就可以看到项目已经引入了这个依赖:

scope 属性
<scope>
属性在 Maven 的 dependency
标签中用于指定依赖的范围,它决定了该依赖在项目构建过程中的可用性和传递性。以下是 <scope>
支持的主要值及其含义:
- compile (默认值):
- 该依赖在项目的整个构建和运行期间都可用,包括编译、测试和运行阶段。
- 它会被传递给使用该项目作为依赖的其他项目。
- provided :
- 表示该依赖期望由 JDK 或容器在运行时提供。例如,Servlet API 通常被标记为 provided,因为它由 Servlet 容器如 Tomcat 提供。
- 这种依赖在编译和测试类路径中可用,但在运行时不会被打包进最终的 WAR 或 JAR 文件。
- 它不会传递给其他模块。
- runtime :
- 该依赖仅在运行时需要,在编译期间不需要。一个常见的例子是 JDBC 驱动程序,它们在编译期间不是必需的,但在运行时是必需的。
- 在测试和执行阶段也会包含这种依赖。
- test :
- 表示该依赖仅在测试编译和执行阶段可用。典型的例子包括 JUnit 等测试框架。
- 这些依赖不会被传递,并且也不会被打包到生产环境中。
- import (仅在
<dependencyManagement>
部分有效):- 用于导入其他 POM 中的依赖管理配置。通过这种方式,可以从不同的 POM 中继承依赖版本定义,而不需要直接继承父 POM。
依赖版本的提取和管理
在 Maven 项目中,依赖版本的管理是一个重要的方面,它确保了所有模块使用一致的依赖版本,并简化了版本更新的过程。Maven 提供了一种机制来集中管理和提取依赖版本,这就是通过 <dependencyManagement>
和 <properties>
标签实现的。
<dependencyManagement>
<dependencyManagement>
在所有模块的父工程中使用,目的是定义项目或一组项目(如多模块项目)中的依赖版本,而不实际引入这些依赖。子模块可以继承这些版本声明,除非它们明确声明需要该依赖,否则不会包含这些依赖。
示例:
xml
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>3.1.0</version> <!-- 假设这是Spring Boot的版本 -->
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
</dependencies>
</dependencyManagement>
在这个例子中,commons-lang3
的版本被固定下来,任何子模块如果想要添加 commons-lang3
作为依赖,只需指定 groupId
和 artifactId
,而无需再次指定 version
,可以避免多个子模块调用不同版本的依赖,避免冲突。
<properties>
使用 <properties>
标签来定义依赖版本号,然后在 <dependencies>
部分引用这些属性。这种方法使得版本号的维护更加方便,尤其是当你有多个依赖需要使用相同的版本时。
示例:
xml
<properties>
<junit.version>5.10.0</junit.version>
<slf4j.version>2.0.6</slf4j.version>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
</dependencies>
这里,junit.version
和 slf4j.version
被定义为属性,然后在依赖声明中通过 ${...}
语法引用。这样做的好处是,如果需要升级版本,只需要在一个地方修改版本号即可。
命名随便,但通常为 依赖.version
。
总结
<dependencyManagement>
是用于控制依赖版本的好方法,特别是对于大型或多模块项目。<properties>
提供了一种简单且灵活的方式来统一管理依赖版本,特别适用于需要在多个依赖之间共享相同版本号的情况。
这两种方法都可以帮助你更有效地管理项目的依赖版本,减少重复工作并降低出错的风险。选择哪种方法取决于你的具体需求和项目结构。
依赖排除
有时,你可能希望从某个依赖传递下来的其他依赖中排除特定的依赖。例如,如果你有一个依赖A,它又依赖于B,而你不想引入B,可以这样做:
-
在父依赖的
<dependency>
元素内部使用<exclusions>
标签 :你需要明确指出要排除的依赖的groupId
和artifactId
。示例:
xml<dependency> <groupId>sample.group</groupId> <artifactId>sample-artifact</artifactId> <version>1.0</version> <exclusions> <exclusion> <groupId>sample.dependency-to-exclude-group</groupId> <artifactId>sample-dependency-to-exclude</artifactId> </exclusion> </exclusions> </dependency>
这样,尽管 sample-artifact
依赖于 sample-dependency-to-exclude
,但是后者将不会被包含在你的项目中。
通过这种方式,Maven 提供了一个强大且灵活的方式来管理项目的依赖关系,确保项目仅包含所需的确切库版本,避免不必要的冲突或冗余。
依赖传递
在 Maven 中,依赖传递是一种机制,它允许项目不仅直接声明所需的依赖,还能自动包含这些依赖所依赖的其他库(即间接依赖)。这种特性简化了依赖管理,因为开发者不需要手动列出所有间接依赖。然而,了解如何管理和控制这些传递依赖对于避免版本冲突和确保构建的稳定性非常重要。
依赖传递的工作原理
当你在一个项目的 pom.xml
文件中声明一个依赖时,Maven 会检查该依赖在其 POM 文件中定义的所有依赖,并将它们也添加到你的项目中。这个过程可以递归进行,因此一个依赖可能会引入多个层级的其他依赖。
例如,如果你的项目依赖于 A 库,而 A 库又依赖于 B 库,那么 B 库也会被加入到你的项目中,即使你并没有在自己的 pom.xml
文件中直接声明对 B 库的依赖。
依赖传递的作用
- 减少重复依赖:当多个项目依赖同一个库时,Maven 可以自动下载并且只下载一次该库。这样可以减少项目的构建时间和磁盘空间。
- 自动管理依赖: Maven 可以自动管理依赖项,使用依赖传递,简化了依赖项的管理,使项目构建更加可靠和一致。
- 确保依赖版本正确性:通过依赖传递的依赖,之间都不会存在版本兼容性问题,确实依赖的版本正确性!
依赖冲突
依赖冲突是 Maven 项目中常见的一个问题,尤其是在大型或多模块项目中。当不同的库依赖于同一个库的不同版本时,就会发生依赖冲突。Maven 默认使用"最近原则"来解决这些冲突,即选择依赖树中最靠近根部的版本(即路径最短的版本)。然而,这种默认行为并不总是最佳解决方案,有时需要手动干预以确保使用正确的库版本。
依赖冲突的原因
- 不同依赖引入了同一库的不同版本:这是最常见的原因,比如你的项目依赖于库 A 和 B,而 A 依赖于库 C 的版本 1.0,B 依赖于库 C 的版本 2.0。
- 传递依赖的复杂性:随着项目的增长和依赖关系的增加,间接依赖变得越来越复杂,增加了冲突的可能性。
Maven 自己解决冲突的方式
对于依赖冲突,Maven 有一套自己的解决方法:
- 谁短谁优先:短指的是路径长短,比如 A -> B -> C 1.0,F -> C 2.0,同时依赖了 A 和 F,那么会引入 C 2.0的依赖,因为它的路径更短。
- 谁先谁优先 :当路径长度相同时,先用
<dependency>
标签声明的依赖优先,比如 A -> C 1.0,B -> C 2.0,依赖 A 的<dependency>
标签在上面,那么会引入 C 1.0 的依赖。
我们解决冲突的方式
Maven 的默认行为并不总是最佳解决方案,有时需要手动干预以确保使用正确的库版本。
-
使用
mvn dependency:tree
命令检查依赖树-
这个命令可以帮助你可视化整个项目的依赖结构,并识别出哪些依赖导致了冲突。例如:
bashmvn dependency:tree -Dverbose -Dincludes=groupId:artifactId
-
-Dverbose
参数显示冲突的详细信息,-Dincludes
参数用于过滤特定的依赖。
-
-
排除冲突的依赖版本
-
使用
<exclusions>
标签在 POM 文件中排除不必要的依赖版本。例如,如果你想要排除某个库对特定版本的依赖,可以这样做:xml<dependency> <groupId>sample.group</groupId> <artifactId>sample-artifact</artifactId> <version>1.0</version> <exclusions> <exclusion> <groupId>conflicting.group</groupId> <artifactId>conflicting-artifact</artifactId> </exclusion> </exclusions> </dependency>
-
-
强制指定版本
-
在
<dependencyManagement>
部分明确指定所有子模块应使用的依赖版本。这样可以覆盖其他地方声明的版本。例如:xml<dependencyManagement> <dependencies> <dependency> <groupId>problematic.group</groupId> <artifactId>problematic-artifact</artifactId> <version>desired-version</version> </dependency> </dependencies> </dependencyManagement>
-
依赖导入失败场景和解决方案
在使用 Maven 构建项目时,可能会发生依赖项下载错误的情况,主要原因有以下几种:
- 下载依赖时出现网络故障或仓库服务器宕机等原因,导致无法连接至 Maven 仓库,从而无法下载依赖。
- 依赖项的版本号或配置文件中的版本号错误,或者依赖项没有正确定义,导致 Maven 下载的依赖项与实际需要的不一致,从而引发错误。
- 本地 Maven 仓库或缓存被污染或损坏,导致 Maven 无法正确地使用现有的依赖项,并且也无法重新下载!
解决方案:
- 检查网络连接和 Maven 仓库服务器状态。
- 确保依赖项的版本号与项目对应的版本号匹配,并检查 POM 文件中的依赖项是否正确。
- 清除本地 Maven 仓库缓存(
lastUpdated
文件),因为只要存在lastupdated
缓存文件,刷新也不会重新下载。本地仓库中,根据依赖的 gav 属性依次向下查找文件夹,最终删除内部的文件,刷新重新下载即可!
Maven 构建管理
Maven 的构建管理是其核心功能之一,它通过定义明确的生命周期和插件机制,提供了一套标准化的构建流程,核心是将源代码、依赖库和资源文件等转换成可执行或可部署的应用程序的过程,在这个过程中包括编译源代码、链接依赖库、打包和部署等多个步骤。
主动触发场景:
- 重新编译:编译不充分,部分文件没有被编译。
- 打包:独立部署到外部服务器软件,打包部署。
- 部署本地或者私服仓库:Maven 工程加入到本地或者私服仓库,供其他工程使用。
构建方法
命令构建
语法:mvn 构建命令 构建命令....
命令 | 描述 |
---|---|
mvn clean | 清理编译或打包后的项目结构,删除 target 文件夹 |
mvn compile | 编译项目,生成 target 文件 |
mvn test | 执行测试源码(测试) |
mvn site | 生成一个项目依赖信息的展示页面 |
mvn package | 打包项目,生成 war/jar 文件 |
mvn install | 打包后上传到 maven 本地仓库(本地部署) |
mvn deploy | 只打包,上传到 maven 私服仓库(私服部署) |
可视化构建
在 idea 中使用 Maven 插件进行可视化构建:

构建命令周期
构建生命周期可以理解成是一组固定构建命令的有序集合,触发周期后的命令,会自动触发周期前的命令,也是一种简化构建的思路。
-
清理周期:主要是对项目编译生成文件进行清理。
包含命令:clean
-
默认周期:定义了真正构件时所需要执行的所有步骤,它是生命周期中最核心的部分。
包含命令:compile - test - package - install / deploy
只要执行
mvn install
,会自动执行compile test package
命令。如果要跳过测试,使用
-DskipTests
参数:bashmvn package -DskipTests
-
报告周期
包含命令:site
常用命令:
打包:mvn clean package
部署到本地仓库:mvn clean install
Maven 工程继承
Maven 的工程继承是一种通过父子关系管理多模块项目 的机制。它允许子模块继承父模块的配置(如依赖、插件、属性等),从而减少重复配置,提高项目的可维护性和一致性。

Maven 工程继承的核心概念
- 父模块(Parent Module)
父模块是一个特殊的 Maven 项目,它本身可能不包含任何代码,而是用于定义公共配置(如依赖管理、插件配置、属性等)。父模块的 pom.xml
文件中通常包含 <modules>
和 <dependencyManagement>
等配置。
- 子模块(Child Module)
子模块是一个普通的 Maven 项目,它通过 <parent>
标签继承父模块的配置。子模块可以覆盖父模块的配置,也可以添加自己的特定配置。
继承的作用
- 减少重复配置:将公共配置提取到父模块中,子模块只需继承即可。
- 统一管理:通过父模块集中管理依赖版本、插件配置等。
- 提高可维护性:修改父模块的配置会自动应用到所有子模块。
父工程与子工程配置
父工程配置:首先父工程的 packaging
打包方式必须设置为 pom,需要定义 <modules>
标签声明所有的子工程,对于依赖管理,父工程只做依赖管理的声明而不引入依赖,使用 <dependencyManagement>
进行配置。
父工程 pom.xml
:
xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- 父工程gavp -->
<groupId>org.yigongsui</groupId>
<artifactId>maven-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<!-- 子工程模块 -->
<modules>
<module>maven-children01</module>
<module>maven-children02</module>
<module>maven-children03</module>
</modules>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- 依赖版本统一管理 -->
<junit.version>4.12</junit.version>
<lombox.version>1.18.24</lombox.version>
</properties>
<!-- 依赖统一管理 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombox.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
子工程配置:使用 parent
标签指明父工程,导入依赖时只需指明 groupId
和 artifactId
即可,版本号由父工程继承而来。
子工程 pom.xml
:
xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- 父工程 -->
<parent>
<groupId>org.yigongsui</groupId>
<artifactId>maven-parent</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<!-- 子工程ID,groupId和version同父工程 -->
<artifactId>maven-children01</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!-- 子工程只需导入依赖即可,版本由父工程配置 -->
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>
继承的规则
- 可继承的配置
子模块可以继承父模块的以下配置:
- groupId 和 version :如果子模块未指定,则继承父模块的
groupId
和version
。 - 依赖管理 :通过
<dependencyManagement>
定义的依赖版本。 - 插件管理 :通过
<pluginManagement>
定义的插件配置。 - 属性 :在
<properties>
中定义的属性。 - 仓库配置 :在
<repositories>
和<pluginRepositories>
中定义的仓库。
- 不可继承的配置
- artifactId :子模块必须显式定义自己的
artifactId
。 - 依赖 :父模块的
<dependencies>
不会直接继承到子模块,除非在子模块中显式声明。
- 覆盖父模块配置
子模块可以覆盖父模块的配置。例如,子模块可以定义自己的 version
或 dependency
。
应用场景
- 多模块项目
在大型项目中,通常会将项目拆分为多个模块(如 core
、web
、service
等),并通过父模块统一管理这些模块的配置。
- 依赖版本管理
通过父模块的 <dependencyManagement>
,可以集中管理所有子模块的依赖版本,确保依赖的一致性。
- 插件配置管理
通过父模块的 <pluginManagement>
,可以统一配置所有子模块的插件,避免重复配置。
Maven 工程聚合
Maven 的工程聚合是一种将多个模块组织在一起构建 的机制。通过聚合,可以在一个父项目中一次性构建多个子模块 ,而不需要分别进入每个子模块的目录执行构建命令。聚合通常与继承结合使用,以实现更高效的多模块项目管理。
聚合的作用
- 统一管理子项目构建:通过聚合,可以将多个子项目组织在一起,方便管理和维护。
- 优化构建顺序:通过聚合,可以对多个项目进行顺序控制,避免出现构建依赖混乱导致构建失败的情况。