第一章. 核心基石:ruoyi-common-core 源码浅析
第一章. 核心基石:ruoyi-common-core 源码浅析
Prorise第一章. 核心基石:ruoyi-common-core 源码浅析
摘要:本章我们将对 RVP 5.x 架构中最重要的基础模块 ruoyi-common-core 进行源码“浅析”。common-core 是被所有业务模块和功能插件所依赖的“地基”。本章将概览其内部的配置、常量、实体、工具类和异常体系,帮助你理解 RVP 框架的底层设计规范。
在第一部分(第 1-23 章)中,我们作为“使用者”,已经全面掌握了 RuoYi-Vue-Plus 5.x 架构的理念、所有核心功能的使用、项目配置(YML)和 Maven 构建(POM)。
从本章开始,我们将转换视角,我们专注于学习后端知识,每一章节都提供一个实操流程作为示例 demo,教您快速上手并读懂 Ruoyi-Vue-Plus 的设计哲学
本章学习路径
我们将按照 ruoyi-common-core 模块的内部包结构,分阶段探索这个核心模块的职责:

1.1. 依赖与 SPI 启动链路
在深入源码之前,我们首先要解决两个问题:common-core 依赖了哪些“工具”?它自己又是如何被 Spring Boot“启动”的?
1.1.1. pom.xml:模块依赖的“工具箱”
ruoyi-common-core/pom.xml 文件定义了 RVP 框架最基础的依赖集合。它不依赖任何其他的 ruoyi-common-* 模块,是一个纯粹的“地基”模块。
它引入的核心依赖(节选)包括:
- Spring 框架:
spring-context-support(上下文支持)、spring-web(Web 支持)、spring-boot-starter-aop(AOP 切面)、spring-boot-starter-validation(校验注解)。 - 工具包:
commons-io、commons-collections4、cn.hutool:hutool-core(Hutool 工具包)。 - 代码简化:
org.projectlombok:lombok(Lombok)。 - 对象映射:
io.github.linpeilie:mapstruct-plus-spring-boot-starter(MapStruct-Plus)。 - IP 地址库:
org.lionsoul:ip2region(离线 IP 地址定位库)。
1.1.2. AutoConfiguration.imports:SPI 启动链路
ruoyi-common-core 作为一个“插件”,它内部的 @Configuration 配置类是如何被 ruoyi-admin 主启动类自动加载的呢?
答案在于 SPI (Service Provider Interface) 机制,这是 Spring Boot 自动装配的标准实现。
文件路径:ruoyi-common-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
1 | org.dromara.common.core.config.ApplicationConfig |
工作机制:
- RVP 5.x 遵循 Spring Boot 3.x 的自动装配规范。
ruoyi-admin启动时,Spring Boot 会扫描所有jar包中META-INF/spring/目录下的...AutoConfiguration.imports文件。- Spring Boot 读取到
common-core模块登记的这 4 个类路径。 - Spring Boot 自动将这 4 个类加载到 Spring 容器中,触发它们的自动配置(
@AutoConfiguration)和初始化(@Bean),从而完成了common-core模块的启动。
1.2. config 包:框架基础配置
在 1.1 节中,我们看到 common-core 通过 SPI 机制自动加载了 3 个核心配置类。config 包是框架启动和运行的基础。
1.2.1. ApplicationConfig:AOP 与异步
文件路径:.../config/ApplicationConfig.java
1 |
|
这个配置类的作用非常纯粹,通过 @EnableAspectJAutoProxy 开启 AOP 功能(@Log 切面等的基础),并通过 @EnableAsync 允许在代码中使用 @Async 注解执行异步任务。
1.2.2. ThreadPoolConfig:线程池
文件路径:.../config/ThreadPoolConfig.java
RVP 框架不推荐开发者在业务代码中随意 new Thread(),而是提供了统一的线程池配置。
1 |
|
- 职责:向 Spring 容器注册一个全局的、可调度的线程池
scheduledExecutorService。 - 特性:RVP 5.x 适配了 JDK 21 的虚拟线程。如果检测到虚拟线程环境,线程工厂 (ThreadFactory) 会使用虚拟线程来执行任务,提高并发性能。
- 销毁:通过
@PreDestroy钩子,确保在 Spring Boot 应用关闭时,能够“优雅停机”,等待线程池任务执行完毕。
1.2.3. ValidatorConfig:校验器
文件路径:.../config/ValidatorConfig.java
此配置类用于自定义 RVP 框架的 参数校验器(validation)。
1 | package org.dromara.common.core.config; |
- Fail-Fast 模式:
hibernate.validator.fail_fast = true是一个重要的性能优化。它告诉校验器,当校验一个 DTO 时,只要遇到第一个不合法的字段,就立即抛出异常,不再 继续校验剩余字段。 - 国际化 (i18n):
setValidationMessageSource(messageSource)将校验器的错误信息与 RVP 的国际化资源文件(messages_zh_CN.properties等)进行绑定。
1.2.4. ThreadPoolProperties:YML 属性 Bean
文件路径:.../config/properties/ThreadPoolProperties.java
这个类是 ThreadPoolConfig 的“数据源”,它使用 @ConfigurationProperties 注解来 映射 YML 文件中的属性。
1 |
|
@ConfigurationProperties(prefix = "thread-pool"):在application.yml中定义的thread-pool.queue-capacity等属性,会被 Spring Boot 自动注入到这个 Java Bean 的queueCapacity字段中。
1.3. constant 包:全局硬编码常量
constant 包的职责是“消除魔法值”。它将 RVP 框架中所有需要“硬编码”的字符串或数字,统一定义为静态常量,便于复用和维护。
CacheConstants.java
定义所有 Redis 缓存键 (Key) 的前缀。1
2
3
4
5
6
7
8// 在线用户令牌
String ONLINE_TOKEN_KEY = "online_tokens:";
// 系统配置
String SYS_CONFIG_KEY = "sys_config:";
// 数据字典
String SYS_DICT_KEY = "sys_dict:";
// 密码错误次数
String PWD_ERR_CNT_KEY = "pwd_err_cnt:";Constants.java
定义最通用的常量,如编码、状态码、默认值等。1
2
3
4
5
6
7String UTF8 = "UTF-8";
// 成功标记
String SUCCESS = "0";
// 验证码有效期(分钟)
Integer CAPTCHA_EXPIRATION = 2;
// 树结构顶级 ParentID
Long TOP_PARENT_ID = 0L;HttpStatus.java
统一定义 HTTP 响应状态码。1
2
3
4
5
6
7
8// 成功
int SUCCESS = 200;
// 客户端错误
int BAD_REQUEST = 400;
// 服务端错误
int ERROR = 500;
// [RVP 自定义] 警告
int WARN = 601;TenantConstants.java
定义多租户相关的常量。1
2
3
4
5
6// 超级管理员的用户ID
Long SUPER_ADMIN_ID = 1L;
// 租户管理员的角色 Key
String TENANT_ADMIN_ROLE_KEY = "admin";
// 默认租户ID(平台)
String DEFAULT_TENANT_ID = "000000";
1.4. domain 包:核心数据模型
domain 包定义了在 RVP 框架各个模块之间流转的核心数据结构,包括统一响应体、登录实体和 DTO。
1.4.1. R.java:统一响应体
R.java 是 RVP 框架 API 响应的 统一包装器。所有 Controller 返回给前端的数据,都必须封装在 R 对象中。
1 | public class R<T> implements Serializable { |
设计目的:确保所有 API 返回给前端的数据结构(code, msg, data)完全一致,便于前端进行统一的响应拦截和处理。
1.4.2. LoginUser.java:登录会话
LoginUser 是 RVP 中 最核心 的实体之一。它代表一个用户登录成功后的“会话上下文”,Sa-Token 会将这个对象存储到 Redis 中。
1 | public class LoginUser implements Serializable { |
设计目的:当用户请求 API 时,LoginHelper.getLoginUser() 会从 Redis(通过 Sa-Token)中反序列化出此对象。后续的“权限校验”、“数据权限”等操作,全部依赖于 LoginUser 中存储的 menuPermission、roles 和 deptId 等信息。
1.4.3. LoginBody 系列:登录参数 DTO
RVP 5.x 支持多种登录方式,common-core 模块为每种方式都定义了专属的 DTO (Data Transfer Object) 来接收前端参数。
| 登录 DTO | 关键字段 | 适用场景 |
|---|---|---|
LoginBody | clientId, tenantId, uuid | 所有登录方式的“基类” |
PasswordLoginBody | username, password | (该类在 domain.model 中) |
SmsLoginBody | phonenumber, smsCode | 短信验证码登录 |
EmailLoginBody | email, emailCode | 邮箱验证码登录 |
SocialLoginBody | source, socialCode | Gitee/GitHub 等三方登录 |
XcxLoginBody | appid, xcxCode | 微信小程序登录 |
1.4.4. DTO 系列:数据传输对象
DTO (Data Transfer Object) 用于 模块与模块之间 的数据传输。
设计目的:common-core 定义了 UserDTO、RoleDTO 等 DTO 接口,用于在 ruoyi-system 和 ruoyi-demo 等模块间传递数据。这实现了模块解耦:ruoyi-demo 只需依赖 common-core 中的 UserDTO,而 无需 依赖 ruoyi-system 模块中的 SysUser 实体,避免了模块间的强耦合。
1 | // .../domain/dto/UserDTO.java |
1.5. enums 包:通用枚举
enums 包提供了 RVP 框架中 最常用 的几种枚举,用于替代“魔法值”(如 0, 1)。
DeviceType.java:设备类型(PC,APP,XCX小程序,SOCIAL三方)。LoginType.java:登录类型(PASSWORD,SMS,EMAIL,XCX)。UserStatus.java:用户状态(OK("0", "正常"),DISABLE("1", "停用"))。UserType.java:用户类型(SYS_USER("sys_user"),APP_USER("app_user"))。
1.6. exception 包:统一异常体系
RVP 框架的异常处理是“集中式”的(由 ruoyi-common-web 中的 GlobalExceptionHandler 捕获)。common-core 的 exception 包则负责定义“抛出什么”。
1.6.1. BaseException:异常基类
文件路径:.../exception/base/BaseException.java
RVP 中所有自定义异常的“根”。
1 | public class BaseException extends RuntimeException { |
设计目的:BaseException 的核心是通过重写 getMessage() 方法,将“异常”与“i18n 国际化”绑定。当抛出一个带 code 的异常时,框架会自动去 messages.properties 文件中查找对应的错误文案。
1.6.2. ServiceException:业务异常
文件路径:.../exception/ServiceException.java
这是 RVP 框架中 最常用 的异常类。当业务逻辑校验失败时(例如“用户名已存在”),应抛出此异常。
1 | public class ServiceException extends RuntimeException { |
应用场景:throw new ServiceException("用户ID {} 不存在", userId);GlobalExceptionHandler 会捕获这个异常,并将其 message 封装到 R.fail() 中返回给前端。
1.6.3. 其他异常
FileException:继承BaseException,module固定为file,用于文件上传相关错误。CaptchaException:继承UserException(user模块),专用于验证码错误(code固定为user.jcaptcha.error)。
1.7. factory 包:YML 加载器
factory 包提供了 RVP 5.x 配置加载的关键工具。
文件路径:.../factory/YmlPropertySourceFactory.java
1 | public class YmlPropertySourceFactory implements PropertySourceFactory { |
设计目的:Spring 的 @PropertySource 注解默认只认识 .properties 文件。YmlPropertySourceFactory 的作用就是“翻译”,它告诉 @PropertySource 如何读取 .yml 文件并将其转换为 Spring 能理解的 PropertySource 对象。
1.8. service 包:“接口下沉”模式
这是 RVP 5.x 架构为了 解决“循环依赖”而设计的最核心的模式。
1.8.1. 痛点:模块间的“循环依赖”
在 4.x 架构中,模块职责不清,很容易出现:
ruoyi-demo(模块 A)需要调用“获取用户信息”的功能。ruoyi-system(模块 B)实现了“获取用户信息”的功能。- 因此,
ruoyi-demo必须在pom.xml中依赖ruoyi-system。 - 问题出现:如果
ruoyi-system模块(例如工作流)也需要调用ruoyi-demo中的某个服务,就产生了 A -> B -> A 的循环依赖,导致 Maven 构建失败。
1.8.2. RVP 5.x 解决方案:“接口下沉”
RVP 5.x 将这种“跨模块”需要调用“公共服务”的“接口 (Interface),全部“下沉”到了最底层的 ruoyi-common-core 模块中。
文件路径:.../service/UserService.java (接口)
1 | // 位于 common-core (最底层) |
文件路径:.../service/DictService.java (接口)
1 | // 位于 common-core (最底层) |
common-core 中还定义了 DeptService、ConfigService、OssService 等多个下沉接口。
1.8.3. 实现类:SysUserServiceImpl
接口在 common-core 中定义,但 实现在 ruoyi-system 模块 中。
文件路径:ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java
1 | // 位于 ruoyi-system (业务层) |
最终效果(依赖解除):
ruoyi-system依赖common-core(实现UserService)。ruoyi-demo依赖common-core(调用UserService)。ruoyi-demo和ruoyi-system之间 没有任何 Maven 依赖关系。- 当
ruoyi-demo调用UserService接口时,Spring 的 IoC 容器会自动注入ruoyi-system中的SysUserServiceImpl实例,成功实现跨模块调用,彻底解决了循环依赖。
1.9. utils 包:核心工具类
utils 包提供了大量静态工具类,是 RVP 框架功能实现的核心“齿轮”。
| 工具类 | 核心职责 | 示例 |
|---|---|---|
SpringUtils.java | [核心] Spring 容器持有器。在非 Spring 管理的类中,获取 Bean 或激活 profile。 | getBean(Class<T> clazz)isVirtual() (判断是否虚拟线程) |
MessageUtils.java | 国际化 (i18n) 工具。用于从 messages_zh_CN.properties 中获取文案。 | message(String code, Object... args) |
StringUtils.java | 字符串工具(扩展 Hutool)。 | isMatch() (Ant 路径匹配)splitTo(String str, ...) (带转换的分割) |
DateUtils.java | 日期工具(扩展 Hutool)。 | validateDateRange(start, end, max) (校验日期范围) |
AddressUtils.java | IP 地址工具。 | resolverIPv4Region(ip) (使用 ip2region 库解析 IP 归属地) |
ReflectUtils.java | 反射工具。 | invokeGetter(obj, field)invokeSetter(obj, field, value) |
SqlUtil.java | SQL 工具。 | filterKeyword(value) (基础 SQL 注入关键词过滤) |
TreeBuildUtils.java | 树结构工具。 | build(list, parser) (基于 Hutool 的 TreeUtil 封装) |
ValidatorUtils.java | 校验工具。 | validate(object, groups) (手动触发 Bean 校验) |
1.10. validate 包:校验分组
最后,validate 包配合 ValidatorConfig 和 ValidatorUtils,提供了“分组校验”的能力。
文件路径:.../validate/AddGroup.java
1 | public interface AddGroup { } |
文件路径:.../validate/EditGroup.java
1 | public interface EditGroup { } |
文件路径:.../validate/QueryGroup.java
1 | public interface QueryGroup { } |
设计目的:这些 空接口(标记接口),用于在 DTO/BO 中对校验规则进行分组。
应用场景:在 SysUserBo(用户 BO)中:
1 | // 示例 |
当 Controller 中使用 @Validated(AddGroup.class) 时,框架 只校验 password 和 nickName;当使用 @Validated(EditGroup.class) 时,框架 只校验 nickName 而 忽略 password。









