工具类库 (Utils)
概述
ruoyi-common-core 的工具类库提供了丰富的工具方法,覆盖字符串处理、日期时间、反射操作、网络地址、文件处理、数据流处理等多个方面。这些工具类采用静态方法设计,线程安全,高度可复用。
目录结构
utils/
├── StringUtils.java # 字符串增强工具
├── DateUtils.java # 日期时间工具
├── ObjectUtils.java # 对象操作工具
├── StreamUtils.java # Stream流处理工具
├── TreeBuildUtils.java # 树形结构构建工具
├── ValidatorUtils.java # 参数校验工具
├── SpringUtils.java # Spring上下文工具
├── ServletUtils.java # Servlet请求响应工具
├── ThreadUtils.java # 线程处理工具
├── MapstructUtils.java # 对象映射工具
├── MessageUtils.java # 国际化消息工具
├── file/
│ ├── FileUtils.java # 文件处理工具
│ └── FileTypeUtils.java # 文件类型工具
├── ip/
│ ├── AddressUtils.java # IP地址解析工具
│ └── RegionUtils.java # IP区域定位工具
├── reflect/
│ └── ReflectUtils.java # 反射操作工具
├── regex/
│ ├── RegexPatterns.java # 正则表达式模式
│ ├── RegexPatternPool.java # 正则模式池
│ ├── RegexValidator.java # 正则校验器
│ └── RegexUtils.java # 正则工具
└── sql/
└── SqlUtil.java # SQL安全工具核心工具类
1. 字符串工具 (StringUtils)
扩展了 Apache Commons Lang3 的 StringUtils,提供更多实用方法。
基础操作
// 空值处理
String result = StringUtils.blankToDefault("", "默认值");
boolean isEmpty = StringUtils.isEmpty("");
boolean isNotEmpty = StringUtils.isNotEmpty("hello");
// 字符串截取
String sub = StringUtils.substring("hello world", 6); // "world"
String sub2 = StringUtils.substring("hello world", 0, 5); // "hello"
// 格式化 - 支持 {} 占位符
String msg = StringUtils.format("Hello {}, age {}", "Tom", 25);
// 结果: "Hello Tom, age 25"分割与转换
// 字符串转集合
Set<String> set = StringUtils.stringToSet("a,b,c", ",");
List<String> list = StringUtils.stringToList("a, b, c", ",", true, true);
// 分割并转换类型
List<Integer> ids = StringUtils.splitToList("1,2,3", Integer::valueOf);
List<Long> userIds = StringUtils.splitToList("100;200;300", ";", Long::valueOf);命名转换
// 驼峰与下划线转换
String underscore = StringUtils.camelToUnderscore("userName"); // "user_name"
String camel = StringUtils.underscoreToCamelCase("user_name"); // "userName"
String pascal = StringUtils.underscoreToPascalCase("user_name"); // "UserName"模式匹配
// URL匹配
boolean match = StringUtils.isMatch("/api/*", "/api/user"); // true
boolean matchAny = StringUtils.matchesAny("/api/user",
Arrays.asList("/api/*", "/admin/*")); // true
// 检查是否包含任意字符串
boolean contains = StringUtils.containsAnyIgnoreCase("Hello", "hello", "world"); // true字符串映射转换
// 单值映射
Map<String, String> statusMap = Map.of("1", "启用", "0", "禁用");
String result = StringUtils.convertWithMapping("1", statusMap); // "启用"
// 多值映射
String result2 = StringUtils.convertWithMapping("1,0", ",", statusMap); // "启用,禁用"
// 反向映射
String key = StringUtils.convertWithReverseMapping("启用", ",", statusMap); // "1"实用功能
// 左补零
String padded = StringUtils.leftPadWithZero(123, 5); // "00123"
// URL编码/解码
String encoded = StringUtils.urlEncode("中文参数");
String decoded = StringUtils.urlDecode("%E4%B8%AD%E6%96%87");
// 向逗号分隔字符串添加元素(自动去重)
String result = StringUtils.addToCommaString("a,b", "c"); // "a,b,c"2. 日期时间工具 (DateUtils)
提供全面的日期时间处理功能,支持多种格式和转换。
获取当前时间
// 获取当前时间
Date now = DateUtils.getNowDate();
String date = DateUtils.getDate(); // yyyy-MM-dd
String time = DateUtils.getTime(); // yyyy-MM-dd HH:mm:ss
String compact = DateUtils.dateTimeNow(); // yyyyMMddHHmmss
// 路径格式日期
String path = DateUtils.datePath(); // yyyy/MM/dd格式化与解析
// 格式化
String formatted = DateUtils.formatDate(new Date()); // yyyy-MM-dd
String dateTime = DateUtils.formatDateTime(new Date()); // yyyy-MM-dd HH:mm:ss
// 使用枚举格式
String custom = DateUtils.parseDateToStr(DateTimeFormat.DATE_COMPACT, new Date());
// 解析多种格式
Date parsed = DateUtils.parseDate("2023-07-22");
Date parsed2 = DateUtils.parseDate("2023/07/22 15:30:45");时间差计算
Date start = DateUtils.parseDate("2023-07-01");
Date end = DateUtils.parseDate("2023-07-22");
// 计算时间差
long days = DateUtils.difference(start, end, TimeUnit.DAYS); // 21
long hours = DateUtils.difference(start, end, TimeUnit.HOURS);
// 格式化时间差
String diff = DateUtils.getDatePoor(end, start); // "21天 0小时 0分钟"
String detail = DateUtils.getTimeDifference(end, start); // "21天"类型转换
// LocalDateTime 转 Date
Date date = DateUtils.toDate(LocalDateTime.now());
// LocalDate 转 Date
Date date2 = DateUtils.toDate(LocalDate.now());日期范围校验
// 校验日期范围不超过30天
try {
DateUtils.validateDateRange(startDate, endDate, 30, TimeUnit.DAYS);
} catch (ServiceException e) {
// 处理范围超限
}3. Stream流处理工具 (StreamUtils)
简化集合的流式处理操作。
过滤与查找
List<User> users = getUserList();
// 过滤
List<User> activeUsers = StreamUtils.filter(users, user -> "1".equals(user.getStatus()));
// 查找第一个
User firstAdmin = StreamUtils.findFirst(users, user -> "admin".equals(user.getRole()));
// 查找任意一个
Optional<User> anyActive = StreamUtils.findAny(users, user -> "1".equals(user.getStatus()));转换操作
// 转换为List
List<String> names = StreamUtils.toList(users, User::getName);
List<Long> ids = StreamUtils.toList(users, User::getId);
// 转换为Set
Set<String> roleSet = StreamUtils.toSet(users, User::getRole);
// 拼接
String nameStr = StreamUtils.join(users, User::getName); // 逗号分隔
String customJoin = StreamUtils.join(users, User::getName, " | "); // 自定义分隔符映射操作
// 转为Map (key-value相同类型)
Map<Long, User> userMap = StreamUtils.toIdentityMap(users, User::getId);
// 转为Map (key-value不同类型)
Map<Long, String> idNameMap = StreamUtils.toMap(users, User::getId, User::getName);分组操作
// 按单一条件分组
Map<String, List<User>> roleGroups = StreamUtils.groupByKey(users, User::getRole);
// 按两个条件分组
Map<String, Map<String, List<User>>> deptRoleGroups =
StreamUtils.groupBy2Key(users, User::getDeptCode, User::getRole);
// 分组为Map
Map<String, Map<Long, User>> groups =
StreamUtils.group2Map(users, User::getDeptCode, User::getId);排序
// 排序
List<User> sorted = StreamUtils.sorted(users,
Comparator.comparing(User::getCreateTime).reversed());Map合并
Map<String, Integer> map1 = Map.of("a", 1, "b", 2);
Map<String, Integer> map2 = Map.of("b", 3, "c", 4);
// 合并Map
Map<String, Integer> merged = StreamUtils.merge(map1, map2,
(v1, v2) -> (v1 == null ? 0 : v1) + (v2 == null ? 0 : v2));
// 结果: {a=1, b=5, c=4}4. 树形结构构建工具 (TreeBuildUtils)
构建和操作树形数据结构。
构建树形结构
List<Menu> menuList = getMenuList();
// 自动识别根节点构建
List<Tree<Long>> tree = TreeBuildUtils.build(menuList, (menu, treeNode) -> {
treeNode.setId(menu.getId());
treeNode.setParentId(menu.getParentId());
treeNode.setName(menu.getName());
treeNode.putExtra("url", menu.getUrl());
});
// 指定根节点构建
List<Tree<Long>> tree2 = TreeBuildUtils.build(menuList, 0L, (menu, treeNode) -> {
// 节点解析逻辑
});树形操作
// 获取所有叶子节点
List<Tree<Long>> leafNodes = TreeBuildUtils.getLeafNodes(tree);
// 获取所有子节点(包括自身)
List<Tree<Long>> allNodes = TreeBuildUtils.getAllNodes(rootNode);
// 查找指定节点
Tree<Long> node = TreeBuildUtils.findNodeById(tree, 123L);
// 获取树的最大深度
int depth = TreeBuildUtils.getMaxDepth(tree);5. 反射工具 (ReflectUtils)
扩展 HuTool 的反射工具,支持多级属性访问。
多级属性访问
User user = getUser();
// 获取多级属性
String profileName = ReflectUtils.invokeGetter(user, "profile.name");
String companyName = ReflectUtils.invokeGetter(user, "profile.company.name");
// 设置多级属性
ReflectUtils.invokeSetter(user, "profile.name", "张三");
ReflectUtils.invokeSetter(user, "profile.company.name", "ABC公司");安全访问
// 安全获取属性(遇到null不抛异常)
String name = ReflectUtils.getPropertySafely(user, "profile.name");
// 检查是否具有属性路径
boolean hasProperty = ReflectUtils.hasProperty(user, "profile.name");6. 对象工具 (ObjectUtils)
扩展 HuTool 的对象工具,提供安全的对象访问。
安全属性访问
User user = getUser(); // 可能为null
// 安全获取属性
String name = ObjectUtils.getIfNotNull(user, User::getName);
// 带默认值的安全获取
String name2 = ObjectUtils.getIfNotNull(user, User::getName, "未知用户");7. Spring上下文工具 (SpringUtils)
访问Spring容器和Bean管理。
Bean操作
// 获取Bean
UserService userService = SpringUtils.getBean(UserService.class);
UserService userService2 = SpringUtils.getBean("userService");
// 检查Bean是否存在
boolean exists = SpringUtils.containsBean("userService");
// 检查是否单例
boolean singleton = SpringUtils.isSingleton("userService");
// 获取Bean类型
Class<?> type = SpringUtils.getType("userService");
// 获取别名
String[] aliases = SpringUtils.getAliases("userService");AOP代理
// 获取AOP代理对象
UserService proxy = SpringUtils.getAopProxy(userServiceImpl);环境信息
// 获取应用上下文
ApplicationContext context = SpringUtils.context();
// 判断是否虚拟线程环境
boolean isVirtual = SpringUtils.isVirtual();8. Servlet工具 (ServletUtils)
处理HTTP请求响应的实用工具。
参数获取
// 获取请求参数
String userName = ServletUtils.getParameter("userName");
String userName2 = ServletUtils.getParameter("userName", "默认值");
// 类型转换
Integer pageNum = ServletUtils.getParameterToInt("pageNum");
Integer pageSize = ServletUtils.getParameterToInt("pageSize", 10);
Boolean active = ServletUtils.getParameterToBool("active");
// 获取所有参数
Map<String, String[]> allParams = ServletUtils.getParams(request);
Map<String, String> paramMap = ServletUtils.getParamMap(request);请求响应对象
// 获取当前请求响应对象
HttpServletRequest request = ServletUtils.getRequest();
HttpServletResponse response = ServletUtils.getResponse();
HttpSession session = ServletUtils.getSession();请求头处理
// 获取请求头
String userAgent = ServletUtils.getHeader(request, "User-Agent");
Map<String, String> headers = ServletUtils.getHeaders(request);响应处理
// 渲染JSON响应
ServletUtils.renderString(response, "{\"success\": true}");
// 判断Ajax请求
boolean isAjax = ServletUtils.isAjaxRequest(request);客户端信息
// 获取客户端IP
String clientIP = ServletUtils.getClientIP();
// URL编码解码
String encoded = ServletUtils.urlEncode("中文");
String decoded = ServletUtils.urlDecode("%E4%B8%AD%E6%96%87");9. 线程工具 (ThreadUtils)
线程池管理和异常处理。
线程池优雅关闭
ExecutorService executor = Executors.newFixedThreadPool(10);
// 优雅关闭(默认120秒超时)
ThreadUtils.shutdownGracefully(executor);
// 自定义超时时间
ThreadUtils.shutdownGracefully(executor, 60);异常处理
// 在ThreadPoolExecutor的afterExecute中使用
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
ThreadUtils.logException(r, t);
}线程休眠
// 安全休眠(自动处理中断异常)
ThreadUtils.sleep(1000); // 1秒
ThreadUtils.sleepSeconds(5); // 5秒
// 获取线程信息
String threadInfo = ThreadUtils.getCurrentThreadInfo();10. 文件工具 (FileUtils & FileTypeUtils)
文件处理和类型判断。
文件下载响应头
@GetMapping("/download")
public void download(HttpServletResponse response) {
String fileName = "用户数据.xlsx";
// 设置下载响应头(自动处理文件名编码)
FileUtils.setAttachmentResponseHeader(response, fileName);
// 文件内容写入响应流
// ...
}文件类型判断
// 按扩展名判断
boolean isImage = FileTypeUtils.isImage("jpg");
boolean isDocument = FileTypeUtils.isDocument("pdf");
boolean isVideo = FileTypeUtils.isVideo("mp4");
// 按File对象判断
File file = new File("demo.png");
boolean isImage2 = FileTypeUtils.isImage(file);
String fileType = FileTypeUtils.getFileType(file); // "图片"
// 检查是否允许的类型
boolean allowed = FileTypeUtils.isAllowed("exe"); // falseURL编码工具
// 百分号编码
String encoded = FileUtils.percentEncode("文件名.txt");11. IP地址工具 (AddressUtils & RegionUtils)
IP地址解析和地理位置定位。
IP地址解析
// 获取IP地理位置
String location = AddressUtils.getRealAddressByIp("8.8.8.8");
// 可能返回: "美国|北美|美国|美国|谷歌"
String location2 = AddressUtils.getRealAddressByIp("192.168.1.1");
// 返回: "内网IP"
String location3 = AddressUtils.getRealAddressByIp("invalid-ip");
// 返回: "XX XX"离线IP定位
// 使用ip2region库进行离线定位
String cityInfo = RegionUtils.getCityInfo("202.108.22.5");
// 返回格式: "中国|华北|北京市|北京市|联通"网络工具
// IP类型判断
boolean isIPv4 = NetUtils.isIpv4("192.168.1.1"); // true
boolean isIPv6 = NetUtils.isIpv6("2001:db8::1"); // true
// 内网判断
boolean isInner = NetUtils.isInnerIP("192.168.1.1"); // true
boolean isInnerV6 = NetUtils.isInnerIpv6("fe80::1"); // true12. 正则工具 (Regex Utils)
正则表达式相关工具。
预定义模式
// 使用预编译模式池
Pattern i18nPattern = RegexPatternPool.I18N_KEY;
Pattern accountPattern = RegexPatternPool.ACCOUNT;
Pattern statusPattern = RegexPatternPool.BINARY_STATUS;校验器
// 国际化键格式校验
boolean valid = RegexValidator.isValidI18nKey("user.profile.name"); // true
// 账号格式校验
boolean validAccount = RegexValidator.isValidAccount("admin123"); // true
// 状态值校验
boolean validStatus = RegexValidator.isValidStatus("1"); // true
// 字典类型校验
boolean validDict = RegexValidator.isValidDictType("sys_user_sex"); // true提取工具
// 从字符串中提取匹配部分
String result = RegexUtils.extractFromString(
"用户ID: 12345", "(\\d+)", "未找到");
// 返回: "12345"13. SQL安全工具 (SqlUtil)
防止SQL注入的安全工具。
ORDER BY安全校验
// 校验排序参数
String orderBy = "name asc, create_time desc";
String safeOrderBy = SqlUtil.escapeOrderBySql(orderBy);
// 检查是否安全
boolean isSafe = SqlUtil.isValidOrderBySql("name asc"); // true
boolean isUnsafe = SqlUtil.isValidOrderBySql("name; drop table"); // falseSQL关键字过滤
try {
SqlUtil.filterKeyword("select * from user");
// 抛出异常: 参数存在SQL注入风险
} catch (IllegalArgumentException e) {
// 处理SQL注入风险
}14. 参数校验工具 (ValidatorUtils)
手动触发Bean Validation校验。
基本校验
User user = new User();
user.setName(""); // 违反@NotBlank约束
try {
// 校验对象(抛出异常)
ValidatorUtils.validate(user);
} catch (ConstraintViolationException e) {
// 处理校验失败
System.out.println(e.getMessage());
}
// 校验并返回结果(不抛异常)
Set<ConstraintViolation<User>> violations = ValidatorUtils.validateAndReturn(user);分组校验
// 使用校验分组
ValidatorUtils.validate(user, AddGroup.class);
ValidatorUtils.validate(user, EditGroup.class);属性校验
// 校验单个属性
ValidatorUtils.validateProperty(user, "email");
// 校验属性值
ValidatorUtils.validateValue(User.class, "email", "invalid-email");便捷方法
// 检查是否通过校验
boolean isValid = ValidatorUtils.isValid(user);
// 获取错误信息
String errors = ValidatorUtils.getValidationErrors(user);
// 返回: "name: 不能为空; email: 邮箱格式错误"15. 对象映射工具 (MapstructUtils)
基于 MapStruct Plus 的对象映射工具。
基本映射
// 对象转换
UserVo userVo = MapstructUtils.convert(user, UserVo.class);
// 列表转换
List<UserVo> userVos = MapstructUtils.convert(userList, UserVo.class);
// 属性赋值
UserVo vo = new UserVo();
MapstructUtils.convert(user, vo); // 将user属性赋值给voMap映射
// Map转对象
Map<String, Object> map = Map.of("name", "张三", "age", 25);
User user = MapstructUtils.convert(map, User.class);16. 国际化消息工具 (MessageUtils)
国际化消息处理。
消息获取
// 获取国际化消息
String message = MessageUtils.message("user.not.found");
// 带参数的消息
String message2 = MessageUtils.message("user.login.success", "张三");
// 消息不存在时返回键本身
String message3 = MessageUtils.message("nonexistent.key"); // 返回: "nonexistent.key"使用最佳实践
1. 性能优化
大数据量处理
// 使用Stream工具处理大量数据时,注意内存使用
List<UserVo> vos = StreamUtils.toList(users, user -> {
// 避免在转换函数中进行复杂操作
return MapstructUtils.convert(user, UserVo.class);
});
// 分批处理大数据量
List<List<User>> batches = Lists.partition(users, 1000);
for (List<User> batch : batches) {
// 分批处理
}缓存利用
// 正则模式使用预编译版本
Pattern pattern = RegexPatternPool.I18N_KEY; // 而不是 Pattern.compile()
// 反射操作缓存Method对象
// ReflectUtils内部已实现缓存2. 异常处理
安全的工具方法使用
// 使用安全版本的方法
String name = ObjectUtils.getIfNotNull(user, User::getName, "未知");
String profileName = ReflectUtils.getPropertySafely(user, "profile.name");
// 或者使用try-catch
try {
String name = ReflectUtils.invokeGetter(user, "profile.name");
} catch (Exception e) {
log.warn("获取用户档案名称失败", e);
// 设置默认值或其他处理
}3. 字符串处理优化
大量字符串拼接
// 使用Stream工具进行拼接
String result = StreamUtils.join(users, User::getName, ",");
// 而不是循环拼接
StringBuilder sb = new StringBuilder();
for (User user : users) {
if (sb.length() > 0) sb.append(",");
sb.append(user.getName());
}字符串映射批量转换
// 批量转换
List<String> statusLabels = StreamUtils.toList(statusCodes,
code -> StringUtils.convertWithMapping(code, statusMap));4. 日期时间处理
时区注意事项
// 使用系统默认时区的转换
Date date = DateUtils.toDate(LocalDateTime.now());
// 如果需要特定时区,使用原生API
ZonedDateTime zdt = LocalDateTime.now().atZone(ZoneId.of("Asia/Shanghai"));
Date date2 = Date.from(zdt.toInstant());5. 线程安全
工具类都是线程安全的
// 所有工具类方法都是静态的,线程安全
// 可以在多线程环境中安全使用
CompletableFuture.supplyAsync(() -> {
return StringUtils.format("用户{}, 时间{}", userName, DateUtils.getTime());
});扩展指南
1. 自定义工具类
// 遵循相同的设计模式
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class CustomUtils {
// 静态方法
public static String customMethod(String input) {
// 实现逻辑
return result;
}
// 重载方法提供便利
public static String customMethod(String input, String defaultValue) {
return StringUtils.isNotBlank(input) ? customMethod(input) : defaultValue;
}
}2. 扩展现有工具
// 通过继承扩展(如果需要)
public class EnhancedStringUtils extends StringUtils {
public static String customFormat(String template, Object... args) {
// 自定义格式化逻辑
return format(template, args);
}
}工具类组合使用
复杂业务场景示例
用户导出场景
/**
* 用户导出服务
*/
@Service
@RequiredArgsConstructor
public class UserExportService {
private final UserMapper userMapper;
/**
* 导出用户数据
*/
public void exportUsers(HttpServletResponse response, UserQueryBo query) {
// 1. 参数校验
ValidatorUtils.validate(query);
// 2. 查询数据
List<User> users = userMapper.selectList(query);
// 3. 数据转换 - 使用StreamUtils和MapstructUtils
List<UserExportVo> exportData = StreamUtils.toList(users, user -> {
UserExportVo vo = MapstructUtils.convert(user, UserExportVo.class);
// 状态值转标签
Map<String, String> statusMap = Map.of("1", "启用", "0", "禁用");
vo.setStatusLabel(StringUtils.convertWithMapping(user.getStatus(), statusMap));
// 日期格式化
vo.setCreateTimeStr(DateUtils.formatDateTime(user.getCreateTime()));
// IP地址解析
vo.setLoginLocation(AddressUtils.getRealAddressByIp(user.getLoginIp()));
return vo;
});
// 4. 按部门分组统计
Map<String, List<UserExportVo>> deptGroups = StreamUtils.groupByKey(exportData, UserExportVo::getDeptName);
// 5. 设置下载响应头
String fileName = StringUtils.format("用户数据_{}.xlsx", DateUtils.dateTimeNow());
FileUtils.setAttachmentResponseHeader(response, fileName);
// 6. 导出Excel
// ExcelUtils.exportExcel(exportData, UserExportVo.class, response);
}
}数据同步场景
/**
* 数据同步服务
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class DataSyncService {
private final SyncMapper syncMapper;
/**
* 同步外部数据
*/
@Transactional
public SyncResult syncExternalData(List<ExternalData> externalList) {
SyncResult result = new SyncResult();
// 1. 数据校验和过滤
List<ExternalData> validData = StreamUtils.filter(externalList, data -> {
// 校验必填字段
if (StringUtils.isEmpty(data.getCode())) {
return false;
}
// 校验格式
if (!RegexValidator.isValidAccount(data.getCode())) {
return false;
}
return true;
});
// 2. 查询已存在的数据
Set<String> existingCodes = StreamUtils.toSet(
syncMapper.selectExistingCodes(),
String::valueOf
);
// 3. 分类处理 - 新增和更新
List<ExternalData> toInsert = StreamUtils.filter(validData,
data -> !existingCodes.contains(data.getCode()));
List<ExternalData> toUpdate = StreamUtils.filter(validData,
data -> existingCodes.contains(data.getCode()));
// 4. 转换为实体
List<SyncEntity> insertEntities = StreamUtils.toList(toInsert, data -> {
SyncEntity entity = MapstructUtils.convert(data, SyncEntity.class);
entity.setCreateTime(DateUtils.getNowDate());
return entity;
});
List<SyncEntity> updateEntities = StreamUtils.toList(toUpdate, data -> {
SyncEntity entity = MapstructUtils.convert(data, SyncEntity.class);
entity.setUpdateTime(DateUtils.getNowDate());
return entity;
});
// 5. 批量操作
if (CollUtil.isNotEmpty(insertEntities)) {
syncMapper.insertBatch(insertEntities);
result.setInsertCount(insertEntities.size());
}
if (CollUtil.isNotEmpty(updateEntities)) {
syncMapper.updateBatch(updateEntities);
result.setUpdateCount(updateEntities.size());
}
// 6. 记录日志
log.info("数据同步完成: 新增={}, 更新={}, 跳过={}",
result.getInsertCount(),
result.getUpdateCount(),
externalList.size() - validData.size());
return result;
}
}树形菜单构建场景
/**
* 菜单服务
*/
@Service
@RequiredArgsConstructor
public class MenuService {
private final MenuMapper menuMapper;
/**
* 构建用户菜单树
*/
public List<RouterVo> buildUserMenuTree(Long userId) {
// 1. 查询用户菜单
List<Menu> menus = menuMapper.selectMenusByUserId(userId);
// 2. 过滤并排序
List<Menu> visibleMenus = StreamUtils.filter(menus,
menu -> "0".equals(menu.getVisible()));
List<Menu> sortedMenus = StreamUtils.sorted(visibleMenus,
Comparator.comparingInt(Menu::getOrderNum));
// 3. 构建树形结构
List<Tree<Long>> menuTree = TreeBuildUtils.build(sortedMenus, (menu, node) -> {
node.setId(menu.getId());
node.setParentId(menu.getParentId());
node.setName(menu.getName());
node.putExtra("path", menu.getPath());
node.putExtra("component", menu.getComponent());
node.putExtra("icon", menu.getIcon());
node.putExtra("menuType", menu.getMenuType());
node.putExtra("perms", menu.getPerms());
});
// 4. 转换为路由格式
return convertToRouters(menuTree);
}
/**
* 转换为路由对象
*/
private List<RouterVo> convertToRouters(List<Tree<Long>> trees) {
return StreamUtils.toList(trees, tree -> {
RouterVo router = new RouterVo();
router.setName(tree.getName().toString());
router.setPath(ObjectUtils.getIfNotNull(tree.get("path"), Object::toString, ""));
router.setComponent(ObjectUtils.getIfNotNull(tree.get("component"), Object::toString, "Layout"));
// 递归处理子菜单
if (CollUtil.isNotEmpty(tree.getChildren())) {
router.setChildren(convertToRouters(tree.getChildren()));
}
return router;
});
}
}工具链式调用
/**
* 工具链式使用示例
*/
public class ToolChainExample {
/**
* 处理用户输入并格式化输出
*/
public String processUserInput(String input) {
// 链式处理
return Optional.ofNullable(input)
.filter(StringUtils::isNotBlank)
.map(String::trim)
.map(str -> StringUtils.urlDecode(str))
.map(str -> SqlUtil.escapeOrderBySql(str))
.orElse("");
}
/**
* 批量处理并收集结果
*/
public Map<String, Object> batchProcess(List<String> inputs) {
// 过滤有效输入
List<String> validInputs = StreamUtils.filter(inputs, StringUtils::isNotBlank);
// 转换并去重
Set<String> uniqueValues = StreamUtils.toSet(validInputs, String::toLowerCase);
// 分组统计
Map<Integer, List<String>> lengthGroups = StreamUtils.groupByKey(validInputs,
String::length);
// 拼接结果
String joined = StreamUtils.join(validInputs, ",");
return Map.of(
"count", validInputs.size(),
"unique", uniqueValues.size(),
"groups", lengthGroups,
"joined", joined
);
}
}异步处理工具
CompletableFuture 组合使用
/**
* 异步数据聚合服务
*/
@Service
@RequiredArgsConstructor
public class AsyncAggregationService {
private final UserService userService;
private final OrderService orderService;
private final MessageService messageService;
@Async
public CompletableFuture<UserDashboardVo> getUserDashboard(Long userId) {
// 并行获取多个数据源
CompletableFuture<User> userFuture = CompletableFuture.supplyAsync(() ->
userService.getById(userId));
CompletableFuture<List<Order>> ordersFuture = CompletableFuture.supplyAsync(() ->
orderService.getRecentOrders(userId, 10));
CompletableFuture<Integer> unreadCountFuture = CompletableFuture.supplyAsync(() ->
messageService.getUnreadCount(userId));
// 等待所有完成并组装结果
return CompletableFuture.allOf(userFuture, ordersFuture, unreadCountFuture)
.thenApply(v -> {
UserDashboardVo dashboard = new UserDashboardVo();
// 使用工具类安全获取数据
User user = userFuture.join();
dashboard.setUserName(ObjectUtils.getIfNotNull(user, User::getName, "未知用户"));
dashboard.setLastLoginTime(DateUtils.formatDateTime(user.getLastLoginTime()));
dashboard.setLoginLocation(AddressUtils.getRealAddressByIp(user.getLoginIp()));
// 订单统计
List<Order> orders = ordersFuture.join();
dashboard.setRecentOrders(StreamUtils.toList(orders,
order -> MapstructUtils.convert(order, OrderVo.class)));
// 消息数量
dashboard.setUnreadMessageCount(unreadCountFuture.join());
return dashboard;
});
}
}批量异步处理
/**
* 批量异步处理工具
*/
public class BatchAsyncUtils {
private static final ExecutorService EXECUTOR = Executors.newFixedThreadPool(
Runtime.getRuntime().availableProcessors() * 2);
/**
* 批量异步处理
*/
public static <T, R> List<R> batchProcess(
List<T> items,
Function<T, R> processor,
int batchSize) {
// 分批
List<List<T>> batches = Lists.partition(items, batchSize);
// 异步处理每批
List<CompletableFuture<List<R>>> futures = StreamUtils.toList(batches, batch ->
CompletableFuture.supplyAsync(() ->
StreamUtils.toList(batch, processor), EXECUTOR));
// 等待所有完成并合并结果
return futures.stream()
.map(CompletableFuture::join)
.flatMap(List::stream)
.collect(Collectors.toList());
}
/**
* 优雅关闭
*/
public static void shutdown() {
ThreadUtils.shutdownGracefully((ExecutorService) EXECUTOR, 60);
}
}JSON处理工具
JsonUtils 使用示例
/**
* JSON工具使用示例
*/
public class JsonUtilsExample {
/**
* 对象序列化
*/
public void serializationExample() {
User user = new User();
user.setId(1L);
user.setName("张三");
user.setCreateTime(LocalDateTime.now());
// 对象转JSON字符串
String json = JsonUtils.toJsonString(user);
// 对象转字节数组
byte[] bytes = JsonUtils.toJsonBytes(user);
// 格式化输出
String prettyJson = JsonUtils.toPrettyJsonString(user);
}
/**
* 反序列化
*/
public void deserializationExample() {
String json = "{\"id\":1,\"name\":\"张三\"}";
// JSON转对象
User user = JsonUtils.parseObject(json, User.class);
// JSON转List
String listJson = "[{\"id\":1},{\"id\":2}]";
List<User> users = JsonUtils.parseArray(listJson, User.class);
// JSON转Map
Map<String, Object> map = JsonUtils.parseMap(json);
// JSON转指定类型Map
Map<String, User> userMap = JsonUtils.parseMap(json, String.class, User.class);
}
/**
* 复杂类型处理
*/
public void complexTypeExample() {
String json = "{\"data\":{\"users\":[{\"id\":1}]}}";
// 使用TypeReference处理复杂类型
Map<String, Map<String, List<User>>> result = JsonUtils.parseObject(json,
new TypeReference<Map<String, Map<String, List<User>>>>() {});
// 获取嵌套数据
List<User> users = result.get("data").get("users");
}
/**
* 空值处理
*/
public void nullHandlingExample() {
// 安全解析(返回null而不是抛异常)
User user = JsonUtils.parseObjectQuietly("{invalid json}", User.class);
// 带默认值解析
User defaultUser = new User();
User result = Optional.ofNullable(JsonUtils.parseObjectQuietly("{}", User.class))
.orElse(defaultUser);
}
}加密工具
常用加密操作
/**
* 加密工具使用示例
*/
public class EncryptUtilsExample {
/**
* MD5加密
*/
public String md5Example(String input) {
// 基础MD5
String md5 = SecureUtil.md5(input);
// 带盐MD5
String saltedMd5 = SecureUtil.md5(input + "salt");
return md5;
}
/**
* AES加密解密
*/
public void aesExample() {
String key = "1234567890123456"; // 16位密钥
String content = "敏感数据";
// 加密
AES aes = SecureUtil.aes(key.getBytes());
String encrypted = aes.encryptBase64(content);
// 解密
String decrypted = aes.decryptStr(encrypted);
}
/**
* RSA加密解密
*/
public void rsaExample() {
// 生成密钥对
KeyPair keyPair = SecureUtil.generateKeyPair("RSA");
String publicKey = Base64.encode(keyPair.getPublic().getEncoded());
String privateKey = Base64.encode(keyPair.getPrivate().getEncoded());
// 公钥加密
RSA rsa = SecureUtil.rsa(privateKey, publicKey);
String encrypted = rsa.encryptBase64("敏感数据", KeyType.PublicKey);
// 私钥解密
String decrypted = rsa.decryptStr(encrypted, KeyType.PrivateKey);
}
/**
* 签名验证
*/
public void signExample() {
String privateKey = "...";
String publicKey = "...";
String data = "需要签名的数据";
// 签名
Sign sign = SecureUtil.sign(SignAlgorithm.SHA256withRSA, privateKey, publicKey);
String signature = sign.signHex(data);
// 验签
boolean verified = sign.verify(data.getBytes(), HexUtil.decodeHex(signature));
}
}性能优化指南
工具类性能对比
/**
* 性能测试示例
*/
public class PerformanceTest {
/**
* 字符串格式化性能对比
*/
@Test
public void stringFormatPerformance() {
int iterations = 100000;
String name = "张三";
int age = 25;
// String.format - 较慢
long start1 = System.currentTimeMillis();
for (int i = 0; i < iterations; i++) {
String result = String.format("姓名: %s, 年龄: %d", name, age);
}
long time1 = System.currentTimeMillis() - start1;
// StringUtils.format - 较快
long start2 = System.currentTimeMillis();
for (int i = 0; i < iterations; i++) {
String result = StringUtils.format("姓名: {}, 年龄: {}", name, age);
}
long time2 = System.currentTimeMillis() - start2;
// StringBuilder - 最快
long start3 = System.currentTimeMillis();
for (int i = 0; i < iterations; i++) {
String result = new StringBuilder()
.append("姓名: ").append(name)
.append(", 年龄: ").append(age)
.toString();
}
long time3 = System.currentTimeMillis() - start3;
System.out.println("String.format: " + time1 + "ms");
System.out.println("StringUtils.format: " + time2 + "ms");
System.out.println("StringBuilder: " + time3 + "ms");
}
/**
* 集合转换性能对比
*/
@Test
public void collectionConvertPerformance() {
List<User> users = generateUsers(10000);
// for循环
long start1 = System.currentTimeMillis();
List<String> names1 = new ArrayList<>();
for (User user : users) {
names1.add(user.getName());
}
long time1 = System.currentTimeMillis() - start1;
// StreamUtils
long start2 = System.currentTimeMillis();
List<String> names2 = StreamUtils.toList(users, User::getName);
long time2 = System.currentTimeMillis() - start2;
// 并行流
long start3 = System.currentTimeMillis();
List<String> names3 = users.parallelStream()
.map(User::getName)
.collect(Collectors.toList());
long time3 = System.currentTimeMillis() - start3;
System.out.println("for循环: " + time1 + "ms");
System.out.println("StreamUtils: " + time2 + "ms");
System.out.println("并行流: " + time3 + "ms");
}
}内存优化建议
/**
* 内存优化示例
*/
public class MemoryOptimization {
/**
* 避免创建不必要的中间集合
*/
public long countActiveUsers(List<User> users) {
// 不推荐 - 创建了中间List
List<User> activeUsers = StreamUtils.filter(users,
user -> "1".equals(user.getStatus()));
return activeUsers.size();
// 推荐 - 直接计数
return users.stream()
.filter(user -> "1".equals(user.getStatus()))
.count();
}
/**
* 大数据量分批处理
*/
public void processBigData(List<String> bigList) {
int batchSize = 1000;
List<List<String>> batches = Lists.partition(bigList, batchSize);
for (List<String> batch : batches) {
// 处理每批数据
processBatch(batch);
// 及时释放引用,帮助GC
batch = null;
}
}
/**
* 使用原生类型避免装箱
*/
public int sumAges(List<User> users) {
// 不推荐 - 装箱开销
return users.stream()
.map(User::getAge)
.reduce(0, Integer::sum);
// 推荐 - 使用mapToInt避免装箱
return users.stream()
.mapToInt(User::getAge)
.sum();
}
}单元测试示例
工具类测试
/**
* StringUtils测试
*/
class StringUtilsTest {
@Test
void testFormat() {
String result = StringUtils.format("Hello {}, age {}", "Tom", 25);
assertEquals("Hello Tom, age 25", result);
}
@Test
void testFormat_WithNull() {
String result = StringUtils.format("Value: {}", (Object) null);
assertEquals("Value: null", result);
}
@Test
void testBlankToDefault() {
assertEquals("default", StringUtils.blankToDefault("", "default"));
assertEquals("default", StringUtils.blankToDefault(null, "default"));
assertEquals("value", StringUtils.blankToDefault("value", "default"));
}
@Test
void testCamelToUnderscore() {
assertEquals("user_name", StringUtils.camelToUnderscore("userName"));
assertEquals("user_id", StringUtils.camelToUnderscore("userId"));
assertEquals("create_time", StringUtils.camelToUnderscore("createTime"));
}
@Test
void testStringToSet() {
Set<String> set = StringUtils.stringToSet("a,b,c", ",");
assertEquals(3, set.size());
assertTrue(set.contains("a"));
assertTrue(set.contains("b"));
assertTrue(set.contains("c"));
}
}
/**
* DateUtils测试
*/
class DateUtilsTest {
@Test
void testFormatDate() {
Date date = DateUtils.parseDate("2023-07-22");
String formatted = DateUtils.formatDate(date);
assertEquals("2023-07-22", formatted);
}
@Test
void testDifference() {
Date start = DateUtils.parseDate("2023-07-01");
Date end = DateUtils.parseDate("2023-07-22");
long days = DateUtils.difference(start, end, TimeUnit.DAYS);
assertEquals(21, days);
}
@Test
void testValidateDateRange() {
Date start = DateUtils.parseDate("2023-07-01");
Date end = DateUtils.parseDate("2023-08-15");
// 超过30天应抛出异常
assertThrows(ServiceException.class, () ->
DateUtils.validateDateRange(start, end, 30, TimeUnit.DAYS));
}
}
/**
* StreamUtils测试
*/
class StreamUtilsTest {
private List<User> users;
@BeforeEach
void setUp() {
users = List.of(
new User(1L, "张三", "admin"),
new User(2L, "李四", "user"),
new User(3L, "王五", "admin")
);
}
@Test
void testFilter() {
List<User> admins = StreamUtils.filter(users,
user -> "admin".equals(user.getRole()));
assertEquals(2, admins.size());
}
@Test
void testToList() {
List<String> names = StreamUtils.toList(users, User::getName);
assertEquals(List.of("张三", "李四", "王五"), names);
}
@Test
void testGroupByKey() {
Map<String, List<User>> groups = StreamUtils.groupByKey(users, User::getRole);
assertEquals(2, groups.size());
assertEquals(2, groups.get("admin").size());
assertEquals(1, groups.get("user").size());
}
@Test
void testJoin() {
String result = StreamUtils.join(users, User::getName, ",");
assertEquals("张三,李四,王五", result);
}
}注意事项
1. 空值处理
// 始终检查输入参数
public void processData(String input) {
// 推荐:使用工具类的空值安全方法
String value = StringUtils.blankToDefault(input, "");
// 或者显式检查
if (StringUtils.isBlank(input)) {
return;
}
}2. 线程安全
// 所有工具类都是线程安全的
// 可以在多线程环境中安全使用
ExecutorService executor = Executors.newFixedThreadPool(10);
executor.submit(() -> {
String result = StringUtils.format("Thread: {}", Thread.currentThread().getName());
log.info(result);
});3. 异常处理
// 使用安全版本的方法避免异常
String value = ReflectUtils.getPropertySafely(obj, "nested.property");
// 或者使用try-catch
try {
Object value = ReflectUtils.invokeGetter(obj, "property");
} catch (Exception e) {
log.warn("属性获取失败", e);
}4. 资源释放
// 使用完毕后释放资源
try {
// 使用工具类处理
} finally {
ThreadUtils.shutdownGracefully(executor);
}5. 日志记录
// 在关键操作处记录日志
log.debug("开始处理数据,数量: {}", data.size());
List<Result> results = StreamUtils.toList(data, this::process);
log.info("处理完成,成功: {}", results.size());常见问题
Q: 为什么某些工具方法返回不可变集合?
A: 为了防止意外修改,某些场景下返回不可变集合。如需修改,请创建新的可变集合。
Q: 如何处理大数据量的Stream操作?
A: 考虑使用并行流、分批处理或者调整JVM堆内存设置。
Q: 正则表达式性能如何优化?
A: 使用 RegexPatternPool 中的预编译模式,避免重复编译。
Q: 反射操作的性能影响?
A: ReflectUtils 内部有缓存机制,但仍建议在性能敏感场景中谨慎使用。
Q: 如何添加新的文件类型支持?
A: 修改 FileTypeUtils 中的扩展名集合常量,或者使用继承的方式扩展。
Q: StringUtils.format 和 String.format 有什么区别?
A: StringUtils.format 使用 {} 作为占位符,性能更好;String.format 使用 %s 等格式符,功能更丰富但性能较低。
Q: StreamUtils 和原生 Stream API 应该如何选择?
A: 对于简单操作推荐使用 StreamUtils,它提供了更简洁的API;对于复杂的链式操作,可以直接使用原生 Stream API。
Q: 工具类是否支持扩展?
A: 是的,可以通过继承或组合的方式扩展现有工具类,但建议保持一致的设计风格。
