14-[架构重构] 迈向企业级:Maven 多模块项目

14-[架构重构] 迈向企业级:Maven 多模块项目
Prorise7. [架构重构] 迈向企业级:Maven 多模块项目
摘要: 随着我们功能的不断累加,我们最初的单体项目正变得日益臃肿和混乱。本章,我们将进行一次意义深远的架构重构,借鉴 RuoYi 等企业级框架的设计思想,亲手将我们的项目改造为一个职责清晰、高度解耦的 Maven 多模块工程。这次重构将极大提升项目的可维护性和扩展性,为未来承载更复杂的业务打下坚实的基础。
7.1. 痛点分析与模块化思想
7.1.1. 现状:单体应用的困境
在开始重构之前,我们首先要清醒地认识到当前项目存在的“成长烦恼”。请看我们目前的项目包结构:
虽然我们遵循了 controller
, service
, mapper
等内部分层,但从整体工程角度看,它依然是一个单体应用。随着项目规模的扩大,这种结构逐渐暴露出一系列问题:
- 包结构扁平化:
advice
,common
,config
,controller
,converter
,dto
,entity
… 所有这些包都挤在同一个src
目录下。当未来我们新增“订单模块”、“产品模块”时,这个列表将变得越来越长,不同业务模块的代码会混杂在一起,难以管理。 - 依赖关系混乱: 我们的
pom.xml
文件中,既有spring-boot-starter-web
这样的 Web 框架依赖,也有mybatis-plus
这样的数据持久化依赖,还有hutool-all
这样的通用工具依赖。所有依赖都打包在一起,职责不清,我们很难说清楚哪个模块具体需要哪个依赖。 - 高度耦合: 所有代码都在一个模块中,理论上任何一个类的改动(即使是一个工具类),都可能需要对整个项目进行重新的编译、测试和部署,模块间的边界是模糊的。
- 复用性差: 如果我们想在另一个新项目中复用当前的
common
包或util
包下的工具类,除了复制粘贴代码,没有更优雅的方式。
7.1.2. 蓝图:借鉴 RuoYi 的模块化设计
要解决以上问题,我们需要引入软件工程领域一个非常重要的思想——模块化,而在 Maven 项目中,实现模块化的最佳实践就是构建**多模块项目 **。
我们将借鉴 RuoYi 这类成熟企业级框架的设计精髓,将我们的单体应用“分而治之”,拆分为以下四个核心模块:
1 | 📂 spring-boot-demo (父模块, pom) |
各模块核心职责:
模块 | 核心职责 | 详细说明 |
---|---|---|
demo-common | 通用工具与核心定义 | 存放与具体业务无关的、可被所有其他模块复用的公共类,如 Result 封装、自定义异常、Hutool 依赖、枚举、常量等。它是一个高度抽象的底层工具包。 |
demo-framework | 框架核心配置与增强 | 存放与 Spring 框架本身相关的配置和增强功能,如 WebMvcConfigurer (CORS, 拦截器)、MybatisPlusConfig 、SpringDocConfig 、全局异常处理器等。它为业务模块提供技术支撑。 |
demo-system | 具体业务模块 | 存放具体业务模块的代码。目前我们只有一个“用户管理”功能,所以所有 User 相关的 Controller, Service, Mapper, DTO, VO 都会放在这里。未来新增“订单模块”时,我们会创建一个新的 demo-order 业务模块。 |
demo-admin | 应用启动入口 | 这是一个非常轻量的“胶水”模块。它本身几乎没有代码,只包含 main 启动类。它的主要作用是通过 Maven 依赖,将 framework 和 system 等模块组装起来,最终打包成一个可运行的 Spring Boot 应用。 |
通过这样的拆分,我们的项目结构将变得异常清晰。每个模块都有明确的边界和单一的职责,实现了物理层面的解耦。这为我们后续的开发、测试和维护工作,奠定了坚实的企业级工程基础。
7.2. 实战:父工程改造与依赖管理
理论学习完毕,我们现在正式开始对项目进行“大手术”。第一步,也是最关键的一步,就是将我们当前的 spring-boot-demo
项目,从一个可运行的 jar
工程,改造为一个只负责管理、不包含任何代码的 pom
父工程。
7.2.1. 修改 packaging 为 pom
请打开项目根目录下的 pom.xml
文件。我们需要做的第一件事,就是将 <packaging>
标签的内容,从默认的 jar
修改为 pom
。
文件路径: pom.xml
(修改)
1 | <groupId>com.example</groupId> |
这个小小的改动,从根本上改变了 pom.xml
的性质。它告诉 Maven:“我不再是一个需要被打成 jar 包的普通应用了,我的新身份是一个父工程,我的职责是管理我的子模块们”。
修改后,您可能会发现项目中的 src
目录在 IDE 中变成了灰色或普通文件夹图标,这是完全正常的,因为 pom
类型的父工程本身不包含业务代码。
7.2.2. 统一版本:使用 <properties>
和 <dependencyManagement>
目前,我们所有的依赖都直接写在 <dependencies>
标签下,版本号散落在各处。当项目模块变多时,这会导致版本混乱和难以升级。专业的做法是使用 <dependencyManagement>
在父工程中进行统一的版本声明。
我们将进行两步操作:
- 使用
<properties>
标签将所有版本号提取为变量。 - 将整个
<dependencies>
块移动到<dependencyManagement>
块内部。
文件路径: pom.xml
(修改)
1 |
|
代码解析:
<properties>
: 我们将所有第三方库的版本号都提取到了这里,便于未来统一升级。<dependencyManagement>
: 这就像一个“依赖版本仲裁中心”。在这里声明的依赖,并不会被实际引入,它只是一个“版本清单”。- 清空
<dependencies>
: 父pom
作为一个管理者,它本身不应该包含任何具体的实现代码,因此也不需要直接依赖任何jar
包。
经过改造后,所有子模块未来在引入这些依赖时,只需要提供 groupId
和 artifactId
,无需再指定 version
,它们会自动继承父工程中声明的版本。这保证了整个项目所有模块使用的依赖版本是高度统一的。
最后,父工程需要知道它到底管理了哪些子模块。我们在 pom.xml
的顶层(与 <properties>
同级)添加 <modules>
标签,并在其中声明我们规划好的四个子模块。
7.3. 实战:拆分核心模块
父工程的改造完成后,现在就如同我们已经画好了建筑蓝图。接下来的任务,就是按照蓝图,一砖一瓦地搭建起每个独立的模块(房间),并将我们现有的代码(家具)搬运到它们各自正确的位置。
在 IDEA 中,您可以通过右键点击项目根目录 -> New
-> Module...
来创建新的 Maven 子模块。请确保在创建时,它能被正确识别为当前父工程的子模块。
7.3.1. 创建并迁移 demo-common
(通用工具模块)
这个模块是我们的基础工具库,存放与具体业务无关的、可被所有其他模块复用的公共代码。
第一步:创建模块与 pom.xml
首先,在项目根目录下创建 demo-common
文件夹,并在其中创建 pom.xml
文件。
文件路径: demo-common/pom.xml
(修改文件)
1 |
|
注意:在子模块的 pom.xml
中,我们引入依赖时无需再指定 <version>
和 <groupId>
,因为它会从父工程的 <dependencyManagement>
中自动继承,这正是父工程的价值所在!
第二步:迁移代码
现在,我们将主项目中所有通用的代码包,整体移动到 demo-common
模块的 src/main/java/
目录下。
迁移清单:
common
包 (包含Result.java
,ResultCode.java
)enums
包 (包含UserStatusEnum.java
)exception
包 (包含BusinessException.java
)util
包 (目前没有文件)
7.3.2. 创建并迁移 demo-framework
(框架核心模块)
这个模块负责所有与 Spring 框架相关的配置和增强功能。
第一步:创建模块与 pom.xml
文件路径: demo-framework/pom.xml
(修改文件)
1 |
|
第二步:迁移代码
迁移清单:
advice
包 (包含GlobalExceptionHandler.java
)config
包 (包含MybatisPlusConfig.java
,SpringDocConfig.java
,WebConfig.java
)converter
包 (包含StringToUserStatusEnumConverter.java
)interceptor
包 (包含AuthInterceptor.java
,LogInterceptor.java
)
7.3.3. 创建并迁移 demo-system
(系统业务模块)
这个模块是我们的核心业务模块,存放所有与“用户管理”相关的代码。
最理想的结构是让 demo-system 依赖 demo-framework,而不是重复声明 spring-boot-starter-web、mybatis-plus 等依赖。这样可以形成清晰的依赖链:admin -> system -> framework -> common
第一步:创建模块与 pom.xml
文件路径: demo-system/pom.xml
(新增文件)
1 |
|
第二步:迁移代码
迁移清单:
controller
包 (所有 Controller)dto
包 (所有 DTO)entity
包 (所有 Entity)mapper
包 (所有 Mapper)service
包 (所有 Service 及其 impl)validation
包 (所有校验分组)vo
包 (所有 VO)
在修复代码之前,必须先让项目能正确加载。
- 打开根目录下的
pom.xml
(spring-boot-demo/pom.xml
)。 - 找到
<modules>
标签。 - 注释或删除那一行
<module>demo-admin</module>
,因为这个模块还不存在。 - 在 IDEA 右侧的 Maven 面板中,点击刷新按钮,确保项目能无错误地加载。
步骤 1: 执行全局搜索和替换
现在,我们要把所有旧的包引用 com.example.springbootdemo.*
替换成新的包名。
按下快捷键
Ctrl + Shift + R
(Windows/Linux) 或Cmd + Shift + R
(Mac)。这将打开全局搜索和替换窗口。执行第一次替换:
在上面的输入框 (Find) 中,输入旧的包前缀:
com.example.springbootdemo
在下面的输入框 (Replace) 中,输入新的包前缀:
com.example
重要: 仔细检查下面的预览窗口,确保替换是你想要的结果。
- 点击 “Replace All” 或 “Replace” 按钮。
因为我们的新包结构是 com.example.democommon
, com.example.demosystem
等,而不是 com.example.springbootdemo.common
。原来的所有 DTO, VO, Service 等都引用了 com.example.springbootdemo
下的类。
建议开启自动导包,对于之前结构的包都进行删除并全部自动导入的操作
步骤 2: 优化 Imports
替换完成后,可能还有一些多余或无效的 import
语句。
- 在项目根目录上右键。
- 选择 “Optimize Imports”。IDEA 会自动清理所有文件中未使用的导入。
- 你也可以使用快捷键
Ctrl + Alt + O
(Windows/Linux) 或Cmd + Option + O
(Mac) 在单个文件中操作。
7.4. 实战:创建 Admin 启动模块
7.4.1. 创建 demo-admin
模块与 pom.xml
首先,请按照同样的方式,创建第四个子模块:demo-admin
。
这个模块的 pom.xml
非常关键,它不直接依赖具体的第三方库,而是依赖于我们自己创建的 framework
和 system
模块,像胶水一样将它们粘合在一起。同时,它需要 spring-boot-maven-plugin
插件来将整个应用打包成一个可执行的 jar
。
文件路径: demo-admin/pom.xml
(新增文件)
1 |
|
7.4.2. 迁移并改造启动类
现在,我们将父工程根目录下那个“光杆司令” SpringBootDemoApplication.java
移动到 demo-admin
模块中。
同时,我们需要对它进行一个小小的改造,以确保 Spring Boot 能够扫描到我们所有子模块中的组件(Bean)。
文件路径: demo-admin/src/main/java/com/example/springbootdemo/SpringBootDemoApplication.java
(迁移并修改)
1 | package com.example.demoadmin; |
7.4.3. 清理项目
重要信息: 记得将配置文件搬迁到 demo-admin
上
为了让我们的项目结构达到最终的完美形态,请执行以下清理操作:
- 删除父工程根目录下的
src
文件夹: 因为SpringBootDemoApplication.java
已经被移动到了demo-admin
模块,父工程根目录下的src
文件夹现在是多余的,可以安全删除。 - 删除子模块中多余的启动类: 您在
demo-common
,demo-framework
和demo-system
中由 IDE 自动生成的Demo...Application.java
和...Tests.java
文件是不需要的,也请一并删除,因为整个应用只有一个启动入口和一套集成的测试环境。
7.4.4. 回归测试:验证重构结果
现在,整个项目的结构已经焕然一新。请确保您在 IDE 中选择的启动目标是 demo-admin
模块中的 SpringBootDemoApplication.java
。
为了不影响读者的阅读和可能读者操作失误,这里提供一个整理好的仓库供读者快速Clone Spring_Mvc_Study: 教学用的SpringMVC文件
启动应用后,请打开 Swagger UI 或 Postman,尝试调用一个我们之前写好的接口,例如 GET /users/1
。
如果接口能够正常返回数据,那么——
恭喜您! 您已经成功地将一个单体应用,重构为了一个结构清晰、职责分明、高度解耦的企业级多模块项目!这次重构的成功,为您未来驾驭大型复杂项目奠定了坚实的工程基础。