第六章. common-core 工具类(四):StringUtils 聚合与增强
第六章. common-core 工具类(四):StringUtils 聚合与增强
Prorise第六章. common-core 工具类(四):StringUtils 聚合与增强
摘要:本章我们将深入 StringUtils。RVP 的 StringUtils 并非从零开始,而是继承了 Apache Commons Lang3,并聚合了 Hutool StrUtil 的功能。我们将重点学习 RVP 在此基础上 增强 的核心方法,特别是与集合转换、路径匹配相关的功能。
在上一章中,我们深入剖析了 StreamUtils,这是一个与 Spring 无关的纯 Java 集合处理工具。我们掌握了它在 集合过滤、转换、分组和合并 中的“空安全”封装,并直接使用了 Lambda 表达式和方法引用来实战。
现在,我们来看 utils 包下的另一个“元老级”工具类——StringUtils。StringUtils 是任何 Java 项目中都不可或缺的,RVP 也不例外。但 RVP 的 StringUtils 并不是“重复造轮子”,而是巧妙地站在了巨人的肩膀上。
本章学习路径
我们将按照“知其然,知其所以然”的路径,先理解 RVP 的设计哲学,再实战其核心增强功能:

6.1. RVP 的“聚合”哲学:为何继承
我们首先打开 StringUtils 的源码:
文件路径:ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StringUtils.java
看到的第一行类定义就揭示了 RVP 的设计哲学:
1 | public class StringUtils extends org.apache.commons.lang3.StringUtils { |
RVP 的 StringUtils 继承 了 org.apache.commons.lang3.StringUtils。这意味着 RVP 的 StringUtils 自动拥有了 Apache Commons Lang3 库中所有强大的字符串处理方法。
6.1.1. Apache Commons Lang3:久经考验的基石
Apache Commons Lang3 是 Java 社区久经考验、最著名、最稳定的工具库之一。RVP 通过 extends 它,获得了诸如:
isBlank(String str)/isNotBlank(String str):空值判断(null、""、" "都算空)。isEmpty(String str)/isNotEmpty(String str):空值判断(null、""算空," "不算)。join(Iterable<?> iterable, String separator):集合拼接。abbreviate(String str, int maxWidth):字符串缩略(例如...)。- …以及上百个其他方法。
RVP 的开发者 不需要 重新实现这些基础功能。
6.1.2. Hutool StrUtil:Hutool 的功能补充
RVP 不仅继承了 Apache 的工具,还在方法内部大量“委托”了 Hutool 的 StrUtil。
例如,我们查看 RVP StringUtils 的 isEmpty 方法:
1 | // 位于 RVP 的 StringUtils.java |
RVP StringUtils 内部封装了 StrUtil,提供了 format、blankToDefault 等更现代、更便捷的方法。
6.1.3. RVP 的职责:聚合、增强与(isMatch)
所以,RVP StringUtils 的角色定位非常清晰:
| 角色 | 实现方式 | 举例 |
|---|---|---|
| 基础能力 | extends org.apache.commons.lang3.StringUtils | isBlank, join, abbreviate |
| 现代补充 | 内部调用 cn.hutool.core.util.StrUtil | isEmpty, format, toCamelCase |
| RVP 增强 | 框架 自己实现 的独有逻辑 | isMatch, str2List, splitTo |
这种“聚合”设计的好处是,作为开发者,我们 只需要 import org.dromara.common.core.utils.StringUtils 这一个类,就能同时使用来自 Apache、Hutool 和 RVP 三方的字符串功能,极大提升了便利性。
6.2. 测试准备:创建 StringUtilsTest (main 方法)
StringUtils 和 StreamUtils 一样,是纯 Java 工具类,不依赖 Spring 容器。因此,我们同样使用 main 方法来测试它。
6.2.1. 创建 utils.test.StringUtilsTest.java
我们在上一章创建的 utils.test 包中,再创建一个新的测试类:
文件路径:ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/utils/test/StringUtilsTest.java
1 | package org.dromara.demo.utils.test; |
6.2.2. 编写 main 方法与分屏技巧
和上一章一样,为了方便我们一边看 StringUtils.java 的源码,一边写 StringUtilsTest.java 的测试,我们可以使用 IDEA 的“分屏”功能:
- 在
StringUtils.java的文件标签页上右键。 - 选择
Split Right(向右分屏)。 - 这样左边就是我们的
StringUtilsTest.java测试类,右边就是StringUtils.java源码,方便我们随时查阅。
6.3. 基础增强:空值处理与格式化 (blankToDefault, format)
我们从最简单、最高频的两个增强方法开始。
6.3.1. 痛点:if (str == null) { str = "default"; }
在业务中,我们经常需要给一个可能为 null 的字符串赋予默认值,原生写法很啰嗦:
1 | // 痛点代码 (Bad Practice) |
6.3.2. RVP 封装 (blankToDefault)
RVP StringUtils(内部委托 StrUtil)提供了 blankToDefault,一行代码搞定:
1 | // 位于 RVP 的 StringUtils.java |
它会判断 str 是否为 null、"" 或 " ",如果是,就返回 defaultValue,否则返回 str 本身。
6.3.3. 字符串格式化 (format)
另一个痛点是字符串拼接:
- 原生:
"Hello, " + name + "! Welcome to " + location + "."(可读性差,有性能问题) - RVP 封装 (
format):StringUtils.format("Hello, {}! Welcome to {}.", name, location)
RVP 的 format(委托 StrUtil.format)使用了 SLF4J 日志框架的 {} 占位符风格,比 String.format()(%s 风格)更简洁易读。我们在 StringUtilsTest.java 中创建 testSimple 方法来测试这两个功能:
1 | // ... |
运行 main 方法,控制台输出:
1 | ... INFO ... StringUtils 测试开始... |
分析:
blankToDefault成功处理了null和空格。isEmpty和isBlank的区别必须牢记:isEmpty不认为空格是空,isBlank认为空格是空。RVP 两者都支持(一个来自 Hutool,一个继承自 Apache)。format成功替换了占位符。
6.4. 核心增强(一):字符串与集合的“双向”转换
在业务中,我们经常需要将数据库中存储的 ",1,2,3," 这种字符串,与 List<String> 或 Set<String> 进行相互转换。RVP 在这方面提供了强大的封装。
6.4.1. str2Set (字符串转 Set,带去重)
str2Set 会自动按分隔符(默认为 ,)切割字符串,并存入一个 Set,天然具有去重属性。
6.4.2. str2List (字符串转 List,带 filterBlank 和 trim 选项)
str2List 是 RVP 源码中大量使用的一个增强方法。它比 str.split(",") 健壮得多,它提供了两个关键的布尔参数:
filterBlank(过滤空白):如果为true,"1,,2"这种字符串中间的 空字符串 会被自动过滤掉。trim(去除首尾空白):如果为true," 1 , 2 "会被处理成"1"和"2",而不是" 1 "和" 2 "。
6.4.3. [重点] splitTo (自定义转换 Function)
splitTo 是 str2List 的“升级版”。str2List 只能返回 List<String>,而 splitTo 允许你传入一个 Function (转换函数),将切割后的字符串 立即转换为你想要的任何类型,例如 List<Integer> 或 List<Long>。
【实战闭环】
我们在 StringUtilsTest.java 中创建 testSplit 方法来测试这些功能:
1 | // ... |
运行 main 方法,控制台输出:
1 | ... INFO ... --- 5. 测试 str2Set (自动去重) --- |
分析:str2List 的 filterBlank=true 和 trim=true 参数非常实用。splitTo 结合 Convert::toInt(Hutool 的类型转换方法引用)可以一步到位地实现 String 转 List<Integer>。
6.5. 核心增强(二):驼峰与下划线 (toUnderScoreCase, toCamelCase)
在 Java 中我们使用驼峰命名(userName),在数据库中我们常用下划线命名(user_name)。StringUtils(委托 StrUtil)提供了它们之间的高效转换。
| 方法 | 示例(输入) | 示例(输出) | 用途 |
|---|---|---|---|
toUnderScoreCase | userName | user_name | 驼峰转下划线 |
toCamelCase | user_name | userName | 小驼峰(首字母小写) |
convertToCamelCase | user_name | UserName | 大驼峰(首字母大写) |
我们在 StringUtilsTest.java 中创建 testCamelCase 方法: |
这些方法非常简单我们就不再过多演示了
6.6. [RVP 核心] 路径匹配:isMatch 与 matches
这是 StringUtils 中 最核心 的 RVP 独有增强功能。它在框架的 权限拦截、URL 放行 等场景中扮演着至关重要的角色。
6.6.1. 核心原理:Spring AntPathMatcher (?, *, **)
RVP 的 isMatch 方法内部封装了 Spring 框架大名鼎鼎的 AntPathMatcher(Ant 路径匹配器)。它使用一套简洁的通配符规则:
?:匹配 单个 字符。- 规则:
/user/find? - 匹配:
/user/find1(✔),/user/findX(✔) - 不匹配:
/user/find(✘),/user/find11(✘)
- 规则:
*:匹配 一层路径 内的任意字符串(0 或多个字符)。- 规则:
/user/*/info - 匹配:
/user/123/info(✔),/user/admin/info(✔) - 不匹配:
/user/info(✘),/user/123/abc/info(✘, 不可跨层)
- 规则:
**:匹配 任意多层路径(0 或多层),注意,在新版本 Spring3 中**只能放置于路径最后方- 规则:
/user/** - 匹配:
/user/info(✔),/user/123/info(✔),/user/123/abc/info(✔)
- 规则:
6.6.2. [实战] isMatch (单规则匹配)
我们来实战验证 AntPathMatcher 的规则。
1 | // ... |
运行 main 方法,控制台输出:
1 | ... INFO ... --- 10. 测试 isMatch (单规则) --- |
6.6.3. [实战] matches (多规则列表匹配)
isMatch 只能判断一个规则,而 matches 允许我们传入一个 规则列表 (List),只要 URL 命中了 任意一个 规则,就返回 true。这正是 RVP 权限框架放行匿名 URL(如 /login, /captchaImage)的实现方式。
1 | // ... |
运行 main 方法,控制台输出:
1 | ... INFO ... --- 11. 测试 matches (多规则) --- |
6.7. 其他便捷工具 (containsAnyIgnoreCase, inStringIgnoreCase, padl)
StringUtils 还提供了一些其他便捷的方法。
6.7.1. 模糊包含 (containsAnyIgnoreCase)
判断一个字符串是否 包含 列表中的任意一个子串(忽略大小写)。
containsAnyIgnoreCase("abc", "A", "D")->true(因为"abc"包含了"a")
6.7.2. 精确包含 (inStringIgnoreCase)
判断一个字符串是否 等于 列表中的任意一个字符串(忽略大小写)。
inStringIgnoreCase("abc", "ABC", "D")->true(因为"abc"等于"ABC")inStringIgnoreCase("abc", "A", "D")->false(不等于)
6.7.3. 左侧补齐 (padl)
常用于生成固定长度的编号,例如将 123 补齐为 000123。我们在 StringUtilsTest.java 中创建 testOthers 方法:
1 | // ... |
运行 main 方法,控制台输出:
1 | ... INFO ... --- 12. 测试 containsAnyIgnoreCase (模糊包含) --- |
6.8. 本章总结
在本章中,我们深入了 RVP StringUtils 的设计与实战。我们必须理解,RVP 的 StringUtils 不是一个孤立的类,而是一个“聚合器”和“增强器”。
- 聚合器:它通过 继承 Apache Commons Lang3 和 内聚 Hutool StrUtil,让我们只
import一个类,就能使用三方工具包的功能。 - 增强器:RVP 提供了许多独有的、高价值的增强方法,我们重点掌握了:
- 集合/字符串转换:
str2List(带trim和filterBlank)、str2Set(去重)和splitTo(自定义转换Function)。 - 命名转换:
toCamelCase(小驼峰)和toUnderScoreCase(下划线)。 - 路径匹配(RVP 核心):
isMatch(单规则)和matches(多规则列表),它们是 RVP 权限体系的基石,基于AntPathMatcher(?,*,**) 规则。
- 集合/字符串转换:
掌握 StringUtils 的这些增强功能,能让我们在处理字符串时写出更健壮、更简洁的代码。









