【Maven】多module项目优雅的实现pom依赖管理

【一】方案设计原则

(1)统一依赖版本管理
所有依赖版本在父POM中集中定义,子模块无需指定版本号。

(2)模块职责分离
按功能划分模块(如核心工具、数据访问、API接口等),减少耦合。

(3)依赖继承与复用
公共依赖由父模块管理,模块间通过引入所需依赖。

(4)BOM集成
使用Spring Boot和第三方库的BOM(Bill of Materials)统一管理版本。

(5)插件集中配置
在父POM中统一配置编译、打包等插件,确保构建一致性。

【二】项目结构示例

parent-project(根项目)
├── pom.xml # 父POM
├── common-core # 通用工具模块
│ └── pom.xml
├── common-dao # 数据访问模块
│ └── pom.xml
├── api-module # API接口模块
│ └── pom.xml
└── service-module # 业务逻辑模块
└── pom.xml

【三】实现思路

通常,一个多模块项目有一个父pom,然后各个子模块继承这个父pom。父pom负责管理公共的依赖版本、插件配置等。子模块可以声明自己的依赖,而版本号由父pom统一管理。这样可以避免版本冲突,也方便统一升级。

优雅管理可能包括:依赖版本统一管理、依赖分类(比如核心依赖、工具类依赖)、避免重复声明、处理模块之间的依赖关系等。

【1】可能的问题点:

(1)父pom如何管理所有依赖的版本?
(2)如何将依赖按功能分组,比如分成dependencies和dependencyManagement?
(3)如何处理不同模块间的依赖共享?
(4)如何避免子模块中重复声明版本号?
(5)如何管理插件的版本和配置?

【2】解决方案的思路:

(1)使用父pom的dependencyManagement来统一管理所有依赖的版本,子模块引入依赖时不需要指定版本,只需要显式声明。
(2)将常用依赖组合成bom(Bill of Materials),或者使用Spring Boot已有的BOM,比如spring-boot-dependencies。
(3)将项目分为多个模块,比如核心模块、通用工具模块、API模块、服务模块等,每个模块负责特定的功能,减少重复依赖。
(4)在父pom中定义公共的插件管理,确保所有子模块使用相同的插件版本和配置。
(5)使用模块化的依赖管理,比如将数据库相关依赖、Web相关依赖分别管理,方便按需引入。

【3】需要注意的地方:

(1)父pom中的依赖管理部分应该清晰,按功能或类别分组,方便查找和维护。
(2)子模块只声明需要的依赖,不重复版本。
(3)避免循环依赖,尤其是在模块之间的依赖关系上。
(4)使用import来引入其他BOM,特别是当项目需要整合多个BOM时,比如同时使用Spring Boot和Spring Cloud的BOM。

【4】可能的错误:

(1)在子模块中错误地指定了版本号,导致父pom的版本管理失效。
(2)模块之间的依赖关系没有正确声明,导致编译或运行时缺少必要的依赖。
(3)没有正确管理插件的版本,导致构建不一致。
(4)父pom中漏掉了某些公共依赖的管理,导致子模块需要自行管理版本,引发冲突。

【四】实现案例

【1】父POM设计(parent-project/pom.xml)

<?xml version="1.0" encoding="UTF-8"?>
<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>parent-project</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>pom</packaging>

    <!-- 模块声明 -->
    <modules>
        <module>common-core</module>
        <module>common-dao</module>
        <module>api-module</module>
        <module>service-module</module>
    </modules>

    <!-- 继承Spring Boot父POM -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.1.5</version>
        <relativePath/> <!-- 从仓库查找 -->
    </parent>

    <!-- 依赖管理:统一版本 -->
    <dependencyManagement>
        <dependencies>
            <!-- 集成Spring Cloud BOM(可选) -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>2022.0.4</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!-- 自定义依赖版本 -->
            <dependency>
                <groupId>com.google.guava</groupId>
                <artifactId>guava</artifactId>
                <version>32.1.2-jre</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.28</version>
                <scope>provided</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <!-- 公共依赖(所有子模块继承) -->
    <dependencies>
        <!-- Spring Boot Starter -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <!-- Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <!-- 插件管理 -->
    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>17</source>
                        <target>17</target>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
</project>

【2】子模块示例(common-core/pom.xml)

(1)module名称为:common-core
(2)父级module是:parent-project
(3)自动继承父级pom里除了dependencyManagement范围内的所有依赖
(4)本module特有的依赖guava,在父级pom的dependencyManagement里管理,使用的时候只需要显式声明这个依赖,不需要指定版本号,依赖的版本统一交给父级pom管理

<?xml version="1.0" encoding="UTF-8"?>
<project>
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.example</groupId>
        <artifactId>parent-project</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <artifactId>common-core</artifactId>

    <!-- 声明模块特有依赖 -->
    <dependencies>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId> <!-- 版本由父POM管理 -->
        </dependency>
    </dependencies>
</project>

