Toolkit
受众:开发者 摘要:spring-open-starter-toolkit 纯 Java 工具集。不依赖 Spring,可在任何 JVM 项目复用。
Toolkit 是框架 starter 体系下的纯 Java 工具模块,可以脱离 Spring Boot、Spring Framework 和数据库独立使用。只有这类不依赖任何 Spring / Spring Boot 相关 Maven 包的独立纯 Java 模块,才允许声明和维护 JDK 1.8 兼容;是否兼容以模块 pom.xml 的编译 release / source / target 和实际验证为准。
<dependency>
<groupId>com.springopen</groupId>
<artifactId>spring-open-starter-toolkit</artifactId>
<version>${spring-open.version}</version>
</dependency>Toolkit 使用 spring-open-starter-toolkit 坐标进入 starter 聚合,但它不是自动配置型 starter:不提供 auto-configuration,不读取配置,不依赖 Spring。应用、starter 或普通 Java 项目都可以直接依赖它。
设计原则
- 不依赖 Spring Boot。
- 不依赖 Spring Framework。
- 不读取配置。
- 不做国际化。
- 不承担业务异常语义。
- 优先复用 JDK 和 Apache Commons 等成熟生态。
- 每个语义只保留一个规范方法名,不维护同义别名。
当前底层依赖:
| 依赖 | 用途 |
|---|---|
| Apache Commons Lang | 字符串处理 |
| Apache Commons Collections | 集合处理 |
字符串
StringUtils.hasText("hello");
StringUtils.isBlank(" ");
StringUtils.trimToNull(" ");
StringUtils.trimToEmpty(null);
StringUtils.defaultIfBlank(value, "default");
StringUtils.equalsIgnoreCase(left, right);
StringUtils.startsWith(path, "/api/");
StringUtils.endsWith(filename, ".json");
StringUtils.splitByComma("a,b,c");
StringUtils.split("a|b|c", "|");
StringUtils.kebabCase("accessKeyId"); // access-key-id说明:
hasText(正向)/isBlank(负向)是仅有的两个判断入口,不再提供blank/filled/isNotBlank等别名。splitByComma会自动去掉空白项。kebabCase用于把 Java 属性名转换成配置字段名,例如sdkAppId -> sdk-app-id、URLValue -> url-value、request_url -> request-url。
JavaBean Lambda 属性名
LambdaProperty 用于从标准 JavaBean getter lambda 解析属性名,并同时得到 kebab-case 名称。它主要服务配置解析、白名单字段声明、表格列、导出字段、日志字段和审计字段等场景,避免重复手写 "product-id"、"access-key-id" 这类字符串。
class SmsProvider {
String getProductId() {
return "";
}
String getAccessKeyId() {
return "";
}
boolean isEnabled() {
return true;
}
}
LambdaProperty.kebabName(SmsProvider::getProductId); // product-id
LambdaProperty.kebabName(SmsProvider::getAccessKeyId); // access-key-id
LambdaProperty.kebabName(SmsProvider::isEnabled); // enabled配置解析建议:
String prefix = "spring.open.sms.providers.tencent";
String key = prefix + "." + LambdaProperty.kebabName(SmsProvider::getProductId);约束:
- lambda 必须引用标准 JavaBean getter:
getXxx或isXxx。 - 不支持 fluent getter、普通方法和复杂 lambda 表达式。
- 该工具只解析 kebab-case 名称,不解析数据库列名。
- MyBatis-Plus 实体字段和数据库列名请使用 data-mybatis-plus 的
LambdaField/FieldName能力,详见 数据库。
计数
CountUtils 用于处理业务里最常见的非负计数,例如浏览数、点赞数、收藏数、评论数和调用次数。它只处理数值边界,不绑定任何业务语义。
CountUtils.nonNegative(null); // 0
CountUtils.nonNegative(-1L); // 0
CountUtils.changeNonNegative(1L, 2); // 3
CountUtils.changeNonNegative(1L, -5); // 0适合:
- 计数展示兜底。
- 增减计数时避免出现负数。
- Blog / Forum / Mall 等模块复用同一套非负计数规则。
集合
CollectionUtils.isEmpty(list);
CollectionUtils.isNotEmpty(list);
CollectionUtils.isEmpty(map);
CollectionUtils.isNotEmpty(map);
CollectionUtils.emptyIfNull(collection);集合工具优先委托 Apache Commons Collections,不重复实现成熟能力。
参数校验
AssertUtils.notNull(value, "value");
AssertUtils.hasText(name, "name");
AssertUtils.notEmpty(ids, "ids");
AssertUtils.positive(timeout, "timeout");
AssertUtils.nonNegative(count, "count");
AssertUtils.isTrue(enabled, "feature must be enabled");Toolkit 的断言只抛出 Java 标准异常:
IllegalArgumentExceptionIllegalStateException
如果异常消息需要多语言,应在 Web、Service 或业务异常层处理,不要把 i18n 放进 Toolkit。
参数名直接在调用点写字符串字面量;业务参数名属于业务语义,不沉淀到 Toolkit 常量。
ID
IdUtils.uuid32();uuid32 返回去掉 - 的 UUID 字符串,适合 traceId、消息 ID、临时 token 等场景。
Hash
HashUtils.sha256Hex("spring-open");
HashUtils.sha256Hex(bytes);
HashUtils.sha256Base64UrlNoPadding(bytes);
HashUtils.hmacSha256Hex(payload, secret);
HashUtils.secureEquals(expected, actual);说明:
sha256Hex(String)要求文本不能为空。sha256Hex(byte[])支持空数组和null,适合请求体 hash。sha256Base64UrlNoPadding(byte[])适合 OAuth PKCE 这类 URL 安全摘要。hmacSha256Hex适合开放平台签名、Webhook 签名等场景。secureEquals使用常量时间比较,适合签名和摘要比对。
HashUtils 使用 JDK 17 HexFormat,符合项目 Java 17+ 基线。
常见用法
配置 key 后缀
String enabledKey = prefix + "." + LambdaProperty.kebabName(ProviderProperties::isEnabled);
String appIdKey = prefix + "." + LambdaProperty.kebabName(ProviderProperties::getAppId);适合配置类字段和 YAML key 一一对应的场景。动态配置项名称、provider 名称、Map key、properties.* 扩展字段仍应保持显式字符串,因为它们是配置数据,不是 Java 属性。
参数校验
String provider = AssertUtils.hasText(inputProvider, "provider");
Collection<Long> ids = AssertUtils.notEmpty(request.getIds(), "ids");AssertUtils 返回校验后的原值,适合在方法开头完成参数收口。
签名比较
String signature = HashUtils.hmacSha256Hex(canonicalString, secret);
if (!HashUtils.secureEquals(expectedSignature, signature)) {
throw new IllegalArgumentException("signature is invalid");
}项目内使用规范
- 新代码优先引用
com.springopen.starter.toolkit.*。 - 不再新增
com.springopen.core.util.*工具类。 - 配置属性名、字段白名单、日志字段等高频字符串优先考虑
LambdaProperty或模块内集中常量。 - 断言参数名在调用点直接写字面量,不向
AssertUtils新增业务参数常量。 - 工具方法如果 JDK 或成熟生态已经提供,不再重复造轮子;Toolkit 只封装项目里高频、稳定、可复用的薄层能力。