Maven多模块项目构建:深入理解聚合与继承机制
在构建大型企业级应用时,项目通常会被拆分为多个子模块。为了避免在每个子模块中重复配置依赖和版本,Maven 提供了聚合(Aggregation)与继承(Inheritance)两大核心特性来实现工程的统一管理。
一、 聚合(Aggregation)
聚合的主要目的是将多个独立的子模块组合成一个整体,以便在父工程中执行统一的构建生命周期操作(如 mvn clean、mvn compile 或 mvn test)。通过在父 POM 中使用 <modules> 标签声明所有子模块,Maven 能够自动计算构建顺序并批量执行任务。
二、 继承(Inheritance)
在多层架构(如 DAO、Service、Web)中,不同模块往往会引入相同的第三方库(如 Spring 框架)。如果各自维护版本号,不仅繁琐,还容易导致版本冲突。继承机制允许我们创建一个父工程,利用 <properties> 集中定义版本号,并通过 <dependencyManagement> 锁定依赖版本。子模块只需声明 groupId 和 artifactId,即可自动继承父工程的版本配置。
三、 实战配置示例
以下以一个电商系统(Mall)为例,演示如何构建 mall-parent 父工程,并对 mall-dao、mall-service 和 mall-web 进行聚合与继承管理。
1. 父工程 POM (mall-parent/pom.xml)
父工程的打包方式必须为 pom,并包含模块聚合与依赖版本管理配置。
<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>
<groupId>com.demo.mall</groupId>
<artifactId>mall-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<!-- 聚合子模块 -->
<modules>
<module>../mall-dao</module>
<module>../mall-service</module>
<module>../mall-web</module>
</modules>
<!-- 集中定义版本号 -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.framework.version>5.3.20</spring.framework.version>
<mybatis.core.version>3.5.9</mybatis.core.version>
<mysql.connector.version>8.0.28</mysql.connector.version>
<junit.jupiter.version>5.8.2</junit.jupiter.version>
</properties>
<!-- 依赖版本管理(子模块引入时无需指定版本) -->
<dependencyManagement>
<dependencies>
<!-- Spring 核心组件 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.framework.version}</version>
</dependency>
<!-- 持久层框架 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.core.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.connector.version}</version>
</dependency>
<!-- Web 容器 API -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!-- 测试框架 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
2. 数据访问层 (mall-dao/pom.xml)
DAO 模块继承父工程,并引入数据库相关依赖,无需重复声明版本号。
<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>com.demo.mall</groupId>
<artifactId>mall-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../mall-parent/pom.xml</relativePath>
</parent>
<artifactId>mall-dao</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
</dependency>
</dependencies>
</project>
3. 业务逻辑层 (mall-service/pom.xml)
Service 模块依赖 DAO 层,并引入 Spring 上下文与事务管理组件。
<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>com.demo.mall</groupId>
<artifactId>mall-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../mall-parent/pom.xml</relativePath>
</parent>
<artifactId>mall-service</artifactId>
<packaging>jar</packaging>
<dependencies>
<!-- 内部模块依赖 -->
<dependency>
<groupId>com.demo.mall</groupId>
<artifactId>mall-dao</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Spring 组件 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
</dependency>
</dependencies>
</project>
4. Web 展现层 (mall-web/pom.xml)
Web 模块打包方式设置为 war,引入 Servlet API 及 Spring MVC 相关依赖。
<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>com.demo.mall</groupId>
<artifactId>mall-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../mall-parent/pom.xml</relativePath>
</parent>
<artifactId>mall-web</artifactId>
<packaging>war</packaging>
<dependencies>
<!-- 内部模块依赖 -->
<dependency>
<groupId>com.demo.mall</groupId>
<artifactId>mall-service</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Web 与 MVC 支持 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
</dependencies>
<build>
<finalName>mall-web-app</finalName>
</build>
</project>
四、 常见问题与解决方案
在配置多模块项目时,可能会遇到 MyBatis 的 Mapper XML 映射文件未被正确编译到 target/classes 目录的问题。这是因为 Maven 默认只会将 src/main/java 下的 .java 文件编译为 .class,而忽略同目录下的 XML 文件。
解决方案:
最规范的做法是将所有的 MyBatis XML 映射文件从 src/main/java 移动到 src/main/resources 目录下,并保持与 Java 接口相同的包结构。这样 Maven 在构建时会自动将 resources 目录下的所有非 Java 文件拷贝到输出目录中,确保运行时能够正确加载映射配置。