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多模块实战技巧,避坑不踩雷!

相关推荐
学术阿凡提2 小时前
Spring Boot 优雅实现异步调用:从入门到自定义线程池与异常处理
java·数据库·算法
easy_coder2 小时前
ReAct 进入死循环?用 Harness 把它拉回来
人工智能·架构·云计算
我是无敌小恐龙2 小时前
Java SE 零基础入门Day06 方法重载+Debug调试+String字符串全套API详解(超全干货)
java·开发语言·人工智能·python·transformer·无人机·量子计算
xiaoye37082 小时前
java接口文档工具 swagger2和swagger3对比
java·服务器·前端
三维频道2 小时前
工业级三维扫描实测:汽车灯具复杂结构件的全尺寸 3D 测量方案分析
java·人工智能·python·数码相机·3d·汽车·汽车轻量化制造
tongyiixiaohuang2 小时前
基于轻易云的数据集成,实现企业系统间灵活对接
java·前端·数据库
码农飞哥2 小时前
从Java后端到AI应用开发,我这两年做了什么
java·开发语言·人工智能
阿丰资源2 小时前
基于SpringBoot智能化体育馆管理系统(附源码+文档+数据库,一键运行)
数据库·spring boot·后端
大龄码农-涵哥2 小时前
Spring Boot项目集成AI对话:使用Spring AI打造智能客服
人工智能·spring boot·spring