【3】模块间依赖(service-module/pom.xml)

<?xml version="1.0" encoding="UTF-8"?>
<project>
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.example</groupId>
        <artifactId>parent-project</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <artifactId>service-module</artifactId>

    <dependencies>
        <!-- 依赖其他模块 -->
        <dependency>
            <groupId>com.example</groupId>
            <artifactId>common-core</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>com.example</groupId>
            <artifactId>common-dao</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!-- Spring Boot Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
</project>

【4】模块间的依赖传递

在 Spring Boot 多模块项目中,若 Module A 依赖 Module B,Module B 显式声明的依赖是否会传递给 Module A,取决于 Maven 的依赖传递机制和具体配置。

(1)默认情况下依赖会传递

<!-- Module B 的 pom.xml -->
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <!-- 未设置 optional 或作用域限制 -->
    </dependency>
</dependencies>

此时,Module A 引入 Module B 时,会自动依赖 spring-boot-starter-web。

(2)依赖传递的限制场景

以下情况可能导致依赖不传递到 Module A:
(1)Optional 标记的依赖​
若 Module B 的依赖声明为 true,则依赖不会传递。例如:

<dependency>
    <groupId>com.example</groupId>
    <artifactId>optional-lib</artifactId>
    <optional>true</optional>
</dependency>

(2)特定作用域的依赖​
若依赖的作用域为 provided 或 test(如 spring-boot-starter-test),则仅在 Module B 内生效,不会传递到 Module A。

(3)​父模块的依赖管理​
若父模块通过 统一管理版本,子模块需显式声明依赖(但版本由父模块控制)

<!-- 父模块的 pom.xml -->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>2.7.0</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

在这里插入图片描述

【六】验证依赖传递的方法

(1)运行 mvn dependency:tree 命令,可清晰看到 Module A 的完整依赖链。

mvn dependency:tree -Dincludes=org.springframework.boot

(2)​IDE 辅助检查​
在 IntelliJ IDEA 或 Eclipse 中,通过 Maven 插件可直接查看依赖关系图,确认传递性依赖是否存在

【七】解决依赖冲突的建议

若 Module B 的依赖与 Module A 其他依赖存在版本冲突,可通过以下方式解决:
(1)统一版本​
在父模块的 中锁定版本。

(2)​排除冲突依赖​
在 Module A 中显式排除不需要的传递性依赖:

<dependency>
    <groupId>com.example</groupId>
    <artifactId>module-b</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>

【八】Pom依赖中的scope作用

在 Maven 的 pom.xml 中, 标签用于定义依赖的作用范围,控制依赖在不同构建阶段(编译、测试、运行、打包)的可见性和传递性。以下是其核心作用及常见取值的详细说明:

(1)<scope> 的主要作用

(1)​控制依赖的生命周期阶段​
确定依赖在编译、测试、运行或打包阶段的可用性。
(2)​管理依赖传递性​
影响依赖是否传递给其他依赖当前项目的模块。
(3)​优化构建和打包结果​
避免不必要的依赖被打包到最终产物中,减少体积和潜在冲突。

(2)常见 <scope> 取值及使用场景

在这里插入图片描述

(3)关键差异与注意事项

(1)compile vs provided
compile 依赖会打包到最终产物,而 provided 依赖由运行环境提供(如应用服务器)。
​示例:servlet-api 在开发时用 provided 避免与容器版本冲突。

(2)​runtime 的编译隔离性
依赖在编译时不可见,仅运行时生效。适用于接口与实现分离的场景(如 JDBC 驱动)。

(3)​test 的局限性
仅用于测试代码编译和执行,不会污染生产环境依赖树。

(4)​谨慎使用 system
依赖本地文件路径,破坏可移植性,建议优先通过私有仓库管理依赖。

(5)​import 的版本管理功能
在 中引入 BOM(物料清单),统一管理多模块的依赖版本

【4】依赖传递性与 的联动

(1)传递性规则:compile 和 runtime 会传递依赖,而 provided、test、system 不会。
​(2)**true:标记为可选的依赖,即使未显式声明也不会传递

<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <optional>true</optional> <!-- 仅当前模块使用,不传递 -->
</dependency>

【九】总结

(1)分层管理依赖
父POM:管理全局依赖版本、插件和公共配置。
子模块:仅声明自身需要的依赖,不重复定义版本。
(2)活用BOM
通过<dependencyManagement>导入Spring Boot、Spring Cloud或其他第三方BOM,简化版本协调。
(3)模块化设计
每个模块职责单一,通过依赖传递共享通用组件。
(4)版本变量提取
对频繁变更的版本号,可在父POM中定义<properties>集中管理。
(5)持续梳理依赖
定期使用mvn dependency:tree分析依赖树,避免冗余和冲突。

(6)优先使用 provided 避免冲突​
对容器或环境提供的依赖(如 servlet-api)始终声明为 provided

(7)减少 system 的使用​
改用私有仓库或 mvn install 安装本地依赖。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值