跳到内容

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 和实际验证为准。

xml
<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集合处理

字符串

java
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-idURLValue -> url-valuerequest_url -> request-url

JavaBean Lambda 属性名

LambdaProperty 用于从标准 JavaBean getter lambda 解析属性名,并同时得到 kebab-case 名称。它主要服务配置解析、白名单字段声明、表格列、导出字段、日志字段和审计字段等场景,避免重复手写 "product-id""access-key-id" 这类字符串。

java
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

配置解析建议:

java
String prefix = "spring.open.sms.providers.tencent";
String key = prefix + "." + LambdaProperty.kebabName(SmsProvider::getProductId);

约束:

  • lambda 必须引用标准 JavaBean getter:getXxxisXxx
  • 不支持 fluent getter、普通方法和复杂 lambda 表达式。
  • 该工具只解析 kebab-case 名称,不解析数据库列名。
  • MyBatis-Plus 实体字段和数据库列名请使用 data-mybatis-plus 的 LambdaField / FieldName 能力,详见 数据库

计数

CountUtils 用于处理业务里最常见的非负计数,例如浏览数、点赞数、收藏数、评论数和调用次数。它只处理数值边界,不绑定任何业务语义。

java
CountUtils.nonNegative(null);        // 0
CountUtils.nonNegative(-1L);         // 0
CountUtils.changeNonNegative(1L, 2); // 3
CountUtils.changeNonNegative(1L, -5); // 0

适合:

  • 计数展示兜底。
  • 增减计数时避免出现负数。
  • Blog / Forum / Mall 等模块复用同一套非负计数规则。

集合

java
CollectionUtils.isEmpty(list);
CollectionUtils.isNotEmpty(list);
CollectionUtils.isEmpty(map);
CollectionUtils.isNotEmpty(map);
CollectionUtils.emptyIfNull(collection);

集合工具优先委托 Apache Commons Collections,不重复实现成熟能力。

参数校验

java
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 标准异常:

  • IllegalArgumentException
  • IllegalStateException

如果异常消息需要多语言,应在 Web、Service 或业务异常层处理,不要把 i18n 放进 Toolkit。

参数名直接在调用点写字符串字面量;业务参数名属于业务语义,不沉淀到 Toolkit 常量。

ID

java
IdUtils.uuid32();

uuid32 返回去掉 - 的 UUID 字符串,适合 traceId、消息 ID、临时 token 等场景。

Hash

java
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 后缀

java
String enabledKey = prefix + "." + LambdaProperty.kebabName(ProviderProperties::isEnabled);
String appIdKey = prefix + "." + LambdaProperty.kebabName(ProviderProperties::getAppId);

适合配置类字段和 YAML key 一一对应的场景。动态配置项名称、provider 名称、Map key、properties.* 扩展字段仍应保持显式字符串,因为它们是配置数据,不是 Java 属性。

参数校验

java
String provider = AssertUtils.hasText(inputProvider, "provider");
Collection<Long> ids = AssertUtils.notEmpty(request.getIds(), "ids");

AssertUtils 返回校验后的原值,适合在方法开头完成参数收口。

签名比较

java
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 只封装项目里高频、稳定、可复用的薄层能力。

Released under the Apache License 2.0.