作为 Java 初学者,在使用 Maven 管理项目时,单模块项目还能轻松应对,但遇到多模块项目时,重复配置依赖、插件版本等问题会让人头疼。Maven 的继承(Inheritance)机制正是为了解决这个问题而生,本文将用最通俗易懂的方式带你入门 Maven 继承。
一、Maven 继承是什么?
你可以把 Maven 的继承理解为 Java 中的类继承:
- 父 POM(Parent POM):相当于 Java 中的父类,存放所有子模块共用的配置(如依赖版本、插件、仓库等)。
- 子 POM(Child POM):相当于 Java 中的子类,继承父 POM 的所有配置,同时可以自定义自己独有的配置。
核心作用:统一管理多模块项目的依赖版本、插件配置,减少重复代码,降低维护成本。
二、为什么需要 Maven 继承?
假设你有一个电商项目,包含order-service、user-service、pay-service三个模块,每个模块都需要用到:
- Spring Boot 2.7.0
- MyBatis 3.5.9
- JUnit 5.8.2
如果没有继承,每个模块的pom.xml都要重复写这些依赖的版本,一旦需要升级版本,就要修改所有模块的配置,既繁琐又容易出错。
而通过继承,只需要在父 POM 中定义一次版本,所有子模块自动继承,修改时只需改父 POM 即可。
三、Maven 继承的实现步骤
步骤 1:创建父工程(Parent Project)
父工程本质是一个打包类型为 pom的 Maven 项目(没有源码,仅用于管理配置)。
1.1 手动创建父 POM(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"> <!-- 1. 基础配置 --> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <!-- 父工程ID --> <artifactId>maven-parent-demo</artifactId> <!-- 版本号 --> <version>1.0.0</version> <!-- 核心:父工程打包类型必须是pom --> <packaging>pom</packaging> <name>maven-parent-demo</name> <description>父工程-统一管理依赖版本</description> <!-- 2. 定义依赖版本(核心:dependencyManagement) --> <!-- 注意:dependencyManagement只是声明版本,不会自动引入依赖,子模块需显式声明依赖(无需写版本) --> <dependencyManagement> <dependencies> <!-- Spring Boot 父依赖(本身也是继承机制) --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.7.0</version> <type>pom</type> <scope>import</scope> </dependency> <!-- MyBatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.9</version> </dependency> <!-- JUnit --> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.8.2</version> <scope>test</scope> </dependency> </dependencies> </dependencyManagement> <!-- 3. 定义插件版本(pluginManagement) --> <build> <pluginManagement> <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> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </pluginManagement> </build> </project>1.2 关键说明
<packaging>pom</packaging>:父工程必须声明为pom类型,这是 Maven 识别父工程的核心标识。dependencyManagement:声明依赖版本,而非实际引入依赖。子模块使用时只需写groupId和artifactId,无需写version,自动继承父工程的版本。pluginManagement:和dependencyManagement同理,声明插件版本,子模块可直接使用无需重复配置版本。
步骤 2:创建子模块(Child Module)
子模块通过<parent>标签继承父工程的配置。
2.1 子模块 POM 示例(以 order-service 为例)
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"> <!-- 1. 继承父工程核心配置 --> <parent> <groupId>com.example</groupId> <artifactId>maven-parent-demo</artifactId> <version>1.0.0</version> <!-- 若子模块和父工程不在同一目录,需指定父POM路径(相对/绝对) --> <!-- <relativePath>../pom.xml</relativePath> --> </parent> <!-- 2. 子模块自身配置 --> <modelVersion>4.0.0</modelVersion> <artifactId>order-service</artifactId> <packaging>jar</packaging> <name>order-service</name> <description>订单服务-子模块</description> <!-- 3. 引入依赖(无需写版本,自动继承父工程) --> <dependencies> <!-- Spring Boot Web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- MyBatis(继承父工程3.5.9版本) --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> </dependency> <!-- JUnit(继承父工程5.8.2版本) --> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <scope>test</scope> </dependency> </dependencies> <!-- 4. 使用父工程定义的插件(无需写版本) --> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> </plugin> </plugins> </build> </project>2.2 子模块关键说明
<parent>标签:指定父工程的groupId、artifactId、version,这是继承的核心。<relativePath>:可选,指定父 POM 的相对路径(默认../pom.xml),如果父工程和子模块目录结构不符,需手动指定。- 依赖声明:子模块只需写
groupId和artifactId,版本由父工程的dependencyManagement统一管理。
步骤 3:项目目录结构(规范)
推荐的多模块项目目录结构(清晰易维护):
plaintext
maven-parent-demo/ (父工程目录) ├── pom.xml (父POM) ├── order-service/ (子模块1) │ └── pom.xml ├── user-service/ (子模块2) │ └── pom.xml └── pay-service/ (子模块3) └── pom.xml四、Maven 继承的核心要点
1. dependencyManagement vs dependencies
| 特性 | dependencyManagement | dependencies |
|---|---|---|
| 作用 | 声明依赖版本,不引入依赖 | 实际引入依赖(需指定版本) |
| 子模块使用 | 子模块需显式声明依赖(无版本) | 子模块自动继承(直接引入) |
| 核心价值 | 统一版本管理,避免版本冲突 | 实际引入项目所需依赖 |
2. 版本覆盖
如果子模块需要使用不同版本的依赖,可在子模块的<dependencies>中手动指定版本,覆盖父工程的声明:
xml
<!-- 子模块中覆盖MyBatis版本 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.10</version> <!-- 覆盖父工程的3.5.9 --> </dependency>3. 继承的内容
父 POM 中以下配置会被子模块继承:
groupId、version(子模块可省略,自动继承);dependencyManagement中的依赖版本;pluginManagement中的插件配置;repositories(仓库配置);properties(自定义属性,如下)。
4. 自定义属性(进阶)
父工程可通过<properties>定义版本常量,简化dependencyManagement配置:
xml
<!-- 父工程中定义属性 --> <properties> <mybatis.version>3.5.9</mybatis.version> <junit.version>5.8.2</junit.version> <java.version>1.8</java.version> </properties> <!-- 依赖中引用属性 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>${mybatis.version}</version> </dependency> </dependencies> </dependencyManagement>五、常见问题与解决
问题 1:子模块找不到父工程
- 原因:
<parent>标签的groupId/artifactId/version写错,或<relativePath>路径错误。 - 解决:
- 检查父工程的坐标是否正确;
- 确认
<relativePath>路径是否匹配实际目录结构; - 执行
mvn clean install先安装父工程到本地仓库(子模块会从本地仓库找父工程)。
问题 2:依赖版本不生效
- 原因:父工程把依赖写在
<dependencies>而非<dependencyManagement>中,或子模块未显式声明依赖。 - 解决:父工程的版本声明必须放在
<dependencyManagement>中,子模块需在<dependencies>中显式声明依赖。
问题 3:多级别继承(父父工程)
Maven 支持多级继承(如子模块继承父工程,父工程继承 Spring Boot 父 POM),只需在父工程的<parent>中指定上一级父工程即可:
xml
<!-- 父工程继承Spring Boot父POM --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.0</version> <relativePath/> </parent>六、总结
核心要点回顾
- Maven 继承的核心是父工程(pom 类型)+ 子模块(parent 标签),解决多模块项目配置重复问题;
dependencyManagement是继承的核心,用于统一声明依赖版本,子模块需显式声明依赖(无版本);- 父工程的
<packaging>pom</packaging>是必选配置,子模块通过<parent>标签关联父工程。