在前面的文章中,我们已经接触到Maven的POM文件和依赖管理。当谈及<packaging>
元素时,除了常见的jar
和war
,pom
类型扮演着至关重要的角色。一个声明了<packaging>pom</packaging>
的Maven项目,其本身通常不包含需要编译的Java源代码,也不会产出可执行或可部署的构件。它的核心价值在于其“管理”和“声明”的职责。本文将深度剖析<packaging>pom</packaging>
项目所能扮演的三重身份:父POM、聚合POM与BOM。
身份一:父POM (Parent POM) - “家族族长,制定家规”
核心职责:为子模块提供一个可继承的配置基线,以实现配置共享、版本统一和规范管理,从而减少子模块中的重复配置。
在 pom.xml
中的体现:
-
子模块中:通过
<parent>
标签声明对父POM的引用。XML
<project ...> <parent> <groupId>com.example</groupId> <artifactId>my-parent-project</artifactId> <version>1.0.0</version> <relativePath>../my-parent-project/pom.xml</relativePath> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>a-module</artifactId> ... </project>
子工程配置继承关系后,其坐标中的
<groupId>
和<version>
若未显式定义,则会自动继承父工程的。<relativePath>
指定父工程pom文件的相对位置;若不指定,Maven会先从本地仓库查找,再从远程仓库查找。 -
父POM本身 (
my-parent-project/pom.xml
):- 必须声明
<packaging>pom</packaging>
。 - 通常会定义:
<properties>
:共享属性,如统一的JDK版本、依赖版本号。<dependencyManagement>
:集中管理依赖版本(子模块仍需显式声明依赖,但可省略版本)。<pluginManagement>
:集中管理插件版本和默认配置(子模块仍需显式声明插件,但可省略版本和部分配置)。<repositories>
和<pluginRepositories>
:统一仓库配置。<distributionManagement>
:统一部署配置。
- 必须声明
工作机制:子模块通过 <parent>
标签“认领”父POM后,就会自动继承父POM中定义的上述可继承元素。子模块也可以覆盖继承来的配置。
类比:“家族族长”制定了“家规”(如采购标准 - <dependencyManagement>
;装修风格 - <pluginManagement>
)和共享资源(公共基金 - <properties>
)。家庭成员(子模块)自动遵守并可使用这些规范和资源。
身份二:聚合POM (Aggregator POM) - “项目总指挥,集合队伍统一行动”
核心职责:将多个独立的子模块组织起来,以便能够一次性地对所有这些模块执行构建命令(如 mvn clean install
)。它主要用于简化多模块项目的构建管理,实现项目的“一键构建”。
在 pom.xml
中的体现:
- 聚合POM本身:
- 必须声明
<packaging>pom</packaging>
。 - 最关键的元素是
<modules>
标签,它列出了所有被聚合的子模块的目录名称(相对于聚合POM的路径)。
<project ...> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>an-aggregator</artifactId> <version>1.0.0</version> <packaging>pom</packaging> <modules> <module>module-one</module> <module>module-two</module> <module>../another-module</module> </modules> ... </project>
- 必须声明
工作机制:当你在聚合POM的目录下执行Maven命令时,Maven的**反应堆 (Reactor)**机制会启动。它会解析聚合POM,找到 <modules>
中列出的所有模块,分析模块间的依赖关系,并按照正确的顺序(先构建被依赖的模块,后构建依赖它的模块)依次构建所有模块。
类比:“项目总指挥”手握所有“施工队”(子模块)名单,一声令下,各施工队按既定工序协同作业完成整个工程。
父POM与聚合POM的关系: 一个聚合POM通常也是其所聚合模块的父POM。这是非常常见且推荐的做法(例如,yudao-cloud
的根POM就是如此,它既是所有模块的父,也聚合了所有模块)。但理论上,父子关系和聚合关系是可以分离的。一个项目可以有一个父POM,同时被另一个不同的聚合POM所管理。
身份三:BOM (Bill of Materials) - “权威的依赖版本推荐清单”
核心职责:提供一个集中管理的、经过测试和验证的、相互兼容的依赖版本集合。它本身不添加任何依赖到项目中,也不直接参与项目的构建流程(除非被其他POM引用其<dependencyManagement>
)。它的主要目的是被其他项目“导入”其 <dependencyManagement>
部分,从而帮助这些项目统一和简化其依赖版本的声明。
在 pom.xml
中的体现:
-
BOM本身 (
my-company-bom/pom.xml
):- 必须声明
<packaging>pom</packaging>
。 - 其灵魂在于
<dependencyManagement>
部分,这里会声明大量的依赖及其精确的版本号。 - 通常BOM中没有
<dependencies>
(或极少),也没有<modules>
。
<project ...> <modelVersion>4.0.0</modelVersion> <groupId>com.example.boms</groupId> <artifactId>my-company-bom</artifactId> <version>1.2.0</version> <packaging>pom</packaging> <properties> <spring.version>5.3.20</spring.version> <jackson.version>2.13.3</jackson.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId>
- 必须声明
<version>${spring.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>${jackson.version}</version> </dependency> </dependencies> </dependencyManagement> </project> ```
工作机制: 其他项目在其自身的 <dependencyManagement>
中,通过特殊的 <dependency>
声明(<type>pom</type>
和 <scope>import</scope>
)来导入BOM。通过这种方式,目标BOM的 <dependencyManagement>
部分的声明会被“合并”到当前项目的 <dependencyManagement>
中。
<project ...>
...
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.example.boms</groupId>
<artifactId>my-company-bom</artifactId>
<version>1.2.0</version>
<type>pom</type>
<scope>import</scope> </dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId> </dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId> </dependency>
</dependencies>
...
</project>
类比:“权威机构发布的推荐配件清单”。你的项目在“选配零件”(声明依赖)时,直接“参考引用”(import
)这份清单,确保用到的都是推荐的、兼容的型号(版本)。
BOM 与父POM <dependencyManagement>
的区别:
- 父POM的
<dependencyManagement>
是通过继承自动传递给子模块的。 - BOM的
<dependencyManagement>
是通过<scope>import</scope>
被其他项目(可以是父POM,也可以是普通项目)主动导入的。 - 一个项目只能有一个直接父POM,但它可以导入多个不同的BOM。
角色关系总结与 yudao-cloud
实践
角色 | 主要职责 | pom.xml 核心体现 |
如何影响其他模块/项目 |
---|---|---|---|
父POM | 提供可继承的共享配置(版本、属性、插件管理等) | 子模块使用 <parent> 指向它 |
子模块自动继承其 <properties> , <dependencyManagement> , <pluginManagement> 等配置 |
聚合POM | 组织多个子模块,实现统一构建 | 自身包含 <modules> 列出子模块目录 |
通过Reactor机制管理并按顺序构建其列出的所有子模块 |
BOM | 声明一套集中的、推荐的依赖版本清单 | 自身核心是 <dependencyManagement> 部分,<packaging>pom</packaging> |
其他项目在其 <dependencyManagement> 中通过 <scope>import</scope> 导入其版本声明 |
在实际项目中,如 yudao-cloud
:
- 其根
pom.xml
(位于yudao/
目录下) 同时扮演父POM和聚合POM的角色。它既是所有子模块的<parent>
,也通过<modules>
聚合了所有子模块。 yudao-cloud
的yudao-dependencies/pom.xml
是一个专用的BOM。这个BOM在其<dependencyManagement>
中定义了大量第三方库和项目内部公共组件的版本。然后,根pom.xml
在自己的<dependencyManagement>
中通过<scope>import</scope>
导入了这个yudao-dependencies
BOM,从而为整个项目体系提供统一、集中的依赖版本管理。
系列文章:
- 《Maven入门:剖析POM核心与GAVP项目坐标》
- 《Maven依赖管理精解:从
<dependencies>
到<dependencyManagement>
与BOM实践》 - 当前: 《Maven
<packaging>pom</packaging>
深度解析:父POM、聚合POM与BOM的三重身份》 - 下一篇: 《玩转Maven多模块项目:继承、聚合与项目结构设计》
Comments NOTHING