Maven 中:作为父项目和作为依赖的本质区别
在 Maven 中,"作为父项目"(parent)和"作为依赖"(dependency)是两种完全不同的概念。它们在 Maven 的坐标体系里形式相似(都有 groupId、artifactId、version),但作用机制和语义完全不同。
一、父项目(parent):继承关系
父项目通过 <parent> 标签引入,子项目继承父项目的所有配置。
xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.18</version>
</parent>
子项目从父项目继承以下内容:
<dependencyManagement>:版本锁定,子项目不需要写版本号<properties>:属性定义,如 Java 版本、编码格式<build>中的<pluginManagement>:插件版本锁定<build>中的<plugins>:直接继承的插件(如 spring-boot-maven-plugin)<repositories>:仓库地址<distributionManagement>:发布配置
继承是单向的,一个子项目只有一个父项目(Maven 单继承限制)。Spring Boot 项目使用这种方式的本质是复用一套经过验证的构建策略和依赖版本组合。
核心影响范围:父项目的配置影响整个子项目的构建生命周期,从编译到打包再到部署。
二、依赖(dependency):依赖关系
依赖通过 <dependency> 标签引入,当前项目需要使用某个 Jar 包提供的类或资源时,就把这个 Jar 包声明为依赖。
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
依赖的作用是让当前项目的代码能引用到这个 Jar 包中的类。它影响的是 classpath 和最终的打包产物,而不是构建流程本身。
依赖不会传递父项目的构建配置。比如你引入了一个自定义 Starter,这个 Starter 的父 POM 里配置了某个插件,你的项目不会因此拥有这个插件。
三、两者的本质区别
| 对比维度 | 父项目(parent) | 依赖(dependency) |
|---|---|---|
| 关系类型 | 继承 | 聚合/引用 |
| 传递性 | 不传递 | 传递(可控制) |
| 数量限制 | 单继承 | 可多个 |
| 影响范围 | 构建生命周期(插件、属性、仓库等) | classpath(编译、运行时) |
| 典型用途 | 统一版本、插件配置 | 引用业务功能、工具库 |
pom 类型 |
父项目是 pom 类型 |
依赖通常是 jar 类型 |
| 构建时是否参与打包 | ❌ 不参与 | ✅ 参与 |
四、为什么会出现"pom"类型的依赖?
Maven 中还有一个特殊场景:<dependency> 的 <type>pom</type>。
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.7.18</version>
<type>pom</type>
<scope>import</scope>
</dependency>
这种写法用的是 BOM(Bill of Materials,物料清单)导入,本质上是把另一个 POM 中的 <dependencyManagement> 内容导入到当前项目的 <dependencyManagement> 中。它只导入版本锁定信息,不导入父项目中的插件、属性、仓库等配置。
这种方式解决了 Maven 单继承的限制。如果项目已经继承了公司的公共父 POM,可以通过 BOM 导入获得 Spring Boot 的版本管理能力,而不需要强行改变父 POM。
五、一个具体的例子说明差异
继承父项目后:
子 POM 只有 20 行,不需要声明 Java 版本、不需要配置 spring-boot-maven-plugin、不需要指定 <dependency> 的 <version>。执行 mvn package 时,repackage 目标自动执行。
只把 Spring Boot 当作依赖引入(不继承 parent):
需要手动在 <properties> 中声明 java.version,手动配置 spring-boot-maven-plugin 并指定 mainClass,手动管理所有依赖的版本号。
xml
<properties>
<java.version>17</java.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.7.18</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.example.Application</mainClass>
</configuration>
</plugin>
</plugins>
</build>
两种方式都能让项目正常启动,区别在于配置的复杂度。继承 parent 是把构建配置的细节交给父 POM 管理,依赖方式是把这些细节暴露给开发者自己维护。
六、选择建议
- 没有其他父项目需要继承时,优先使用继承
spring-boot-starter-parent的方式,配置最简洁 - 已经继承了其他父项目时,用 BOM 导入 + 手动配置插件,达到相同的效果
- 公司内部维护自己的父 POM 时,可以在公司父 POM 中引入
spring-boot-dependenciesBOM,让所有子项目自动获得 Spring Boot 版本管理 - 父项目只适合管理公司级别的统一配置(如代码规范插件、仓库地址),不适合频繁修改。版本升级涉及所有子项目,需要在公司层面统一协调