首页 文章 精选 留言 我的

精选列表

搜索[java],共10000篇文章
优秀的个人博客,低调大师

比雪花短比UUID可读,这个Java业务流水号工具类有3个方法

## 📋 目录 - 一、工具类功能 - 二、所需环境 - 三、方法与调用示例(按需调用) - 四、工具类源码(可直接复制) ## 一、工具类功能 生成「前缀 + 日期(yyyyMMdd) + 递增序列」的短单号,例如 `ORD20261005000001`,比雪花 ID 短、比 UUID 可读,序列按天在进程内递增、入口方法加了同步。 ## 二、所需环境 JDK 17或以上 ## 三、方法与调用示例(按需调用) 🔢 **方法序号**:1 🎯 **方法用途**: - 按系统当天日期出「前缀 + yyyyMMdd + 6 位序列」的业务单号,比雪花 ID 短、比 UUID 可读 - 内部订单号、退款号这类要看日期和先后顺序的场景用它 🧩 **方法定义**:`public static synchronized String next(String prefix)` 🗣️ **入参解释**:`public static synchronized String next(业务前缀)` 📤 **返回值解释**:`String`,形如 `ORD20261013000001` 的单号 💡 **边界口径**: - 序列按天各自计数,某天在本进程首次取号从 1 开始 - 同一天内所有前缀共用一个计数器,换前缀是跳号而不是重新计数 - 序列不足 6 位左补零,涨过 6 位后按实际长度输出,不截断 - 号段只存在内存里,进程重启后当天序列同样从 1 重新开始,只适合单实例、可接受重启重号的内部流水 - 前缀原样拼接,不做长度与字符集校验,但空白前缀被拒绝 🧪 **方法示例**: - 本进程第一次 `next("ORD")` 拿到 `ORD20261013000001` - 紧接着再调一次拿到 `ORD20261013000002`,当天序列往上加 - 换成 `next("PAY")` 拿到 `PAY20261013000003`,同一天所有前缀共用一个计数器 - `next(" ")` 抛 `业务前缀不能为空` --- 🔢 **方法序号**:2 🎯 **方法用途**: - 同样按当天日期取号,补零位数由 `seqLength` 决定 - 单号字段宽度有限、或者想让序列短一点时用这一版 🧩 **方法定义**:`public static synchronized String next(String prefix, int seqLength)` 🗣️ **入参解释**:`public static synchronized String next(业务前缀, 序列位数)` 📤 **返回值解释**:`String`,前缀加日期再加指定位数的序列 💡 **边界口径**: - `seqLength` 只是补零目标,序列涨到比它长时按实际长度输出,不会截断成固定宽度 - `seqLength` 给 1 时没有任何补零,直接是实际序列 - `seqLength` 小于等于 0 抛 `IllegalArgumentException`,消息里带上实际值 - 与单参版共用同一把类锁和同一份号段,两个入口之间也互相递增 🧪 **方法示例**: - `next("REFUND", 3)` 拿到 `REFUND20261013004` - `next("SETTLE", 12)` 拿到 `SETTLE20261013000000000005`,补到 12 位 - `next("MIN", 1)` 拿到 `MIN202610132`,一位都不补 - `next("ORD", 0)` 抛 `序列位数必须大于0,当前为0` - `next("ORD", -3)` 抛 `序列位数必须大于0,当前为-3` --- 🔢 **方法序号**:3 🎯 **方法用途**: - 指定业务日期取号,按历史日期补单、造测试数据时用 - 时钟回拨或跨零点这类场景不想让号段错挂到另一天上,就用这个显式入口 🧩 **方法定义**:`public static synchronized String next(String prefix, LocalDate date, int seqLength)` 🗣️ **入参解释**:`public static synchronized String next(业务前缀, 业务日期, 序列位数)` 📤 **返回值解释**:`String`,前缀加指定日期再加该天的序列 💡 **边界口径**: - 每一天有自己的序列,日期前进后那天首次取号从 1 开始 - 再传回今天会接着当天的序列往上加,既不重发已发出的号,也不被回拨打乱 - 号段只保留最近 7 天,更早的日期被回收后再取号会从 1 重新计 - 取日期、读该天序列、写回在同一个临界区里完成,16000 次并发取号也不重号 - `date` 传 null 抛 `IllegalArgumentException` 🧪 **方法示例**: - `next("SHIP", today, 6)` 拿到 `SHIP20261013000006`,接在当天序列后面 - 日期前进一天首取拿到 `SHIP20261014000001`,那天从零开始 - 再把日期传回今天拿到 `SHIP20261013000007`,当天已用过的号不会被重发 - 8 个线程各取 2000 次去重后拿到 `16000 条`,没有重号 - 序列涨到 16008 之后拿 2 位宽度也是 `TRADE2026101316008`,不截断 - 连续补 10 天的单再回取最早那天,拿到 `ARCH20261004000001`,过期号段被回收后从 1 重计 - `next("ORD", null, 6)` 抛 `业务日期不能为null` ## 四、工具类源码(可直接复制) 📦 评论区留言

