若依多模块工程化设计

本文基于若依3.9.2、SpringBoot3版本。

image

若依多模块工程化设计

为什么用多模块

单体项目把所有代码塞在一个模块里,随着业务增长会遇到几个实际问题:改动一个工具类需要重新编译整个项目、不同团队互相踩代码、某些功能想单独拆出来用做不到。多模块设计把项目按职责拆分成独立的 Maven 子工程,每个模块有自己的 pom 文件和源码目录,带来三个直接好处:

  • 职责隔离:每个模块只关注一件事(如安全、数据、工具),边界清晰
  • 独立编译:修改 system 模块的业务代码,common 和 framework 无需重新构建
  • 可裁剪:不需要的模块(如代码生成、定时任务)可以直接删除,不影响核心功能

若依正是这样做的——6 个模块各司其职,通过依赖关系组合成一个完整的应用。

模块总览

若依采用 Maven 多模块设计,共 6 个模块:

com.ruoyi(父POM)
├── ruoyi-common        公共基础层
├── ruoyi-system        系统业务层
├── ruoyi-framework     框架核心层
├── ruoyi-quartz        定时任务模块
├── ruoyi-generator     代码生成模块
└── ruoyi-admin         启动入口层

模块间的依赖关系:

                    ┌──────────────┐
                    │  ruoyi-admin │  ← 最终启动入口
                    └──────┬───────┘
                           │
              ┌────────────┼────────────┐
              │            │            │
              ▼            ▼            ▼
     ┌────────────┐ ┌──────────┐ ┌──────────────┐
     │ ruoyi-     │ │ ruoyi-   │ │ ruoyi-       │
     │ framework  │ │ quartz   │ │ generator    │
     └─────┬──────┘ └────┬─────┘ └──────┬───────┘
           │             │              │
           ▼             ▼              ▼
     ┌────────────┐ ┌──────────┐ ┌──────────┐
     │ ruoyi-     │ │ ruoyi-   │ │ ruoyi-   │
     │ system     │ │ common   │ │ common   │
     └─────┬──────┘ └──────────┘ └──────────┘
           │
           ▼
     ┌──────────┐
     │ ruoyi-   │
     │ common   │  ← 最底层
     └──────────┘

各模块职责与打包方式:

模块 职责 打包
ruoyi-common 统一返回对象、分页封装、自定义注解、常量、异常体系、工具类 jar
ruoyi-system 用户、角色、菜单、部门、字典等系统管理功能的 Entity/Mapper/Service jar
ruoyi-framework Spring Security 配置、AOP 切面、全局异常处理、异步任务管理 jar
ruoyi-quartz 基于 Quartz 的定时任务管理,支持 CRUD 与暂停恢复 jar
ruoyi-generator 基于 Velocity 模板引擎从数据库表生成前后端代码 jar
ruoyi-admin 启动类、Controller、配置文件,最终打包部署的模块 jar/war

Maven 的依赖传递机制下,admin 无需直接声明 common 的依赖,通过 framework → system → common 的传递链自动获得。

可裁剪设计

quartz 和 generator 与核心业务无耦合,可安全删除。若不需要定时任务或代码生成功能,每个模块只需三步即可移除:

  1. 删除模块目录
  2. 从父 POM 的 <modules> 中移除对应的 <module>
  3. 从 ruoyi-admin 的 <dependencies> 中移除对应依赖

quartz 模块还需额外删除数据库中的 qrtz_* 表,generator 模块需删除 gen_tablegen_table_column 表。

父工程的统一管理

在多模块设计中,根目录下的父工程不写代码,只提供 pom 文件,承担三项核心职责:聚合子模块统一版本管理配置构建环境

聚合子模块

父工程的打包方式设为 pom,自身不参与打包,仅用作子模块的管理容器:

<modules>
    <module>ruoyi-admin</module>
    <module>ruoyi-framework</module>
    <module>ruoyi-system</module>
    <module>ruoyi-quartz</module>
    <module>ruoyi-generator</module>
    <module>ruoyi-common</module>
</modules>
<packaging>pom</packaging>

<modules> 标签声明哪些子目录属于本项目的子模块。配置后,在根目录执行一条构建命令就能让 Maven 按依赖关系自动排列构建顺序,若依的构建顺序为:common → system → framework → quartz → generator → admin。

统一版本管理

父工程通过 <properties> 定义版本号变量,通过 <dependencyManagement> 声明受管控的依赖及其版本:

<properties>
    <spring-boot.version>3.5.11</spring-boot.version>
