SpringBoot多模块依赖冲突排查与架构优化实战(避坑指南)

目录

一、项目初始结构(问题场景)

二、核心问题排查与解决方案(按优先级)

问题1:tomcat-embed-core版本冲突(父模块10.1.54,子模块10.1.42)

[1.1 问题根源](#1.1 问题根源)

[1.2 解决方案(父模块优化)](#1.2 解决方案(父模块优化))

问题2:spring-security-web版本升级不生效(需升级至6.5.10)

[2.1 问题根源](#2.1 问题根源)

[2.2 解决方案(调整父模块bom导入顺序)](#2.2 解决方案(调整父模块bom导入顺序))

问题3:非公共依赖(security)污染无关模块(C/D)

[3.1 问题根源](#3.1 问题根源)

[3.2 解决方案(模块化拆分,按需依赖)](#3.2 解决方案(模块化拆分,按需依赖))

三、最终优化后的标准架构(企业级最佳实践)

四、关键注意点(避坑核心)

五、总结


前言:在SpringBoot多模块项目开发中,依赖冲突、版本不一致、模块依赖设计不合理是最常见的"拦路虎"。本文结合实际项目场景,记录从依赖版本冲突(tomcat、spring-security)到模块架构优化的完整排查、解决过程,总结企业级多模块最佳实践,帮助更多开发者避坑,提升项目可维护性。

本文核心解决场景:父模块+通用模块A+业务模块BCD的多模块结构中,依赖版本冲突、非公共依赖污染无关模块、模块依赖设计不合理等问题,全程贴合实际开发场景,提供可直接复用的解决方案。

一、项目初始结构(问题场景)

先明确项目初始结构,也是问题的起源,方便大家对号入座:

bash 复制代码
父项目(parent)
├── A模块(通用模块):包含service、dao、工具类,引入mybatis-plus、redis、spring-boot-starter-security等
├── B模块(业务模块):依赖A模块,需要security功能
├── C模块(业务模块):依赖A模块,不需要security功能
└── D模块(业务模块):依赖A模块,不需要security功能

初始问题汇总:

    1. 父模块在dependencies中直接引入tomcat-embed-core、spring-boot-starter-web(带排除),导致子模块出现tomcat版本冲突(父模块10.1.54,A模块传递10.1.42);
    1. A模块引入spring-boot-starter-security,需升级spring-security-web至6.5.10,但父模块锁定后不生效,A/B模块版本不一致;
    1. A模块引入的security依赖,传递给不需要的C/D模块,造成依赖冗余;
    1. 父模块未遵循"只管理版本,不引入依赖"的原则,导致依赖传递混乱。

二、核心问题排查与解决方案(按优先级)

以下解决方案按"先解决紧急冲突,再优化架构"的顺序,每一步都贴合实际操作,可直接复制配置使用。

问题1:tomcat-embed-core版本冲突(父模块10.1.54,子模块10.1.42)

1.1 问题根源

父模块在dependencies中直接引入tomcat-embed-core:10.1.54,同时父模块还引入了spring-boot-starter-web(排除了tomcat);A模块引入spring-boot-starter-web,自带tomcat-embed-core:10.1.42(SpringBoot 3.3.13默认版本);BCD模块依赖A模块,导致传递引入10.1.42,与父模块的10.1.54冲突。

关键原因:父模块直接在dependencies中引入依赖,优先级低于SpringBoot自带的版本管理;且未通过dependencyManagement统一锁定版本。

1.2 解决方案(父模块优化)

核心原则:父模块只通过dependencyManagement管理版本,不直接在dependencies中引入任何依赖。

XML 复制代码
<properties>
    <spring-boot.version>3.3.13</spring-boot.version>
    <tomcat.version>10.1.54</tomcat.version> <!-- 统一tomcat版本 -->
</properties>

<dependencyManagement>
    <dependencies>
        <!-- Spring Boot 官方依赖管理 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>${spring-boot.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>

<!-- 强制锁定tomcat版本,覆盖SpringBoot默认版本 -->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-core</artifactId>
            <version>${tomcat.version}</version>
        </dependency>
    </dependencies>
</dependencyManagement>

<!-- 父模块dependencies标签为空,不引入任何依赖 -->
<dependencies>
</dependencies>

后续操作:删除父模块中所有直接引入的依赖(tomcat-embed-core、spring-boot-starter-web),A模块正常引入spring-boot-starter-web即可,无需排除tomcat,会自动继承父模块锁定的10.1.54版本。

问题2:spring-security-web版本升级不生效(需升级至6.5.10)

2.1 问题根源

A模块引入spring-boot-starter-security,父模块通过dependencyManagement锁定spring-security.version=6.5.10,但版本不生效,A/B模块仍显示6.3.10(SpringBoot 3.3.13默认版本)。

关键原因:spring-security-bom的导入顺序在spring-boot-dependencies之后,Maven规则"先导入的bom优先级更高",导致SpringBoot自带的版本覆盖了我们锁定的版本。

2.2 解决方案(调整父模块bom导入顺序)
XML 复制代码
<properties>
    <spring-boot.version>3.3.13</spring-boot.version>
    <tomcat.version>10.1.54</tomcat.version>
    <spring-security.version>6.5.10</spring-security.version> <!-- 统一security版本 -->
</properties>

<dependencyManagement>
    <dependencies>
        <!-- 重点:spring-security-bom放前面,优先级高于SpringBoot自带版本 -->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-bom</artifactId>
            <version>${spring-security.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>

        <!-- Spring Boot 官方依赖放后面 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>${spring-boot.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>

        <!-- tomcat版本锁定 -->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-core</artifactId>
            <version>${tomcat.version}</version>
        </dependency>
    </dependencies>
</dependencyManagement>

验证方法:在A/B模块目录执行命令 mvn dependency:tree -Dincludes=org.springframework.security:spring-security-web,输出结果中版本为6.5.10即生效。

注意:无需在A模块做任何排除操作,保持spring-boot-starter-security的正常引入即可。

问题3:非公共依赖(security)污染无关模块(C/D)

3.1 问题根源

A模块作为通用模块,引入了只有B模块需要的spring-boot-starter-security,导致依赖A的C/D模块也被动引入了security,造成冗余和不必要的依赖污染。

关键原因:模块依赖设计不合理,将"非公共依赖"放入了通用模块A中。

3.2 解决方案(模块化拆分,按需依赖)

核心原则:通用模块A只放"所有子模块(BCD)都需要"的依赖,非公共依赖由需要的模块自己引入。

  1. 第一步:清理A模块,移除spring-boot-starter-security依赖(A模块只保留mybatis-plus、redis、dao、service等公共依赖);

  2. 第二步:B模块自己引入spring-boot-starter-security(因为只有B需要);

  3. 第三步:C/D模块只依赖A模块,不引入任何多余依赖。

具体配置:

XML 复制代码
<!-- A模块pom.xml(清理后) -->
<dependencies>
    <!-- 公共依赖:BCD都需要 -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <!-- 其他公共service、dao相关依赖 -->
</dependencies>

<!-- B模块pom.xml(自己引入security) -->
<dependencies>
    <!-- 依赖通用模块A -->
    <dependency>
        <groupId>xxx</groupId>
        <artifactId>A</artifactId>
    </dependency>
    <!-- 自己需要的security,不传递给C/D -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
</dependencies>

<!-- C/D模块pom.xml(只依赖A) -->
<dependencies>
    <dependency>
        <groupId>xxx</groupId>
        <artifactId>A</artifactId>
    </dependency>
</dependencies>

效果:B模块有security功能,C/D模块无security依赖,彻底解决依赖污染,无需任何排除操作。

三、最终优化后的标准架构(企业级最佳实践)

优化后结构,彻底解决所有问题,兼顾复用性和可维护性:

bash 复制代码
父项目(parent)
├── 核心职责:只通过dependencyManagement管理所有依赖版本,dependencies为空
├── A模块(core-service,通用模块)
│   ├── 核心职责:提供公共数据层、服务层能力,所有子模块都需要
│   ├── 依赖:mybatis-plus、redis、数据库驱动、公共entity/dao/service/工具类
│   └── 不包含:web、security等非公共依赖
├── B模块(业务模块)
│   ├── 依赖:A模块 + 自身需要的security等依赖
│   └── 核心职责:业务逻辑实现(需要security)
├── C模块(业务模块)
│   ├── 依赖:只依赖A模块
│   └── 核心职责:业务逻辑实现(不需要security)
└── D模块(业务模块)
    ├── 依赖:只依赖A模块
    └── 核心职责:业务逻辑实现(不需要security)

四、关键注意点(避坑核心)

结合本次排查,总结10个高频避坑点,覆盖依赖管理和模块设计:

  1. 父模块核心原则:只管理版本,不引入依赖,所有依赖通过dependencyManagement锁定,dependencies标签为空,避免子模块被动继承不必要的依赖;

  2. bom导入顺序:自定义版本的bom(如spring-security-bom)必须放在spring-boot-dependencies前面,否则会被SpringBoot默认版本覆盖;

  3. 通用模块A的定位:只放"所有子模块都需要"的依赖,非公共依赖(如security、web)绝对不放入;

  4. 版本统一:所有核心依赖(tomcat、security、mybatis-plus等)在父模块properties中定义变量,统一管理,避免硬编码;

  5. 依赖传递:子模块依赖通用模块时,会自动继承其所有依赖,因此通用模块必须"干净",不引入非公共依赖;

  6. 避免排除滥用:排除(exclusion)只用于临时救急,长期解决方案是"按需依赖",拆分模块,而非大量使用排除;

  7. 版本验证:修改依赖后,用mvn dependency:tree命令验证版本是否正确,避免隐性冲突;

  8. SpringBoot与组件版本兼容性:SpringBoot 3.3.13默认对应spring-security 6.3.x,升级至6.5.x需确认API兼容性(本次实践无问题);

  9. 模块命名规范:通用模块建议命名为core-service、common-core等,明确其定位,避免混淆;

  10. 长期维护:定期清理无用依赖,保持模块"瘦身",避免依赖冗余导致的冲突和性能问题。

五、总结

SpringBoot多模块项目的核心痛点的是"依赖冲突"和"模块设计不合理",本次实践通过"父模块版本统一管理+通用模块瘦身+按需依赖",彻底解决了tomcat、spring-security版本冲突,以及非公共依赖污染问题。

核心思路:模块化设计的本质是"职责清晰、按需依赖",父模块管版本,通用模块管公共能力,业务模块管自身需求,这样既能保证代码复用,又能避免依赖混乱,提升项目可维护性。

本文所有配置均经过实际项目验证,可直接复制使用,若遇到类似问题,可对照排查,也欢迎在评论区交流补充。

结尾彩蛋:关注我,后续分享更多SpringBoot多模块实战技巧,避坑不踩雷!

相关推荐
xieliyu.6 小时前
Java算法精讲:双指针(三)
java·开发语言·算法
love530love6 小时前
LiveTalking 数字人项目 Windows 部署完全指南(EPGF 架构)
人工智能·windows·python·架构·livetalking·epgf
星辰徐哥6 小时前
Spring Boot 微服务架构设计与实现
spring boot·后端·微服务
星辰徐哥6 小时前
Spring Boot 数据导入导出与报表生成
spring boot·后端·ui
明夜之约6 小时前
Spring Boot 自动装配源码
java·spring boot·后端
Leaton Lee6 小时前
Spring Boot分层架构详解:从Controller到Service再到Mapper的完整流程
java·spring boot·后端·架构
Micro麦可乐6 小时前
Spring Boot 实战:从零设计一个短链系统(含完整代码与数据库设计)
数据库·spring boot·后端·哈希算法·雪花算法·短链系统
Jinkxs6 小时前
Resilience4j- 与 Spring Boot 快速集成:自动配置与基础注解使用
java·spring boot·后端
毕设源码_郑学姐6 小时前
计算机毕业设计springboot网络相册设计与实现 基于Spring Boot框架的在线相册管理系统开发与应用 Spring Boot驱动的网络影集设计与实践
spring boot·后端·课程设计
辣机小司6 小时前
【踩坑记录:Spring Boot 配置文件读取值不一致?警惕 YAML 的“八进制陷阱”与 SnakeYAML 版本之谜】
java·spring boot·后端·yaml·踩坑记录