第十三章. common-core 正则校验:Validator 三层架构详解
第十三章. common-core 正则校验:Validator 三层架构详解
Prorise第十三章. common-core 正则校验:Validator 三层架构详解
摘要:本章我们将深入 RVP 封装的“字段校验器”体系。我们将重点分析 RVP 独创的“Constants (字符串) -> Factory (编译) -> Validator (调用)”三层架构,并掌握 Hutool Validator(父类)和 RVP RegexValidator(子类)在数据校验中的实战应用。
在上一章(第十二章)中,我们深入学习了 RegexUtils (Hutool ReUtil),它是一个 文本处理 工具。我们掌握了它的“痛点”(原生 Pattern/Matcher)和核心功能(get, findAll, replaceAll),并系统学习了正则表达式的语法。
现在,我们转向 common-core 中 另一套 基于正则的工具体系。这套体系 不用于“文本处理”,而是专门用于“数据校验”(判断是非)。
RVP 在 common-core 的 utils.regex, factory 和 constant 包中,精心设计了一套 三层架构 来实现这个校验功能。本章,我们将深入解析这套架构的设计思想与实战应用。
本章学习路径

13.1. RVP 校验器架构:Constants -> Factory -> Validator
在 RVP 框架中,RegexUtils (Hutool ReUtil) 用于“文本处理”,而 RegexValidator (Hutool Validator) 用于“数据校验”。
在开始实战 RegexValidator 之前,我们必须先理解 RVP 围绕它构建的这套三层架构,这套架构展示了框架在“性能”与“复用”上的思考。
13.1.1. 架构概览:三层分离的设计哲学
我们知道,Java 原生的 Pattern.compile(regex) 是一个相对耗时的操作(它需要将正则表达式字符串编译成一个状态机)。如果每次调用 isMatch(regex, ...) 都重新编译一次,性能会很差。
RVP 的三层架构正是为了解决这个问题:
RegexConstants(常量层):只负责存储 正则表达式字符串 (String)。RegexPatternPoolFactory(工厂/编译层):负责将RegexConstants中的“字符串”编译 成Pattern对象,并 缓存(“池化”)起来。RegexValidator(应用/门面层):负责调用Factory中 已编译好 的Pattern对象,去执行最终的 校验 逻辑。
我们来逐层解析这三个文件。
13.1.2. 【层级 1】RegexConstants (字符串池)
文件路径:ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/RegexConstants.java
这个类继承了 Hutool 的 RegexPool(Hutool 的正则字符串常量池),并在其基础上,增强 了 RVP 业务所需的专属常量。
1 | // 位于 RegexConstants.java |
这一层非常纯粹,只定义 String 常量。例如 STATUS = "^[01]$",通过 ^ (开头) 和 $ (结尾) 锚定,确保这个字符串 必须且只能 是 “0” 或 “1”。
13.1.3. 【层级 2】RegexPatternPoolFactory (编译池)
文件路径:ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/factory/RegexPatternPoolFactory.java
这一层负责**“编译”和“缓存”**。它同样继承了 Hutool 的 PatternPool。
1 | // 位于 RegexPatternPoolFactory.java |
分析:此类中的所有字段都是 static final Pattern 类型。它调用了父类 PatternPool.get(String regex) 方法。
get(String regex) 方法(Hutool 提供)内部 自带缓存(PATTERN_CACHE)。它会检查这个 regex 字符串是否已经被编译过,如果已在缓存中,直接返回 Pattern 对象,如果不在,才调用 Pattern.compile(),存入缓存后再返回。
通过这一层,RVP 保证了 RegexConstants.DICTIONARY_TYPE 这个字符串 在整个应用生命周期中只被编译一次。
13.1.4. 【层级 3】RegexValidator (工具门面)
文件路径:ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/regex/RegexValidator.java
这是我们“二开”时 唯一应该调用 的类。它继承了 Hutool 的 Validator,并聚合了 Factory 层的成果。
1 | // 位于 RegexValidator.java |
分析:RegexValidator 作为“门面”,它做了两件事:
- 将
Factory中 已编译、已缓存 的Pattern对象(如ACCOUNT,STATUS)声明为public static final,方便我们 在其他地方(如RegexUtils)中复用。 - 提供了 RVP 专属的校验方法,如
isAccount(value),其内部调用了父类Validator.isMatchRegex(),并传入了 已编译好 的ACCOUNTPattern。
13.2. Hutool Validator 详解(RegexValidator 的父类)
在上一节中,我们理清了 RVP 的三层架构。我们知道 RVP RegexValidator 继承了 Hutool Validator。这个父类 Validator 已经提供了海量的通用校验方法(isMoney, isEmail, isUrl 等)。
13.2.1. isXxx() vs validateXxx():返回 boolean 与抛出异常的区别
Hutool Validator 的设计遵循一个清晰的命名约定,这对于我们“二开”使用者来说至关重要:
isXxx(value)- 功能:判断
value是否符合规则。 - 返回:
true(符合) 或false(不符合)。 - 特点:不抛出异常。你需要自己写
if逻辑来处理false的情况。 - 适用:用在业务逻辑的
if判断中。
- 功能:判断
validateXxx(value, errorMsg)- 功能:校验
value是否符合规则。 - 返回:
value(如果校验通过)。 - 特点:如果校验 不通过,会 立即抛出
ValidateException(errorMsg)。 - 适用:用于“断言”,不符合就中断程序(例如在
Controller或Service的入口处)。
- 功能:校验
13.2.2. 测试准备:创建 RegexValidatorTest
RegexValidator 是一个纯 Java 工具类,不依赖 Spring 容器。我们使用 main 方法来测试它。
文件路径:ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/utils/test/RegexValidatorTest.java
(我们创建一个新的 main 方法测试类)
1 | package org.dromara.demo.utils.test; |
13.2.3. 实战:isMoney, isEmail, isUrl, isBetween…
在 testHutoolValidatorApis() 方法中添加以下代码:
1 | // 位于 testHutoolValidatorApis() |
运行 main 方法,控制台输出(内联在注释中):
我们看到 isXxx 方法提供了大量预设的、开箱即用的校验逻辑。
我们继续在 testHutoolValidatorApis() 方法中添加 validateXxx 的测试:
1 | // 位于 testHutoolValidatorApis() |
运行 main 方法,控制台输出(内联在注释中):
分析:validateXxx 方法在校验失败时,会立即抛出 ValidateException 并携带我们指定的错误信息。这在需要中断程序执行的场景下非常有用。
13.3. RVP RegexValidator 增强实战
在 13.1.4 中我们看到,RVP 在 Hutool Validator 的基础上,增加了 RVP 业务专属 的校验(如 Account 和 Status)。
我们现在来实战这些 RVP 增强的方法。
13.3.1. 实战 isAccount() / validateAccount() (RVP 独有)
isAccount 检查是否符合 RVP 定义的“账号”规则(以字母开头,5-16 位,包含字母、数字、下划线)。
在 RegexValidatorTest.java 中创建 testRvpValidatorApis:
1 | public class RegexValidatorTest { |
运行 main 方法,控制台输出(内联在注释中):isAccount 精确地执行了 RegexConstants.ACCOUNT 常量中定义的规则。
13.3.2. 实战 isStatus() / validateStatus() (RVP 独有)
isStatus 检查是否符合 RVP 的“通用状态”规则(必须是 “0” 或 “1”)。
在 testRvpValidatorApis() 方法中继续添加:
1 | // 位于 testRvpValidatorApis() |
运行 main 方法(接上文输出):
分析:RVP 增强的 isAccount 和 isStatus 为通用业务校验提供了极大的便利。
13.4. 【RVP 真实场景】如何在“注解校验”中使用
在第十一章中,我们学习了 RVP 的参数校验体系,当时我们使用了 @Pattern(regexp = "...") 来执行自定义正则校验。
如果我们直接在注解里“硬编码”正则表达式,会带来两个严重问题:
- 难以阅读:复杂的正则(比如密码强度)写在 DTO 里,会让 DTO 变得臃肿不堪。
- 难以维护:如果“账号”或“密码”的校验规则需要全局修改,我们就必须去搜索并修改 所有 DTO(
UserBo,RegisterBody等)中的@Pattern注解,这极易遗漏。
RVP 的解决方案:这正是 RegexConstants.java(字符串池)存在的 真正价值。它提供了一个“单一事实来源”(Single Source of Truth),让所有校验注解都来引用它。
我们来看一个 RVP system 模块中的真实案例:
1 | // 位于 ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/RegexConstants.java |
这个正则要求密码必须同时包含“小写字母、大写字母、数字、特殊字符”,且至少 8 位。
现在,我们来看“注册”功能是如何使用它的:
文件路径:ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserBo.java(或其他相关 DTO)
1 | // 位于 SysUserBo.java (或 RegisterBody.java 等) |
分析:@Pattern 注解的 regexp 属性接收的只是一个 String。通过引用 RegexConstants.PASSWORD,SysUserBo 这个 DTO 就和“密码的具体规则”解耦 了。
【二开守则】:当我们“二开”时,如果需要新增一个全局复用的正则校验(例如“工号”employee_id):
- 第一步:在
RegexConstants.java中添加String EMPLOYEE_ID = "^EMP\\d{8}$";。 - 第二步:在我们的 DTO(例如
MyEmployeeBo)中,使用@Pattern(regexp = RegexConstants.EMPLOYEE_ID, ...)。
13.5. 性能陷阱分析:Validator vs Factory (缓存差异)
在 13.1.3 节中,我们提到 RVP 的 RegexPatternPoolFactory 是 带缓存 的,但 Hutool Validator(RegexValidator 的父类)中的 Pattern 常量 不带缓存。这是一个非常隐蔽的性能陷阱。
我们来对比一下 Hutool Validator 和 RVP RegexValidator 的设计差异。
Hutool Validator (父类) 源码(简化):
1 | // 位于 Hutool 的 Validator.java |
RVP RegexValidator (子类) 源码(精简):
1 | // 位于 RVP 的 RegexValidator.java |
分析:
- Hutool
Validator自身的Pattern常量(如Validator.GENERAL)是在类加载时直接Pattern.compile()的,它 没有 利用PatternPool的缓存机制。 - RVP
RegexValidator在设计上 更胜一筹。它 没有 直接compile,而是从RegexPatternPoolFactory中获取 已缓存 的Pattern。
【二开守则】为何应优先使用 RegexValidator.ACCOUNT?
- 性能更优:RVP 的
RegexValidator.ACCOUNT(或RegexValidator.STATUS等) 100% 来自PatternPool缓存。 - API 统一:RVP
RegexValidator提供了isAccount()/validateAccount()这样语义更清晰的方法,我们应优先调用它们。 - 避免使用:我们“二开”时,应 避免 直接使用 RVP 继承 自 Hutool
Validator的Pattern常量(如RegexValidator.GENERAL或Validator.NUMBERS),因为它们没有利用 RVP 的缓存体系。
13.6. 本章总结
在本章中,我们深入剖析了 RVP common-core 模块中的 正则校验 体系,它与上一章的“文本处理”(RegexUtils) 截然不同。
RVP 三层架构:我们首先理解了 RVP 为“校验”设计的精妙分层:
RegexConstants(常量层):只存String字符串(如"^[01]$")。RegexPatternPoolFactory(工厂层):调用PatternPool.get(),将字符串 编译 为Pattern对象并 缓存。RegexValidator(门面层):继承 HutoolValidator,并聚合Factory中 已缓存 的Pattern,提供给外部使用。
Hutool
Validator:我们掌握了RegexValidator父类Validator的核心使用方法:isXxx():用于if判断,返回boolean,不抛异常。validateXxx():用于“断言”,校验失败 立即抛出ValidateException。
RVP 真实场景:我们明确了
RegexConstants的 最主要用途——是在 DTO 中配合@Pattern(regexp = RegexConstants.PASSWORD)注解,实现“声明式校验”的统一维护。性能与“二开”守则:我们分析了 RVP
RegexValidator提供的Pattern(如ACCOUNT)是 带缓存 的,而它继承自 HutoolValidator的Pattern(如GENERAL)是 不带缓存 的。因此在“二开”中,我们应 优先使用 RVP 增强的isAccount()等方法。