优秀的个人博客,低调大师

这个Java工具类14个方法兼容微信支付宝

## 📋 目录 - [一、工具类功能](#sec-func) - [二、所需环境](#sec-env) - [三、方法与调用示例(按需调用)](#sec-method) ## 一、工具类功能 参数表按 key 字典序拼成签名原文,再走摘要式或 HMAC-SHA256 出签名,回调验签、请求签名、兼容微信支付宝老协议都能用,拿回来是十六进制签名串与验签结论。 ## 二、所需环境 JDK 17或以上 ## 三、方法与调用示例(按需调用) 🔢 **方法序号**:1 🎯 **方法用途**: - 把参数表按 key 字典序拼成签名原文,`sign`、`sign_type` 与空值参数一律不进原文 - 对接支付网关、开放平台回调时先拿它出原文,再交给具体算法 🧩 **方法定义**:`public static String buildSignSource(Map params)` 🗣️ **入参解释**:`public static String buildSignSource(参数表)` 📤 **返回值解释**:`String`,形如 `k1=v1&k2=v2` 的签名原文 💡 **边界口径**: - 排序按 key 的字典序走,与参数表原本的插入顺序无关 - 值为 null 与空串的参数被跳过,各家平台在这点上是一致的 - 集合与数组按出现顺序展开成同名多项,`tag=b&tag=a` 这种写法 - 原文不做 URL 编码,这是它与 `UrlCodecUtils.encodeQuery` 的本质区别,拿编码后的串去签名一定对不上 - 原样拼接的边界有歧义:值里含 `&` 或 `=` 时能拼出同一份原文,参与签名的字段不能来自不可信输入的自由拼接 - 参数表传 null 抛 `IllegalArgumentException`,key 为 null 的条目被直接跳过 🧪 **方法示例**: - `buildSignSource(params)` 拿到 `appid=wx08804b9c9f8f8e3a&body=测试商品-上海&nonce_str=593EF8934B5A94F0C2&out_trade_no=ORD20260925001&total_fee=1` - `buildSignSource(multiValue)` 拿到 `amount=12800&tag=b&tag=a`,集合按出现顺序展开 - `buildSignSource(Map.of("a", "1&b=2"))` 拿到 `a=1&b=2`,与两个参数拼出的原文完全一样 - `buildSignSource(null)` 抛 `参数Map不能为null` --- 🔢 **方法序号**:2 🎯 **方法用途**: - 在默认剔除清单之上再加本平台特有的字段,比如某些平台要把 `sign_key`、`attach` 摘掉 - 各家协议要求剔除的字段不同,用这一版就不用自己先复制一份 Map 再 remove 🧩 **方法定义**:`public static String buildSignSource(Map params, Set excludeKeys)` 🗣️ **入参解释**:`public static String buildSignSource(参数表, 额外剔除字段)` 📤 **返回值解释**:`String`,剔除后按字典序拼出的签名原文 💡 **边界口径**: - 自定义清单是追加在 `sign`、`sign_type` 之上的,没法靠它把默认剔除关掉 - `excludeKeys` 传 null 时只按默认清单剔,不报错 - 其余排序、空值跳过、不做 URL 编码的口径与单参版一致 🧪 **方法示例**: - `buildSignSource(withExtra, Set.of("sign_key"))` 拿到 `appid=wx08804b9c9f8f8e3a&body=测试商品-上海&nonce_str=593EF8934B5A94F0C2&out_trade_no=ORD20260925001&total_fee=1`,与默认清单剔完的结果一模一样 - `buildSignSource(params, null)` 拿到 `body=测试商品-上海&out_trade_no=ORD20260925001&total_fee=1` - `buildSignSource(Map.of("body", "x", "partner", "y"), Set.of("partner"))` 拿到 `body=x` --- 🔢 **方法序号**:3 🎯 **方法用途**: - 对现成的签名原文做摘要式签名,原文末尾拼上 `&key=密钥` 再整体取摘要,输出小写 Hex - 微信 JS-SDK 这类要自己拼原文(`jsapi_ticket=...&noncestr=...&url=...`)的协议只能走这一层 🧩 **方法定义**:`public static String signSourceByDigest(String signSource, String secret, String digestAlgorithm)` 🗣️ **入参解释**:`public static String signSourceByDigest(签名原文, 密钥, 摘要算法)` 📤 **返回值解释**:`String`,小写十六进制的签名值 💡 **边界口径**: - 算法取 `ALG_MD5`、`ALG_SHA256` 这类常量,白名单外的名字抛 `IllegalArgumentException` 并列出支持范围 - 密钥拼在原文末尾的 `&key=` 之后,这是历史协议的固定写法 - 摘要式为兼容老协议保留,新接入用 HMAC,涉及资金的验签用非对称算法 - 原文为 null、密钥为 null 或空白都抛 `IllegalArgumentException`,空原文照样能算 🧪 **方法示例**: - `signSourceByDigest(source, SECRET, ApiSignUtils.ALG_SHA256)` 拿到 `0f0d73df94313475f5585dcb66f4caabecc465f01605630afbb02dfa13b18491` - `signSourceByDigest(jsApiSource, SECRET, ApiSignUtils.ALG_SHA256)` 拿到 `8b9bb3ee34396e862a5bb946a7c63b4cad0a1bcc6839372547df4594b0317ce5` - `signSourceByDigest("", SECRET, ApiSignUtils.ALG_MD5)` 拿到 `0b4ad1d96492c0442d79efa9125434f4`,空原文也能算 - `signSourceByDigest(null, SECRET, ApiSignUtils.ALG_SHA256)` 抛 `签名原文不能为null` - `signByDigest(params, SECRET, "SM3")` 抛 `不支持的摘要算法: SM3,仅支持 MD5/SHA-1/SHA-256/SHA-512` --- 🔢 **方法序号**:4 🎯 **方法用途**: - 摘要式签名并要求输出大写 Hex,部分银行网关的字段就是这个形态 - 只是大小写不同的展示形式,签名值本身与三参版一致 🧩 **方法定义**:`public static String signSourceByDigest(String signSource, String secret, String digestAlgorithm, boolean uppercase)` 🗣️ **入参解释**:`public static String signSourceByDigest(签名原文, 密钥, 摘要算法, 是否大写)` 📤 **返回值解释**:`String`,`uppercase` 为 true 时给大写 Hex 💡 **边界口径**: - 转大写用的是 `Locale.ROOT`,不受系统语言环境影响 - 验签侧比的是解码后的字节,发出去是大写、对方按小写验也能过 - 参数校验与三参版相同 🧪 **方法示例**: - `signSourceByDigest(source, SECRET, ApiSignUtils.ALG_SHA256, true)` 拿到 `0F0D73DF94313475F5585DCB66F4CAABECC465F01605630AFBB02DFA13B18491` - `verifySourceByDigest(source, SECRET, ApiSignUtils.ALG_MD5, 大写签名)` 拿到 `true` --- 🔢 **方法序号**:5 🎯 **方法用途**: - 对签名原文做 HMAC 签名,输出小写 Hex,新接入首选 `HmacSHA256` - 密钥不进原文、而是当 HMAC 的 key 用,比把密钥拼在明文尾巴上稳 🧩 **方法定义**:`public static String signSourceByHmac(String signSource, String key, String hmacAlgorithm)` 🗣️ **入参解释**:`public static String signSourceByHmac(签名原文, 密钥, HMAC算法)` 📤 **返回值解释**:`String`,小写十六进制的 HMAC 签名值 💡 **边界口径**: - HMAC 算法白名单是 `HmacMD5`、`HmacSHA1`、`HmacSHA256`、`HmacSHA512`,写别的直接抛异常 - 原文为 null、密钥为 null 或空白都抛 `IllegalArgumentException` - 原文这层不做任何拼接与排序,传进来什么就签什么 🧪 **方法示例**: - `signSourceByHmac(source, SECRET, "HmacSHA256")` 拿到 `9aa4610b16258cd2970cdaa371c699a93ad8916889a527c89cd456c7600bf27c` - `signSourceByHmac(source, " ", "HmacSHA256")` 抛 `密钥不能为null或空白` - `signSourceByHmac(source, SECRET, "HmacSM3")` 抛 `不支持的HMAC算法: HmacSM3,仅支持 HmacMD5/HmacSHA1/HmacSHA256/HmacSHA512` --- 🔢 **方法序号**:6 🎯 **方法用途**: - HMAC 签名并控制输出大小写,网关要求大写 Hex 时用 - 与三参版只差展示形式,签名值一致 🧩 **方法定义**:`public static String signSourceByHmac(String signSource, String key, String hmacAlgorithm, boolean uppercase)` 🗣️ **入参解释**:`public static String signSourceByHmac(签名原文, 密钥, HMAC算法, 是否大写)` 📤 **返回值解释**:`String`,`uppercase` 为 true 时给大写 Hex 💡 **边界口径**: - 大写只影响字符形态,验签比的是字节 - 参数校验与三参版相同 🧪 **方法示例**: - `signSourceByHmac(source, SECRET, "HmacSHA256", true)` 拿到 `9AA4610B16258CD2970CDAA371C699A93AD8916889A527C89CD456C7600BF27C` --- 🔢 **方法序号**:7 🎯 **方法用途**: - 参数表一步出摘要式签名,内部就是拼原文再做摘要 - 兼容微信公众号、支付宝老接口这类 `MD5(原文&key=密钥)` 的历史协议 🧩 **方法定义**:`public static String signByDigest(Map params, String secret, String digestAlgorithm)` 🗣️ **入参解释**:`public static String signByDigest(参数表, 密钥, 摘要算法)` 📤 **返回值解释**:`String`,小写十六进制的签名值 💡 **边界口径**: - 原文口径与 `buildSignSource(params)` 完全一致,`sign` 与空值不参与 - 回调验签时对方带回来的 `sign` 会被自动剔掉,不会把自己算进去 - MD5 只用于兼容,新接入别用 🧪 **方法示例**: - `signByDigest(params, SECRET, ApiSignUtils.ALG_MD5)` 拿到 `d7922d5ef9ab694bc6b87abb5d6c5f86` - `signByDigest(params, SECRET, ApiSignUtils.ALG_SHA256)` 拿到 `0f0d73df94313475f5585dcb66f4caabecc465f01605630afbb02dfa13b18491`,与先拼原文再签同值 --- 🔢 **方法序号**:8 🎯 **方法用途**: - 参数表一步出摘要式签名并指定大写 Hex - 老协议里要求签名字段大写的网关用这一版 🧩 **方法定义**:`public static String signByDigest(Map params, String secret, String digestAlgorithm, boolean uppercase)` 🗣️ **入参解释**:`public static String signByDigest(参数表, 密钥, 摘要算法, 是否大写)` 📤 **返回值解释**:`String`,指定大小写形态的签名值 💡 **边界口径**: - 只是把三参版的结果整体转大写,原文与算法口径不变 - 参数表为 null 同样抛 `IllegalArgumentException` 🧪 **方法示例**: - `signByDigest(params, SECRET, ApiSignUtils.ALG_MD5, true)` 拿到 `D7922D5EF9AB694BC6B87ABB5D6C5F86` --- 🔢 **方法序号**:9 🎯 **方法用途**: - 参数表一步出 HMAC 签名,新接入的首选写法 - 不想自己先调一次拼原文时,用它把两步并成一步 🧩 **方法定义**:`public static String signByHmac(Map params, String key, String hmacAlgorithm)` 🗣️ **入参解释**:`public static String signByHmac(参数表, 密钥, HMAC算法)` 📤 **返回值解释**:`String`,小写十六进制的签名值 💡 **边界口径**: - 与先 `buildSignSource` 再 `signSourceByHmac` 结果同值,两条路径可以混用 - 算法白名单、密钥与原文的校验口径与原文级入口一致 🧪 **方法示例**: - `signByHmac(params, SECRET, "HmacSHA256")` 拿到 `9aa4610b16258cd2970cdaa371c699a93ad8916889a527c89cd456c7600bf27c`,与原文级入口同值 --- 🔢 **方法序号**:10 🎯 **方法用途**: - 参数表一步出 HMAC 签名并指定大写输出 - 对端字段要求大写时用它,其余口径与三参版相同 🧩 **方法定义**:`public static String signByHmac(Map params, String key, String hmacAlgorithm, boolean uppercase)` 🗣️ **入参解释**:`public static String signByHmac(参数表, 密钥, HMAC算法, 是否大写)` 📤 **返回值解释**:`String`,指定大小写形态的签名值 💡 **边界口径**: - 大写只改展示形式,验签比的是字节 - 需要去掉 `=`、走 URL 安全字母形的 Base64 签名不属于本类范围 🧪 **方法示例**: - `signByHmac(params, SECRET, "HmacSHA256", true)` 拿到 `9AA4610B16258CD2970CDAA371C699A93AD8916889A527C89CD456C7600BF27C` --- 🔢 **方法序号**:11 🎯 **方法用途**: - 校验摘要式签名,走常量时间比较,避免按字节早退带来的计时侧信道 - 已经自己拼好原文(比如微信 JS-SDK)就验原文这一层 🧩 **方法定义**:`public static boolean verifySourceByDigest(String signSource, String secret, String digestAlgorithm, String expectedSign)` 🗣️ **入参解释**:`public static boolean verifySourceByDigest(签名原文, 密钥, 摘要算法, 待验签名)` 📤 **返回值解释**:`boolean`,签名与原文匹配返回 `true` 💡 **边界口径**: - 比的是解码后的字节,待验签名的大小写、首尾空白都不影响结果 - 签名值缺失、长度不对、含非十六进制字符一律判 `false`,不抛异常 - 会先算一次签名,所以原文、密钥、算法这类调用方错误照样抛 `IllegalArgumentException` - 只保证签名与原文一致,防重放还得自己配 nonce 与时间戳校验 🧪 **方法示例**: - `verifySourceByDigest(source, SECRET, ApiSignUtils.ALG_MD5, legacyMd5)` 拿到 `true` - 待验签名用大写形态传,同样拿到 `true` - `verifySourceByDigest(source, SECRET, "SM3", "anyhex")` 抛 `不支持的摘要算法: SM3,仅支持 MD5/SHA-1/SHA-256/SHA-512` --- 🔢 **方法序号**:12 🎯 **方法用途**: - 校验 HMAC 签名,同样走常量时间比较 - 对端把签名转成大写、或者串里带了空白时不会因为格式判错 🧩 **方法定义**:`public static boolean verifySourceByHmac(String signSource, String key, String hmacAlgorithm, String expectedSign)` 🗣️ **入参解释**:`public static boolean verifySourceByHmac(签名原文, 密钥, HMAC算法, 待验签名)` 📤 **返回值解释**:`boolean`,签名与原文匹配返回 `true` 💡 **边界口径**: - 待验签名先按十六进制解析,解不出就判 `false` - 大小写、首尾空白不参与比较 - 算法名非法、密钥空白这类调用方错误仍抛 `IllegalArgumentException` 🧪 **方法示例**: - `verifySourceByHmac(source, SECRET, "HmacSHA256", hmacHex.toUpperCase())` 拿到 `true` - 把签名改成小写再套上首尾空白,`verifyByHmac(params, SECRET, "HmacSHA256", " 大写串 ")` 仍拿到 `true` --- 🔢 **方法序号**:13 🎯 **方法用途**: - 对整张回调参数表验摘要式签名,一步完成拼原文与比较 - 支付回调通知的验签入口,`sign` 字段本身会被自动剔掉 🧩 **方法定义**:`public static boolean verifyByDigest(Map params, String secret, String digestAlgorithm, String expectedSign)` 🗣️ **入参解释**:`public static boolean verifyByDigest(参数表, 密钥, 摘要算法, 待验签名)` 📤 **返回值解释**:`boolean`,参数表与签名匹配返回 `true` 💡 **边界口径**: - 等于 `buildSignSource(params)` 再走原文级验签,剔除与排序口径完全一致 - 忘了剔 `sign` 这类字段导致验不过的情况在这一版不会发生 - 签名值非法只判 `false`,参数表为 null 抛异常 🧪 **方法示例**: - `verifyByDigest(params, SECRET, ApiSignUtils.ALG_MD5, legacyMd5)` 拿到 `true` --- 🔢 **方法序号**:14 🎯 **方法用途**: - 对整张参数表验 HMAC 签名,公网接口收到请求时的第一道校验 - 金额、订单号被改一分都过不了,签名值本身残缺也不会把异常抛给调用方 🧩 **方法定义**:`public static boolean verifyByHmac(Map params, String key, String hmacAlgorithm, String expectedSign)` 🗣️ **入参解释**:`public static boolean verifyByHmac(参数表, 密钥, HMAC算法, 待验签名)` 📤 **返回值解释**:`boolean`,参数表与签名匹配返回 `true` 💡 **边界口径**: - 参数字段被改、被删、多出未签名字段都会判 `false` - 待验签名为 null、空白、长度为奇数、含非十六进制字符都判 `false` 而不抛异常,公网收到的就是这种输入 - 值为 null 与空串的参数本来就不进原文,这类字段改了不影响验签结果 - 验过签名不等于请求没被重放,nonce 与时间戳要自己校验 🧪 **方法示例**: - `verifyByHmac(tampered, SECRET, "HmacSHA256", hmacHex)` 拿到 `false`,total_fee 被改了一分钱 - 参数表原样不动时拿到 `true` - 待验签名传 null 拿到 `false`,不抛异常 - 待验签名截掉最后一位(长度变奇数)拿到 `false` - 待验签名塞进非十六进制字符拿到 `false` - 待验签名传空白串拿到 `false`

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册