</properties>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>${spring-boot.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencyManagement> 只声明版本,不引入依赖。子模块在自己的 <dependency> 中引用这些依赖时可以省略 <version>,由父工程统一管控。所有模块使用同一框架时不会出现版本不一致的问题,升级时只需改父 POM 的一处变量。

配置构建环境

父工程统一管理构建插件和远程仓库地址:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.13.0</version>
            <configuration>
                <parameters>true</parameters>
                <source>${java.version}</source>
                <target>${java.version}</target>
                <encoding>${project.build.sourceEncoding}</encoding>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <version>${spring-boot.version}</version>
        </plugin>
    </plugins>
</build>

在父 POM 的 <build><plugins> 中直接声明的插件会被所有子模块继承。同时父工程还配置了阿里云镜像仓库,所有子模块下载依赖和插件时自动使用该地址,无需重复配置。

子模块的声明与继承

每个子模块在自身的 pom 文件中声明父工程:

<parent>
    <artifactId>ruoyi</artifactId>
    <groupId>com.ruoyi</groupId>
    <version>3.9.2</version>
</parent>

建立继承关系后,子模块自动获得父工程的版本管控、插件配置和仓库地址。子模块的 groupIdversion 与父工程一致时可以省略,只需保留 artifactId

admin 模块结构

admin 是整个框架的后端入口,包含了启动类、所有 Controller 和配置文件:

ruoyi-admin
├── pom.xml
└── src/main/
    ├── java/com/ruoyi/
    │   ├── RuoYiApplication.java          # 启动类
    │   ├── RuoYiServletInitializer.java   # Web容器部署初始化器
    │   └── web/
    │       ├── controller/
    │       │   ├── common/                # 公共接口(验证码、文件上传)
    │       │   ├── monitor/               # 监控接口(缓存、服务器、日志)
    │       │   ├── system/                # 系统管理接口(用户/角色/菜单等)
    │       │   └── tool/                  # 工具接口
    │       └── core/config/
    │           └── SwaggerConfig.java     # API文档配置
    └── resources/
        ├── application.yml                # 主配置文件
        ├── application-druid.yml          # 数据源配置
        └── logback.xml                    # 日志配置

common 模块结构

common 是所有模块需要的公共基础层,定义了注解、常量、异常、工具类和实体类:

ruoyi-common/src/main/java/com/ruoyi/common/
├── annotation/                # 自定义注解(@Log、@DataScope、@DataSource、@RateLimiter 等)
├── config/                    # 配置类(项目配置、脱敏序列化)
├── constant/                  # 常量(缓存key、状态码、用户常量等)
├── core/
│   ├── controller/            # Controller基类
│   ├── domain/                # 统一返回对象(AjaxResult、BaseEntity、分页对象)
│   ├── redis/                 # Redis工具类
│   └── text/                  # 字符集/类型转换工具
├── enums/                     # 枚举(操作类型、数据源类型、限流类型等)
├── exception/                 # 异常体系(业务异常、文件异常、用户异常)
├── filter/                    # 过滤器(XSS防护、防盗链、可重复读)
└── utils/                     # 工具类(字符串、日期、文件、安全、HTTP等)

其余模块的内部结构遵循标准的分层模式:system 按 domain/mapper/service 组织;framework 按功能域组织(aspectj/config/security/web);quartz 和 generator 各自独立维护 domain/mapper/service/util 结构。

admin 的构建插件

在所有子模块中,只有启动模块 ruoyi-admin 额外配置了构建插件:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <addResources>true</addResources>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>repackage</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-war-plugin</artifactId>
            <version>3.1.0</version>
            <configuration>
                <failOnMissingWebXml>false</failOnMissingWebXml>
                <warName>${project.artifactId}</warName>
            </configuration>
        </plugin>
    </plugins>
    <finalName>${project.artifactId}</finalName>
</build>

只有 admin 需要打成可执行的 fat jar,其余模块打成普通 jar 供依赖传递。repackage 目标将普通 jar 重新打包为包含所有依赖的可执行 fat jar,可以直接用 java -jar 运行。

不继承 starter-parent 的 SpringBoot 接入

若依的根 pom 中没有继承 spring-boot-starter-parent,原因是 Maven 的 <parent> 是单继承——子模块的父工程必须是 com.ruoyi:ruoyi,没有多余的继承位留给 starter-parent。

若依在自有父 POM 中通过 BOM import 引入 SpringBoot 的版本管理,再手动补齐编译配置和插件声明,达到等价效果:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>${spring-boot.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<type>pom</type> 声明引入的是 POM 构件,<scope>import</scope> 将其 <dependencyManagement> 合并到当前父工程中。这是 Spring 官方文档推荐的多模块项目做法。

⚠️ <dependencyManagement> 只管理版本、不引入依赖。import 了 BOM 之后,子模块要用哪些依赖仍需自己写 <dependency> 声明(只是可以省略 <version>)。"import 了 BOM 就能直接用 Spring 的类"是误解。

工程化思考

插件声明位置:若依把 spring-boot-maven-plugin 直接写在父 POM 的 <build><plugins> 里,而不是 <pluginManagement><build><plugins> 的语义是"所有子模块执行该插件",但这里实际上依赖了"不绑定 execution 则插件不执行"这一隐式规则——只有 admin 显式绑定了 repackage,所以只有 admin 真正生效,其余模块不受影响。更规范的写法是父 POM 用 <pluginManagement> 统一插件版本和默认配置,admin 里单独声明并绑定 execution,意图更明确。若依的写法能正确工作且无副作用,但可读性稍差。

依赖传递 vs 显式声明:ruoyi-admin 不需要直接声明对 ruoyi-common 的依赖,因为通过 framework → system → common 的传递链可以自动获得。但若依选择让每个模块只声明直接依赖,这种做法降低了耦合的显式程度——未来如果调整传递链,上层的隐式依赖可能失效。在大型项目中,对关键依赖做显式声明是一种防御性编程策略。

打包灵活性:admin 同时配置了 spring-boot-maven-pluginmaven-war-plugin,意味着它既可以打成可执行 jar(默认),也可以打成 war 部署到外部 Tomcat。这是若依为不同部署场景预留的工程化考量。

posted @ 2026-08-18 23:38  咖啡八杯  阅读(0)  评论(0)    收藏  举报