diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md
index bee351f57..25f233bd9 100755
--- a/docs/SUMMARY.md
+++ b/docs/SUMMARY.md
@@ -102,6 +102,8 @@
* [数学相关-MathUtil](core/数学/数学相关-MathUtil.md)
* 线程和并发
* [线程工具-ThreadUtil](core/线程和并发/线程工具-ThreadUtil.md)
+ * [自定义线程池-ExecutorBuilder](core/线程和并发/自定义线程池-ExecutorBuilder.md)
+ * [高并发测试-ConcurrencyTester](core/线程和并发/高并发测试-ConcurrencyTester.md)
* 图片
* [图片工具-ImgUtil](core/图片/图片工具-ImgUtil.md)
* [图片编辑器-Img](core/图片/图片编辑器-Img.md)
@@ -136,7 +138,8 @@
* [加密解密工具-SecureUtil](crypto/加密解密工具-SecureUtil.md)
* [对称加密-SymmetricCrypto](crypto/对称加密-SymmetricCrypto.md)
* [非对称加密-AsymmetricCrypto](crypto/非对称加密-AsymmetricCrypto.md)
- * [摘要加密-Digester和HMac](crypto/摘要加密-Digester和HMac.md)
+ * [摘要加密-Digester](crypto/摘要加密-Digester.md)
+ * [消息认证码算法-HMac.md](crypto/消息认证码算法-HMac.md)
* [签名和验证-Sign](crypto/签名和验证-Sign.md)
* [国密算法工具-SmUtil](crypto/国密算法工具-SmUtil.md)
* DFA查找(Hutool-dfa)
diff --git "a/docs/core/\346\227\245\346\234\237\346\227\266\351\227\264/\345\206\234\345\216\206\346\227\245\346\234\237-ChineseDate.md" "b/docs/core/\346\227\245\346\234\237\346\227\266\351\227\264/\345\206\234\345\216\206\346\227\245\346\234\237-ChineseDate.md"
index b16ce6e34..1cc805666 100644
--- "a/docs/core/\346\227\245\346\234\237\346\227\266\351\227\264/\345\206\234\345\216\206\346\227\245\346\234\237-ChineseDate.md"
+++ "b/docs/core/\346\227\245\346\234\237\346\227\266\351\227\264/\345\206\234\345\216\206\346\227\245\346\234\237-ChineseDate.md"
@@ -4,7 +4,22 @@
## 使用
+1. 构建`ChineseDate`对象
+
+`ChineseDate`表示了农历的对象,构建此对象既可以使用公历的日期,也可以使用农历的日期。
+
+```java
+//通过农历构建
+ChineseDate chineseDate = new ChineseDate(1992,12,14);
+
+//通过公历构建
+ChineseDate chineseDate = new ChineseDate(DateUtil.parseDate("1993-01-06"));
+```
+
+2. 基本使用
+
```java
+//通过公历构建
ChineseDate date = new ChineseDate(DateUtil.parseDate("2020-01-25"));
// 一月
date.getChineseMonth();
@@ -14,10 +29,22 @@ date.getChineseMonthName();
date.getChineseDay();
// 庚子
date.getCyclical();
-// 鼠
+// 生肖:鼠
date.getChineseZodiac();
-// 春节
+// 传统节日(部分支持,逗号分隔):春节
date.getFestivals();
// 庚子鼠年 正月初一
date.toString();
+```
+
+3. 获取天干地支
+
+从`5.4.1`开始,Hutool支持天干地支的获取:
+
+```java
+//通过公历构建
+ChineseDate chineseDate = new ChineseDate(DateUtil.parseDate("2020-08-28"));
+
+// 庚子年甲申月癸卯日
+String cyclicalYMD = chineseDate.getCyclicalYMD();
```
\ No newline at end of file
diff --git "a/docs/core/\347\272\277\347\250\213\345\222\214\345\271\266\345\217\221/\347\272\277\347\250\213\345\267\245\345\205\267-ThreadUtil.md" "b/docs/core/\347\272\277\347\250\213\345\222\214\345\271\266\345\217\221/\347\272\277\347\250\213\345\267\245\345\205\267-ThreadUtil.md"
index 6f7d041ae..1f132770a 100755
--- "a/docs/core/\347\272\277\347\250\213\345\222\214\345\271\266\345\217\221/\347\272\277\347\250\213\345\267\245\345\205\267-ThreadUtil.md"
+++ "b/docs/core/\347\272\277\347\250\213\345\222\214\345\271\266\345\217\221/\347\272\277\347\250\213\345\267\245\345\205\267-ThreadUtil.md"
@@ -2,7 +2,7 @@
并发在Java中算是一个比较难理解和容易出问题的部分,而并发的核心在线程。好在从JDK1.5开始Java提供了`concurrent`包可以很好的帮我们处理大部分并发、异步等问题。
-不过,ExecutorService和Executors等众多概念依旧让我们使用这个包变得比较麻烦,如何才能隐藏这些概念?又如何用一个方法解决问题?`ThreadUtil`便为此而生。
+不过,`ExecutorService`和`Executors`等众多概念依旧让我们使用这个包变得比较麻烦,如何才能隐藏这些概念?又如何用一个方法解决问题?`ThreadUtil`便为此而生。
## 原理
Hutool使用`GlobalThreadPool`持有一个全局的线程池,默认所有异步方法在这个线程池中执行。
diff --git "a/docs/core/\347\272\277\347\250\213\345\222\214\345\271\266\345\217\221/\350\207\252\345\256\232\344\271\211\347\272\277\347\250\213\346\261\240-ExecutorBuilder.md" "b/docs/core/\347\272\277\347\250\213\345\222\214\345\271\266\345\217\221/\350\207\252\345\256\232\344\271\211\347\272\277\347\250\213\346\261\240-ExecutorBuilder.md"
new file mode 100644
index 000000000..e5b03dbf4
--- /dev/null
+++ "b/docs/core/\347\272\277\347\250\213\345\222\214\345\271\266\345\217\221/\350\207\252\345\256\232\344\271\211\347\272\277\347\250\213\346\261\240-ExecutorBuilder.md"
@@ -0,0 +1,81 @@
+## 由来
+
+在JDK中,提供了`Executors`用于创建自定义的线程池对象`ExecutorService`,但是考虑到线程池中存在众多概念,这些概念通过不同的搭配实现灵活的线程管理策略,单独使用`Executors`无法满足需求,构建了`ExecutorBuilder`。
+
+### 概念
+
+- `corePoolSize` 初始池大小
+- `maxPoolSize` 最大池大小(允许同时执行的最大线程数)
+- `workQueue` 队列,用于存在未执行的线程
+- `handler` 当线程阻塞(block)时的异常处理器,所谓线程阻塞即线程池和等待队列已满,无法处理线程时采取的策略
+
+### 线程池对待线程的策略
+
+1. 如果池中任务数 < corePoolSize -> 放入立即执行
+2. 如果池中任务数 > corePoolSize -> 放入队列等待
+3. 队列满 -> 新建线程立即执行
+4. 执行中的线程 > maxPoolSize -> 触发handler(RejectedExecutionHandler)异常
+
+### workQueue线程池策略
+
+- `SynchronousQueue` 它将任务直接提交给线程而不保持它们。当运行线程小于`maxPoolSize`时会创建新线程,否则触发异常策略
+- `LinkedBlockingQueue` 默认无界队列,当运行线程大于`corePoolSize`时始终放入此队列,此时`maxPoolSize`无效。当构造LinkedBlockingQueue对象时传入参数,变为有界队列,队列满时,运行线程小于`maxPoolSize`时会创建新线程,否则触发异常策略
+- `ArrayBlockingQueue` 有界队列,相对无界队列有利于控制队列大小,队列满时,运行线程小于`maxPoolSize`时会创建新线程,否则触发异常策略
+
+## 使用
+
+1. 默认线程池
+
+策略如下:
+
+- 初始线程数为corePoolSize指定的大小
+- 没有最大线程数限制
+- 默认使用LinkedBlockingQueue,默认队列大小为1024(最大等待数1024)
+- 当运行线程大于corePoolSize放入队列,队列满后抛出异常
+
+```java
+ExecutorService executor = ExecutorBuilder builder = ExecutorBuilder.create()..build();
+```
+
+2. 单线程线程池
+
+- 初始线程数为 1
+- 最大线程数为 1
+- 默认使用LinkedBlockingQueue,默认队列大小为1024
+- 同时只允许一个线程工作,剩余放入队列等待,等待数超过1024报错
+
+```java
+ExecutorService executor = ExecutorBuilder.create()//
+ .setCorePoolSize(1)//
+ .setMaxPoolSize(1)//
+ .setKeepAliveTime(0)//
+ .build();
+```
+
+3. 更多选项的线程池
+
+- 初始5个线程
+- 最大10个线程
+- 有界等待队列,最大等待数是100
+
+```java
+ExecutorService executor = ExecutorBuilder.create()
+ .setCorePoolSize(5)
+ .setMaxPoolSize(10)
+ .setWorkQueue(new LinkedBlockingQueue<>(100))
+ .build();
+```
+
+3. 特殊策略的线程池
+
+- 初始5个线程
+- 最大10个线程
+- 它将任务直接提交给线程而不保持它们。当运行线程小于maxPoolSize时会创建新线程,否则触发异常策略
+
+```java
+ExecutorService executor = ExecutorBuilder.create()
+ .setCorePoolSize(5)
+ .setMaxPoolSize(10)
+ .useSynchronousQueue()
+ .build();
+```
\ No newline at end of file
diff --git "a/docs/core/\347\272\277\347\250\213\345\222\214\345\271\266\345\217\221/\351\253\230\345\271\266\345\217\221\346\265\213\350\257\225-ConcurrencyTester.md" "b/docs/core/\347\272\277\347\250\213\345\222\214\345\271\266\345\217\221/\351\253\230\345\271\266\345\217\221\346\265\213\350\257\225-ConcurrencyTester.md"
new file mode 100644
index 000000000..b939d0007
--- /dev/null
+++ "b/docs/core/\347\272\277\347\250\213\345\222\214\345\271\266\345\217\221/\351\253\230\345\271\266\345\217\221\346\265\213\350\257\225-ConcurrencyTester.md"
@@ -0,0 +1,17 @@
+## 由来
+
+很多时候,我们需要简单模拟N个线程调用某个业务测试其并发状况,于是Hutool提供了一个简单的并发测试类——ConcurrencyTester。
+
+## 使用
+
+```java
+ConcurrencyTester tester = ThreadUtil.concurrencyTest(100, () -> {
+ // 测试的逻辑内容
+ long delay = RandomUtil.randomLong(100, 1000);
+ ThreadUtil.sleep(delay);
+ Console.log("{} test finished, delay: {}", Thread.currentThread().getName(), delay);
+});
+
+// 获取总的执行时间,单位毫秒
+Console.log(tester.getInterval());
+```
diff --git "a/docs/crypto/\345\233\275\345\257\206\347\256\227\346\263\225\345\267\245\345\205\267-SmUtil.md" "b/docs/crypto/\345\233\275\345\257\206\347\256\227\346\263\225\345\267\245\345\205\267-SmUtil.md"
index 1babb65e8..5a9739bf6 100755
--- "a/docs/crypto/\345\233\275\345\257\206\347\256\227\346\263\225\345\267\245\345\205\267-SmUtil.md"
+++ "b/docs/crypto/\345\233\275\345\257\206\347\256\227\346\263\225\345\267\245\345\205\267-SmUtil.md"
@@ -15,14 +15,14 @@ Hutool针对`Bouncy Castle`做了简化包装,用于实现国密算法中的SM
```xml
- org.bouncycastle
- bcprov-jdk15on
- ${bouncycastle.version}
+ org.bouncycastle
+ bcprov-jdk15to18
+ 1.66
```
> 说明
-> `bcprov-jdk15on`的版本请前往Maven中央库搜索,查找对应JDK的版本。
+> `bcprov-jdk15to18`的版本请前往Maven中央库搜索,查找对应JDK的最新版本。
### 非对称加密SM2
@@ -52,6 +52,49 @@ String encryptStr = sm2.encryptBcd(text, KeyType.PublicKey);
String decryptStr = StrUtil.utf8Str(sm2.decryptFromBcd(encryptStr, KeyType.PrivateKey));
```
+3. SM2签名和验签
+
+```java
+String content = "我是Hanley.";
+final SM2 sm2 = SmUtil.sm2();
+String sign = sm2.signHex(HexUtil.encodeHexStr(content));
+
+// true
+boolean verify = sm2.verifyHex(HexUtil.encodeHexStr(content), sign);
+```
+
+当然,也可以自定义密钥对:
+
+```java
+String content = "我是Hanley.";
+KeyPair pair = SecureUtil.generateKeyPair("SM2");
+final SM2 sm2 = new SM2(pair.getPrivate(), pair.getPublic());
+
+byte[] sign = sm2.sign(content.getBytes());
+
+// true
+boolean verify = sm2.verify(content.getBytes(), sign);
+```
+
+4. 使用SM2曲线点构建SM2
+
+使用曲线点构建中的点生成和验证见:[https://i.goto327.top/CryptTools/SM2.aspx?tdsourcetag=s_pctim_aiomsg](https://i.goto327.top/CryptTools/SM2.aspx?tdsourcetag=s_pctim_aiomsg)
+
+```java
+String privateKeyHex = "FAB8BBE670FAE338C9E9382B9FB6485225C11A3ECB84C938F10F20A93B6215F0";
+String x = "9EF573019D9A03B16B0BE44FC8A5B4E8E098F56034C97B312282DD0B4810AFC3";
+String y = "CC759673ED0FC9B9DC7E6FA38F0E2B121E02654BF37EA6B63FAF2A0D6013EADF";
+
+// 数据和ID此处使用16进制表示
+String dataHex = "434477813974bf58f94bcf760833c2b40f77a5fc360485b0b9ed1bd9682edb45";
+String idHex = "31323334353637383132333435363738";
+
+final SM2 sm2 = new SM2(privateKeyHex, x, y);
+final String sign = sm2.signHex(data, id);
+// true
+boolean verify = sm2.verifyHex(data, sign)
+```
+
### 摘要加密算法SM3
```java
diff --git "a/docs/crypto/\346\221\230\350\246\201\345\212\240\345\257\206-Digester\345\222\214HMac.md" "b/docs/crypto/\346\221\230\350\246\201\345\212\240\345\257\206-Digester.md"
old mode 100755
new mode 100644
similarity index 60%
rename from "docs/crypto/\346\221\230\350\246\201\345\212\240\345\257\206-Digester\345\222\214HMac.md"
rename to "docs/crypto/\346\221\230\350\246\201\345\212\240\345\257\206-Digester.md"
index 191201f41..d78d241e7
--- "a/docs/crypto/\346\221\230\350\246\201\345\212\240\345\257\206-Digester\345\222\214HMac.md"
+++ "b/docs/crypto/\346\221\230\350\246\201\345\212\240\345\257\206-Digester.md"
@@ -6,11 +6,10 @@
但是,由于输出的密文是提取原数据经过处理的定长值,所以它已经不能还原为原数据,即消息摘要算法是不可逆的,理论上无法通过反向运算取得原数据内容,因此它通常只能被用来做数据完整性验证。
-### HMAC介绍
-HMAC,全称为“Hash Message Authentication Code”,中文名“散列消息鉴别码”,主要是利用哈希算法,以一个密钥和一个消息为输入,生成一个消息摘要作为输出。一般的,消息鉴别码用于验证传输于两个共 同享有一个密钥的单位之间的消息。HMAC 可以与任何迭代散列函数捆绑使用。MD5 和 SHA-1 就是这种散列函数。HMAC 还可以使用一个用于计算和确认消息鉴别值的密钥。
-
## Hutool支持的摘要算法类型
+在不引入第三方库的情况下,JDK支持有限的摘要算法:
+
详细见:[https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#MessageDigest](https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#MessageDigest)
### 摘要算法
@@ -21,59 +20,38 @@ HMAC,全称为“Hash Message Authentication Code”,中文名“散列消
- SHA-384
- SHA-512
-### Hmac算法
-- HmacMD5
-- HmacSHA1
-- HmacSHA256
-- HmacSHA384
-- HmacSHA512
-
-## 摘要算法抽象
-
-摘要对象被抽象为两个对象:
-- Digester
-- HMac
-
## 使用
+
### Digester
以MD5为例:
```java
Digester md5 = new Digester(DigestAlgorithm.MD5);
-String digestHex = md5.digestHex(testStr);//5393554e94bf0eb6436f240a4fd71282
+
+// 5393554e94bf0eb6436f240a4fd71282
+String digestHex = md5.digestHex(testStr);
```
当然,做为最为常用的方法,MD5等方法被封装为工具方法在`DigestUtil`中,以上代码可以进一步简化为:
```java
+// 5393554e94bf0eb6436f240a4fd71282
String md5Hex1 = DigestUtil.md5Hex(testStr);
-//Junit单元测试
-//Assert.assertEquals("5393554e94bf0eb6436f240a4fd71282", md5Hex1);
```
-### HMac
-
-以HmacMD5为例:
-```java
-String testStr = "test中文";
-
-byte[] key = "password".getBytes();
-HMac mac = new HMac(HmacAlgorithm.HmacMD5, key);
-
-String macHex1 = mac.digestHex(testStr);//b977f4b13f93f549e06140971bded384
-```
+## 更多摘要算法
### SM3
-在4.2.1之后,Hutool借助Bouncy Castle库可以支持国密算法,以SM3为例:
+在`4.2.1`之后,Hutool借助`Bouncy Castle`库可以支持国密算法,以SM3为例:
我们首先需要引入Bouncy Castle库:
```xml
org.bouncycastle
- bcpkix-jdk15on
- 1.60
+ bcprov-jdk15to18
+ 1.66
```
@@ -81,6 +59,9 @@ String macHex1 = mac.digestHex(testStr);//b977f4b13f93f549e06140971bded384
```java
Digester digester = DigestUtil.digester("sm3");
+
//136ce3c86e4ed909b76082055a61586af20b4dab674732ebd4b599eef080c9be
String digestHex = digester.digestHex("aaaaa");
-```
\ No newline at end of file
+```
+
+> Java标准库的`java.security`包提供了一种标准机制,允许第三方提供商无缝接入。当引入`Bouncy Castle`库的jar后,Hutool会自动检测并接入。具体方法可见`SecureUtil.createMessageDigest`。
\ No newline at end of file
diff --git "a/docs/crypto/\346\266\210\346\201\257\350\256\244\350\257\201\347\240\201\347\256\227\346\263\225-HMac.md" "b/docs/crypto/\346\266\210\346\201\257\350\256\244\350\257\201\347\240\201\347\256\227\346\263\225-HMac.md"
new file mode 100644
index 000000000..1aeac44a3
--- /dev/null
+++ "b/docs/crypto/\346\266\210\346\201\257\350\256\244\350\257\201\347\240\201\347\256\227\346\263\225-HMac.md"
@@ -0,0 +1,40 @@
+## 介绍
+
+### HMAC介绍
+HMAC,全称为“Hash Message Authentication Code”,中文名“散列消息鉴别码”,主要是利用哈希算法,以一个密钥和一个消息为输入,生成一个消息摘要作为输出。一般的,消息鉴别码用于验证传输于两个共 同享有一个密钥的单位之间的消息。HMAC 可以与任何迭代散列函数捆绑使用。MD5 和 SHA-1 就是这种散列函数。HMAC 还可以使用一个用于计算和确认消息鉴别值的密钥。
+
+## Hutool支持的算法类型
+
+### Hmac算法
+
+在不引入第三方库的情况下,JDK支持有限的摘要算法:
+
+- HmacMD5
+- HmacSHA1
+- HmacSHA256
+- HmacSHA384
+- HmacSHA512
+
+## 使用
+
+### HMac
+
+以HmacMD5为例:
+```java
+String testStr = "test中文";
+
+// 此处密钥如果有非ASCII字符,考虑编码
+byte[] key = "password".getBytes();
+HMac mac = new HMac(HmacAlgorithm.HmacMD5, key);
+
+// b977f4b13f93f549e06140971bded384
+String macHex1 = mac.digestHex(testStr);
+```
+
+## 更多HMac算法
+
+与摘要算法类似,通过加入`Bouncy Castle`库可以调用更多算法,使用也类似:
+
+```java
+HMac mac = new HMac("XXXX", key);
+```
\ No newline at end of file
diff --git "a/docs/crypto/\347\255\276\345\220\215\345\222\214\351\252\214\350\257\201-Sign.md" "b/docs/crypto/\347\255\276\345\220\215\345\222\214\351\252\214\350\257\201-Sign.md"
index deb3aeb16..6a7c28230 100755
--- "a/docs/crypto/\347\255\276\345\220\215\345\222\214\351\252\214\350\257\201-Sign.md"
+++ "b/docs/crypto/\347\255\276\345\220\215\345\222\214\351\252\214\350\257\201-Sign.md"
@@ -1,9 +1,9 @@
## 介绍
Hutool针对`java.security.Signature`做了简化包装,包装类为:`Sign`,用于生成签名和签名验证。
-对于签名算法,Hutool封装了JDK的,具体介绍见:[https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#Signature](https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#Signature):
+对于签名算法,Hutool封装了JDK的Signature,具体介绍见:[https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#Signature](https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#Signature):
-```
+```java
// The RSA signature algorithm
NONEwithRSA
diff --git "a/docs/crypto/\351\235\236\345\257\271\347\247\260\345\212\240\345\257\206-AsymmetricCrypto.md" "b/docs/crypto/\351\235\236\345\257\271\347\247\260\345\212\240\345\257\206-AsymmetricCrypto.md"
index 674b31f5f..702ac436c 100755
--- "a/docs/crypto/\351\235\236\345\257\271\347\247\260\345\212\240\345\257\206-AsymmetricCrypto.md"
+++ "b/docs/crypto/\351\235\236\345\257\271\347\247\260\345\212\240\345\257\206-AsymmetricCrypto.md"
@@ -10,14 +10,15 @@
Hutool封装了JDK的,详细见[https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#KeyPairGenerator](https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#KeyPairGenerator):
- RSA
-- DSA
-- EC
+- RSA_ECB_PKCS1(RSA/ECB/PKCS1Padding)
+- RSA_None(RSA/None/NoPadding)
+- ECIES(需要Bouncy Castle库)
## 使用
在非对称加密中,我们可以通过`AsymmetricCrypto(AsymmetricAlgorithm algorithm)`构造方法,通过传入不同的算法枚举,获得其加密解密器。
-当然,为了方便,我们针对最常用的RSA和DSA算法构建了单独的对象:`RSA`和`DSA`。
+当然,为了方便,我们针对最常用的RSA算法构建了单独的对象:`RSA`。
### 基本使用
@@ -93,3 +94,33 @@ byte[] decrypt = rsa.decrypt(aByte, KeyType.PrivateKey);
//Assert.assertEquals("虎头闯杭州,多抬头看天,切勿只管种地", StrUtil.str(decrypt, CharsetUtil.CHARSET_UTF_8));
```
+## 其它算法
+
+### ECIES
+
+ECIES全称集成加密方案(elliptic curve integrate encrypt scheme)
+
+Hutool借助`Bouncy Castle`库可以支持`ECIES`算法:
+
+我们首先需要引入Bouncy Castle库:
+
+```xml
+
+ org.bouncycastle
+ bcprov-jdk15to18
+ 1.66
+
+```
+
+```java
+final ECIES ecies = new ECIES();
+String textBase = "我是一段特别长的测试";
+StringBuilder text = new StringBuilder();
+for (int i = 0; i < 10; i++) {
+ text.append(textBase);
+}
+
+// 公钥加密,私钥解密
+String encryptStr = ecies.encryptBase64(text.toString(), KeyType.PublicKey);
+String decryptStr = StrUtil.utf8Str(ecies.decrypt(encryptStr, KeyType.PrivateKey));
+```
\ No newline at end of file