From 2cfb33af9eeac7869e180c496ac1c427f77fa903 Mon Sep 17 00:00:00 2001 From: jiazhengquan <2466896229@qq.com> Date: Tue, 8 Mar 2022 18:01:07 +0800 Subject: [PATCH 01/24] =?UTF-8?q?=E9=92=88=E5=AF=B9issue#I4WUWR=E5=88=9D?= =?UTF-8?q?=E7=89=88=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/hutool/core/util/ReflectUtil.java | 33 ++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java index de406bfbf..4f9ded9d2 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java @@ -663,18 +663,35 @@ public class ReflectUtil { Assert.notNull(beanClass); Method[] allMethods = null; + Class[] searchInterfaces = null, parentInterfaces = null; Class searchType = beanClass; Method[] declaredMethods; - while (searchType != null) { - declaredMethods = searchType.getDeclaredMethods(); - if (null == allMethods) { - allMethods = declaredMethods; - } else { - allMethods = ArrayUtil.append(allMethods, declaredMethods); + while (searchType != null || searchInterfaces != null) { + if (searchType != null) { + declaredMethods = searchType.getDeclaredMethods(); + if (null == allMethods) { + allMethods = declaredMethods; + } else { + allMethods = ArrayUtil.append(allMethods, declaredMethods); + } + Class[] interfaces = searchType.getInterfaces(); + for (Class element : interfaces) { + allMethods = ArrayUtil.append(allMethods, element.getDeclaredMethods()); + parentInterfaces = ArrayUtil.addAll(element.getInterfaces()); + } + searchInterfaces = parentInterfaces; + Class[] classes = searchInterfaces.length == 0 ? null : searchInterfaces; + searchInterfaces = withSuperClassMethods ? classes : null; + searchType = withSuperClassMethods ? searchType.getSuperclass() : null; + } + if (searchInterfaces != null) { + for (Class searchInterface : searchInterfaces) { + allMethods = ArrayUtil.append(allMethods, searchInterface.getDeclaredMethods()); + parentInterfaces = ArrayUtil.addAll(searchInterface.getInterfaces()); + } + searchInterfaces = parentInterfaces.length == 0 ? null : parentInterfaces; } - searchType = withSuperClassMethods ? searchType.getSuperclass() : null; } - return allMethods; } From 1aeb56564f38826d575e63e4e52298b9784c484e Mon Sep 17 00:00:00 2001 From: jiazhengquan <2466896229@qq.com> Date: Wed, 9 Mar 2022 11:25:52 +0800 Subject: [PATCH 02/24] =?UTF-8?q?=E9=92=88=E5=AF=B9issue#I4WUWR=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/hutool/core/util/ReflectUtil.java | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java index 4f9ded9d2..dbad1e91d 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java @@ -663,34 +663,34 @@ public class ReflectUtil { Assert.notNull(beanClass); Method[] allMethods = null; - Class[] searchInterfaces = null, parentInterfaces = null; Class searchType = beanClass; + Class[] tempInterfaceType, interfaceType; Method[] declaredMethods; - while (searchType != null || searchInterfaces != null) { - if (searchType != null) { + if (searchType.isInterface()) { + allMethods = searchType.getDeclaredMethods(); + interfaceType = searchType.getInterfaces(); + while (ArrayUtil.isNotEmpty(interfaceType)) { + tempInterfaceType = interfaceType; + for (int i = 0; i < tempInterfaceType.length; i++) { + Class temp = tempInterfaceType[i]; + allMethods = ArrayUtil.append(allMethods, temp.getDeclaredMethods()); + if (i == 0 || ArrayUtil.isEmpty(interfaceType)) { + interfaceType = temp.getInterfaces(); + } else { + interfaceType = ArrayUtil.append(interfaceType, temp.getInterfaces()); + } + } + } + } else { + while (searchType != null) { declaredMethods = searchType.getDeclaredMethods(); if (null == allMethods) { allMethods = declaredMethods; } else { allMethods = ArrayUtil.append(allMethods, declaredMethods); } - Class[] interfaces = searchType.getInterfaces(); - for (Class element : interfaces) { - allMethods = ArrayUtil.append(allMethods, element.getDeclaredMethods()); - parentInterfaces = ArrayUtil.addAll(element.getInterfaces()); - } - searchInterfaces = parentInterfaces; - Class[] classes = searchInterfaces.length == 0 ? null : searchInterfaces; - searchInterfaces = withSuperClassMethods ? classes : null; searchType = withSuperClassMethods ? searchType.getSuperclass() : null; } - if (searchInterfaces != null) { - for (Class searchInterface : searchInterfaces) { - allMethods = ArrayUtil.append(allMethods, searchInterface.getDeclaredMethods()); - parentInterfaces = ArrayUtil.addAll(searchInterface.getInterfaces()); - } - searchInterfaces = parentInterfaces.length == 0 ? null : parentInterfaces; - } } return allMethods; } From 10d5cabf976bba2c4eba3b66d6dbda080baffd6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9F=A9=E5=B8=85?= Date: Wed, 9 Mar 2022 15:48:31 +0800 Subject: [PATCH 03/24] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=A4=87=E7=94=A8?= =?UTF-8?q?=E6=96=87=E6=A1=A3=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README-EN.md | 2 ++ README.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/README-EN.md b/README-EN.md index 05475e644..7456a08f7 100644 --- a/README-EN.md +++ b/README-EN.md @@ -111,6 +111,8 @@ Each module can be introduced individually, or all modules can be introduced by [📘Chinese documentation](https://www.hutool.cn/docs/) +[📘Chinese back-up documentation](https://plus.hutool.cn/docs/#/) + [📙API](https://apidoc.gitee.com/dromara/hutool/) [🎬Video](https://www.bilibili.com/video/BV1bQ4y1M7d9?p=2) diff --git a/README.md b/README.md index f8834f52a..409e4e22d 100644 --- a/README.md +++ b/README.md @@ -107,6 +107,8 @@ Hutool的存在就是为了减少代码搜索成本,避免网络上参差不 [📘中文文档](https://www.hutool.cn/docs/) +[📘中文备用文档](https://plus.hutool.cn/docs/#/) + [📙参考API](https://apidoc.gitee.com/dromara/hutool/) [🎬视频介绍](https://www.bilibili.com/video/BV1bQ4y1M7d9?p=2) From aec10a7714a72ee038a9fa497e4e3726cfff1fc4 Mon Sep 17 00:00:00 2001 From: gxz <514190950@qq.com> Date: Wed, 9 Mar 2022 17:03:17 +0800 Subject: [PATCH 04/24] =?UTF-8?q?=E4=BF=AE=E6=94=B9Bug:=E5=BD=93FileCopier?= =?UTF-8?q?=E7=9A=84=E7=9B=AE=E6=A0=87=E6=96=87=E4=BB=B6=E6=98=AF=E7=9B=B8?= =?UTF-8?q?=E5=AF=B9=E8=B7=AF=E5=BE=84=E7=9A=84=E6=97=B6=E5=80=99=EF=BC=8C?= =?UTF-8?q?=E5=A4=8D=E5=88=B6=E4=BC=9A=E5=87=BA=E7=8E=B0=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/hutool/core/io/file/FileCopier.java | 2 +- .../cn/hutool/core/io/FileCopierTest.java | 20 ++++++++++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/io/file/FileCopier.java b/hutool-core/src/main/java/cn/hutool/core/io/file/FileCopier.java index fbdc4938f..6f133f2e3 100644 --- a/hutool-core/src/main/java/cn/hutool/core/io/file/FileCopier.java +++ b/hutool-core/src/main/java/cn/hutool/core/io/file/FileCopier.java @@ -268,7 +268,7 @@ public class FileCopier extends SrcToDestCopier{ }else { //路径不存在则创建父目录 //noinspection ResultOfMethodCallIgnored - dest.getParentFile().mkdirs(); + dest.getAbsoluteFile().getParentFile().mkdirs(); } final ArrayList optionList = new ArrayList<>(2); diff --git a/hutool-core/src/test/java/cn/hutool/core/io/FileCopierTest.java b/hutool-core/src/test/java/cn/hutool/core/io/FileCopierTest.java index 80c50288d..97f702638 100644 --- a/hutool-core/src/test/java/cn/hutool/core/io/FileCopierTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/io/FileCopierTest.java @@ -5,41 +5,51 @@ import org.junit.Test; import cn.hutool.core.io.file.FileCopier; +import java.io.File; + /** * 文件拷贝单元测试 * @author Looly * */ public class FileCopierTest { - + @Test @Ignore public void dirCopyTest() { FileCopier copier = FileCopier.create("D:\\Java", "e:/eclipse/eclipse2.zip"); copier.copy(); } - + @Test @Ignore public void dirCopyTest2() { //测试带.的文件夹复制 FileCopier copier = FileCopier.create("D:\\workspace\\java\\.metadata", "D:\\workspace\\java\\.metadata\\temp"); copier.copy(); - + FileUtil.copy("D:\\workspace\\java\\looly\\hutool\\.git", "D:\\workspace\\java\\temp", true); } - + @Test(expected = IORuntimeException.class) public void dirCopySubTest() { //测试父目录复制到子目录报错 FileCopier copier = FileCopier.create("D:\\workspace\\java\\.metadata", "D:\\workspace\\java\\.metadata\\temp"); copier.copy(); } - + @Test @Ignore public void copyFileToDirTest() { FileCopier copier = FileCopier.create("d:/GReen_Soft/XshellXftpPortable.zip", "c:/hp/"); copier.copy(); } + + @Test + @Ignore + public void copyFileByRelativePath(){ + // 当复制的目标文件位置是相对路径的时候可以通过 + FileCopier copier = FileCopier.create(new File("pom.xml"),new File("aaa.txt")); + copier.copy(); + } } From 206de3bad0493dbe699065aa6ce2855bfa107368 Mon Sep 17 00:00:00 2001 From: MCP Date: Thu, 10 Mar 2022 15:45:43 +0800 Subject: [PATCH 05/24] =?UTF-8?q?=E9=98=BF=E6=8B=89=E4=BC=AF=E6=95=B0?= =?UTF-8?q?=E5=AD=97=E8=BD=AC=E6=8D=A2=E6=88=90=E4=B8=AD=E6=96=87=E5=AF=B9?= =?UTF-8?q?=E5=8F=91=E7=A5=A8=E7=A5=A8=E9=9D=A2=E9=87=91=E9=A2=9D=E8=BD=AC?= =?UTF-8?q?=E6=8D=A2=E7=9A=84=E6=89=A9=E5=B1=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/convert/NumberChineseFormatter.java | 107 +++++++++++------- 1 file changed, 64 insertions(+), 43 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/NumberChineseFormatter.java b/hutool-core/src/main/java/cn/hutool/core/convert/NumberChineseFormatter.java index 2a315d38d..5f8a7b52f 100644 --- a/hutool-core/src/main/java/cn/hutool/core/convert/NumberChineseFormatter.java +++ b/hutool-core/src/main/java/cn/hutool/core/convert/NumberChineseFormatter.java @@ -40,7 +40,6 @@ public class NumberChineseFormatter { new ChineseUnit('亿', 1_0000_0000, true), }; - /** * 阿拉伯数字转换成中文,小数点后四舍五入保留两位. 使用于整数、小数的转换. * @@ -53,15 +52,25 @@ public class NumberChineseFormatter { } /** - * 阿拉伯数字转换成中文,小数点后四舍五入保留两位. 使用于整数、小数的转换. + * 阿拉伯数字转换成中文. + * + *

主要是对发票票面金额转换的扩展 + *

如:-12.32 + *

发票票面转换为:(负数)壹拾贰圆叁角贰分 + *

而非:负壹拾贰元叁角贰分 + *

共两点不同:1、(负数) 而非 负;2、圆 而非 元 + * 2022/3/9 * * @param amount 数字 * @param isUseTraditional 是否使用繁体 - * @param isMoneyMode 是否为金额模式 - * @return 中文 + * @param isMoneyMode 是否金额模式 + * @param negativeName 负号转换名称 如:负、(负数) + * @param unitName 单位名称 如:元、圆 + * @return java.lang.String + * @author machuanpeng */ - public static String format(double amount, boolean isUseTraditional, boolean isMoneyMode) { - if(0 == amount){ + public static String format(double amount, boolean isUseTraditional, boolean isMoneyMode, String negativeName, String unitName) { + if (0 == amount) { return "零"; } Assert.checkBetween(amount, -99_9999_9999_9999.99, 99_9999_9999_9999.99, @@ -71,7 +80,7 @@ public class NumberChineseFormatter { // 负数 if (amount < 0) { - chineseStr.append("负"); + chineseStr.append(StrUtil.isNullOrUndefined(negativeName) ? "负" : negativeName); amount = -amount; } @@ -82,44 +91,44 @@ public class NumberChineseFormatter { yuan = yuan / 10; // 元 - if(false == isMoneyMode || 0 != yuan){ + if (false == isMoneyMode || 0 != yuan) { // 金额模式下,无需“零元” chineseStr.append(longToChinese(yuan, isUseTraditional)); - if(isMoneyMode){ - chineseStr.append("元"); + if (isMoneyMode) { + chineseStr.append(StrUtil.isNullOrUndefined(unitName) ? "元" : unitName); } } - if(0 == jiao && 0 == fen){ + if (0 == jiao && 0 == fen) { //无小数部分的金额结尾 - if(isMoneyMode){ + if (isMoneyMode) { chineseStr.append("整"); } return chineseStr.toString(); } // 小数部分 - if(false == isMoneyMode){ + if (false == isMoneyMode) { chineseStr.append("点"); } // 角 - if(0 == yuan && 0 == jiao){ + if (0 == yuan && 0 == jiao) { // 元和角都为0时,只有非金额模式下补“零” - if(false == isMoneyMode){ + if (false == isMoneyMode) { chineseStr.append("零"); } - }else{ + } else { chineseStr.append(numberToChinese(jiao, isUseTraditional)); - if(isMoneyMode && 0 != jiao){ + if (isMoneyMode && 0 != jiao) { chineseStr.append("角"); } } // 分 - if(0 != fen){ + if (0 != fen) { chineseStr.append(numberToChinese(fen, isUseTraditional)); - if(isMoneyMode){ + if (isMoneyMode) { chineseStr.append("分"); } } @@ -127,16 +136,28 @@ public class NumberChineseFormatter { return chineseStr.toString(); } + /** + * 阿拉伯数字转换成中文,小数点后四舍五入保留两位. 使用于整数、小数的转换. + * + * @param amount 数字 + * @param isUseTraditional 是否使用繁体 + * @param isMoneyMode 是否为金额模式 + * @return 中文 + */ + public static String format(double amount, boolean isUseTraditional, boolean isMoneyMode) { + return format(amount, isUseTraditional, isMoneyMode, "负", "元"); + } + /** * 阿拉伯数字(支持正负整数)转换成中文 * - * @param amount 数字 + * @param amount 数字 * @param isUseTraditional 是否使用繁体 * @return 中文 * @since 5.7.17 */ - public static String format(long amount, boolean isUseTraditional){ - if(0 == amount){ + public static String format(long amount, boolean isUseTraditional) { + if (0 == amount) { return "零"; } Assert.checkBetween(amount, -99_9999_9999_9999.99, 99_9999_9999_9999.99, @@ -179,16 +200,16 @@ public class NumberChineseFormatter { * 格式化-999~999之间的数字
* 这个方法显示10~19以下的数字时使用"十一"而非"一十一"。 * - * @param amount 数字 + * @param amount 数字 * @param isUseTraditional 是否使用繁体 * @return 中文 * @since 5.7.17 */ - public static String formatThousand(int amount, boolean isUseTraditional){ + public static String formatThousand(int amount, boolean isUseTraditional) { Assert.checkBetween(amount, -999, 999, "Number support only: (-999 ~ 999)!"); final String chinese = thousandToChinese(amount, isUseTraditional); - if(amount < 20 && amount >= 10){ + if (amount < 20 && amount >= 10) { // "十一"而非"一十一" return chinese.substring(1); } @@ -218,7 +239,7 @@ public class NumberChineseFormatter { * @return 中文 */ private static String longToChinese(long amount, boolean isUseTraditional) { - if(0 == amount){ + if (0 == amount) { return "零"; } @@ -235,11 +256,11 @@ public class NumberChineseFormatter { // 千 partValue = parts[0]; - if(partValue > 0){ + if (partValue > 0) { partChinese = thousandToChinese(partValue, isUseTraditional); chineseStr.insert(0, partChinese); - if(partValue < 1000){ + if (partValue < 1000) { // 和万位之间空0,则补零,如一万零三百 addPreZero(chineseStr); } @@ -247,26 +268,26 @@ public class NumberChineseFormatter { // 万 partValue = parts[1]; - if(partValue > 0){ - if((partValue % 10 == 0 && parts[0] > 0)){ + if (partValue > 0) { + if ((partValue % 10 == 0 && parts[0] > 0)) { // 如果"万"的个位是0,则补零,如十万零八千 addPreZero(chineseStr); } partChinese = thousandToChinese(partValue, isUseTraditional); chineseStr.insert(0, partChinese + "万"); - if(partValue < 1000){ + if (partValue < 1000) { // 和亿位之间空0,则补零,如一亿零三百万 addPreZero(chineseStr); } - } else{ + } else { addPreZero(chineseStr); } // 亿 partValue = parts[2]; - if(partValue > 0){ - if((partValue % 10 == 0 && parts[1] > 0)){ + if (partValue > 0) { + if ((partValue % 10 == 0 && parts[1] > 0)) { // 如果"万"的个位是0,则补零,如十万零八千 addPreZero(chineseStr); } @@ -274,25 +295,25 @@ public class NumberChineseFormatter { partChinese = thousandToChinese(partValue, isUseTraditional); chineseStr.insert(0, partChinese + "亿"); - if(partValue < 1000){ + if (partValue < 1000) { // 和万亿位之间空0,则补零,如一万亿零三百亿 addPreZero(chineseStr); } - } else{ + } else { addPreZero(chineseStr); } // 万亿 partValue = parts[3]; - if(partValue > 0){ - if(parts[2] == 0){ + if (partValue > 0) { + if (parts[2] == 0) { chineseStr.insert(0, "亿"); } partChinese = thousandToChinese(partValue, isUseTraditional); chineseStr.insert(0, partChinese + "万"); } - if(StrUtil.isNotEmpty(chineseStr) && '零' == chineseStr.charAt(0)){ + if (StrUtil.isNotEmpty(chineseStr) && '零' == chineseStr.charAt(0)) { return chineseStr.substring(1); } @@ -386,7 +407,7 @@ public class NumberChineseFormatter { } else { // 非节单位,和单位前的单数字组合为值 int unitNumber = number; - if(0 == number && 0 == i){ + if (0 == number && 0 == i) { // issue#1726,对于单位开头的数组,默认赋予1 // 十二 -> 一十二 // 百二 -> 一百二 @@ -502,12 +523,12 @@ public class NumberChineseFormatter { } } - private static void addPreZero(StringBuilder chineseStr){ - if(StrUtil.isEmpty(chineseStr)){ + private static void addPreZero(StringBuilder chineseStr) { + if (StrUtil.isEmpty(chineseStr)) { return; } final char c = chineseStr.charAt(0); - if('零' != c){ + if ('零' != c) { chineseStr.insert(0, '零'); } } From 04ae795ce651589223a85e89246bc9bd08c82504 Mon Sep 17 00:00:00 2001 From: jiazhengquan <2466896229@qq.com> Date: Fri, 11 Mar 2022 09:50:37 +0800 Subject: [PATCH 06/24] =?UTF-8?q?=E9=92=88=E5=AF=B9issue#I4X9TT=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hutool-core/src/main/java/cn/hutool/core/text/NamingCase.java | 2 +- hutool-core/src/test/java/cn/hutool/core/util/StrUtilTest.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/text/NamingCase.java b/hutool-core/src/main/java/cn/hutool/core/text/NamingCase.java index a35d78f1e..a5c7a5d52 100644 --- a/hutool-core/src/main/java/cn/hutool/core/text/NamingCase.java +++ b/hutool-core/src/main/java/cn/hutool/core/text/NamingCase.java @@ -97,7 +97,7 @@ public class NamingCase { // 后一个为大写,按照专有名词对待,如aBC -> a_BC } else { //前一个为大写 - if (null == nextChar || Character.isLowerCase(nextChar)) { + if (null != nextChar && Character.isLowerCase(nextChar)) { // 普通首字母大写,如ABcc -> A_bcc sb.append(symbol); c = Character.toLowerCase(c); diff --git a/hutool-core/src/test/java/cn/hutool/core/util/StrUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/StrUtilTest.java index ee1e1da50..2dcef2850 100644 --- a/hutool-core/src/test/java/cn/hutool/core/util/StrUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/util/StrUtilTest.java @@ -422,6 +422,7 @@ public class StrUtilTest { .set("H2", "H2") .set("H#case", "H#case") .set("PNLabel", "PN_label") + .set("DEPT_NAME","DEPT_NAME") .forEach((key, value) -> Assert.assertEquals(value, StrUtil.toUnderlineCase(key))); } From 78f886075eb45b1fdeca88cc0ace0f9aefcc4ff5 Mon Sep 17 00:00:00 2001 From: jiazhengquan <2466896229@qq.com> Date: Fri, 11 Mar 2022 17:51:09 +0800 Subject: [PATCH 07/24] =?UTF-8?q?issue=20#I4XG4L=20=E5=88=9D=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/hutool/core/util/ArrayUtil.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ArrayUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ArrayUtil.java index 8c948e8a9..01e4ea721 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/ArrayUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/ArrayUtil.java @@ -368,6 +368,43 @@ public class ArrayUtil extends PrimitiveArrayUtil { } } + /** + * 将元素值设置为数组的某个位置,根据元素顺序添加
+ * 当给定的index大于数组长度,则追加 + * + * @param 数组元素类型 + * @param buffer 已有数组 + * @param index 位置,大于长度追加,否则替换 + * @param values 新值 + * @return 新数组或原有数组 + */ + public static T[] replace(T[] buffer, int index, T... values) { + return index == 0 ? values : replaceBy(buffer, index, values); + } + + /** + * 将元素值设置为数组的某个位置,根据元素顺序添加
+ * 当给定的index大于数组长度,则追加 + * + * @param 数组元素类型 + * @param buffer 已有数组 + * @param index 位置,大于长度追加,否则替换 + * @param values 新值 + * @return 新数组或原有数组 + */ + public static T[] replaceBy(T[] buffer, int index, T... values) { + if (index < buffer.length && buffer.length - index - 1 >= values.length) { + for (int i = index; i < values.length; i++) { + Array.set(buffer, index, values[i]); + } + return buffer; + } else { + final T[] result = (T[]) Array.newInstance(buffer.getClass().getComponentType(), buffer.length - index - 1); + System.arraycopy(buffer, 0, result, 0, buffer.length - index - 1); + return append(result, values); + } + } + /** * 将新元素插入到到已有数组中的某个位置
* 添加新元素会生成一个新的数组,不影响原数组
From 55c9b0c37a6f124f3bd97e15f357de84cc2d7a36 Mon Sep 17 00:00:00 2001 From: jcsun <80867809@qq.com> Date: Sun, 13 Mar 2022 18:14:36 +0800 Subject: [PATCH 08/24] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20Spring=20Task?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../extra/spring/config/SpringCronConfig.java | 31 +++++++++++++++++++ .../main/resources/META-INF/spring.factories | 3 +- 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 hutool-extra/src/main/java/cn/hutool/extra/spring/config/SpringCronConfig.java diff --git a/hutool-extra/src/main/java/cn/hutool/extra/spring/config/SpringCronConfig.java b/hutool-extra/src/main/java/cn/hutool/extra/spring/config/SpringCronConfig.java new file mode 100644 index 000000000..1f31d0832 --- /dev/null +++ b/hutool-extra/src/main/java/cn/hutool/extra/spring/config/SpringCronConfig.java @@ -0,0 +1,31 @@ +package cn.hutool.extra.spring.config; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.TaskScheduler; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; + +/** + * 可自行配置任务线程池, 修改默认参数 + * + * @author JC + * @date 03/13 + */ +@Configuration +@EnableScheduling +public class SpringCronConfig { + @Bean + @ConditionalOnMissingBean(value = TaskScheduler.class) + public TaskScheduler taskScheduler() { + ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); + // 任务线程池初始化 + scheduler.setThreadNamePrefix("TaskScheduler-"); + scheduler.setPoolSize(Runtime.getRuntime().availableProcessors() / 3 + 1); + + // 保证能立刻丢弃运行中的任务 + scheduler.setRemoveOnCancelPolicy(true); + return scheduler; + } +} diff --git a/hutool-extra/src/main/resources/META-INF/spring.factories b/hutool-extra/src/main/resources/META-INF/spring.factories index 7b08ebdf8..06d39a3ec 100644 --- a/hutool-extra/src/main/resources/META-INF/spring.factories +++ b/hutool-extra/src/main/resources/META-INF/spring.factories @@ -1,3 +1,4 @@ # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ -cn.hutool.extra.spring.SpringUtil \ No newline at end of file +cn.hutool.extra.spring.SpringUtil,\ +cn.hutool.extra.spring.config.SpringCronConfig From c73ea53f8f504f0d6e46f7d5c308040158bb090d Mon Sep 17 00:00:00 2001 From: jcsun <80867809@qq.com> Date: Sun, 13 Mar 2022 18:30:34 +0800 Subject: [PATCH 09/24] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20=E5=8A=A8=E6=80=81?= =?UTF-8?q?=E5=AE=9A=E6=97=B6=E4=BB=BB=E5=8A=A1=20=E5=B7=A5=E5=85=B7?= =?UTF-8?q?=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hutool/extra/spring/SpringCronUtil.java | 157 ++++++++++++++++++ .../main/resources/META-INF/spring.factories | 3 +- 2 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 hutool-extra/src/main/java/cn/hutool/extra/spring/SpringCronUtil.java diff --git a/hutool-extra/src/main/java/cn/hutool/extra/spring/SpringCronUtil.java b/hutool-extra/src/main/java/cn/hutool/extra/spring/SpringCronUtil.java new file mode 100644 index 000000000..01947029a --- /dev/null +++ b/hutool-extra/src/main/java/cn/hutool/extra/spring/SpringCronUtil.java @@ -0,0 +1,157 @@ +package cn.hutool.extra.spring; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.IdUtil; +import org.springframework.scheduling.TaskScheduler; +import org.springframework.scheduling.support.CronTrigger; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ScheduledFuture; + +/** + * Spring 动态定时任务封装 + *

    + *
  1. 创建定时任务
  2. + *
  3. 修改定时任务
  4. + *
  5. 取消定时任务
  6. + *
  7. 高级操作
  8. + *
+ * + * @author JC + * @date 03/13 + */ +@Component +public class SpringCronUtil { + /** + * 任务调度器 + */ + private static TaskScheduler taskScheduler; + + /** + * ID 与 Future 绑定 + */ + private static final Map> TASK_FUTURE = MapUtil.newConcurrentHashMap(); + + /** + * ID 与 Runnable 绑定 + */ + private static final Map TASK_RUNNABLE = MapUtil.newConcurrentHashMap(); + + /** + * 加入定时任务 + * + * @param task 任务 + * @param expression 定时任务执行时间的cron表达式 + * @return 定时任务ID + */ + public static String schedule(Runnable task, String expression) { + String id = IdUtil.fastUUID(); + return schedule(id, task, expression); + } + + /** + * 加入定时任务 + * + * @param id 定时任务ID + * @param expression 定时任务执行时间的cron表达式 + * @param task 任务 + * @return 定时任务ID + */ + public static String schedule(Serializable id, Runnable task, String expression) { + ScheduledFuture schedule = taskScheduler.schedule(task, new CronTrigger(expression)); + TASK_FUTURE.put(id, schedule); + TASK_RUNNABLE.put(id, task); + return id.toString(); + } + + /** + * 修改定时任务 + * + * @param id 定时任务ID + * @param expression 定时任务执行时间的cron表达式 + * @return 是否修改成功,{@code false}表示未找到对应ID的任务 + */ + public static boolean update(Serializable id, String expression) { + if (!TASK_FUTURE.containsKey(id)) { + return false; + } + ScheduledFuture future = TASK_FUTURE.get(id); + if (future == null) { + return false; + } + future.cancel(true); + schedule(id, TASK_RUNNABLE.get(id), expression); + return true; + } + + /** + * 移除任务 + * + * @param schedulerId 任务ID + * @return 是否移除成功,{@code false}表示未找到对应ID的任务 + */ + public static boolean cancel(Serializable schedulerId) { + ScheduledFuture future = getScheduledFuture(schedulerId); + if (future == null) { + return false; + } + boolean cancel = future.cancel(false); + if (cancel) { + TASK_FUTURE.remove(schedulerId); + TASK_RUNNABLE.remove(schedulerId); + } + return cancel; + } + + @Resource + public void setTaskScheduler(TaskScheduler taskScheduler) { + SpringCronUtil.taskScheduler = taskScheduler; + } + + /** + * @return 获得Scheduler对象 + */ + public static TaskScheduler getScheduler() { + return taskScheduler; + } + + /** + * 可在项目中 进行细粒度控制 + * + * @return 获得ScheduledFuture对象 + */ + private static ScheduledFuture getScheduledFuture(Serializable id) { + return TASK_FUTURE.get(id); + } + + /** + * 获得当前运行的所有任务 + * + * @return 所有任务 + */ + public static List getAllTask() { + if (CollUtil.isNotEmpty(TASK_FUTURE.keySet())) { + return new ArrayList<>(TASK_FUTURE.keySet()); + } + return new ArrayList<>(); + } + + /** + * 取消所有的任务 + */ + public static void destroy() { + for (ScheduledFuture future : TASK_FUTURE.values()) { + if (future != null) { + future.cancel(true); + } + } + TASK_FUTURE.clear(); + TASK_RUNNABLE.clear(); + } +} diff --git a/hutool-extra/src/main/resources/META-INF/spring.factories b/hutool-extra/src/main/resources/META-INF/spring.factories index 06d39a3ec..0f0032ef8 100644 --- a/hutool-extra/src/main/resources/META-INF/spring.factories +++ b/hutool-extra/src/main/resources/META-INF/spring.factories @@ -1,4 +1,5 @@ # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ cn.hutool.extra.spring.SpringUtil,\ -cn.hutool.extra.spring.config.SpringCronConfig +cn.hutool.extra.spring.config.SpringCronConfig,\ +cn.hutool.extra.spring.SpringCronUtil From 57212bfdbac019b6e3eb050271ca6ed973585580 Mon Sep 17 00:00:00 2001 From: jcsun <80867809@qq.com> Date: Sun, 13 Mar 2022 18:40:13 +0800 Subject: [PATCH 10/24] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20junit=20=E6=B5=8B?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/hutool/extra/spring/SpringCronUtil.java | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/hutool-extra/src/main/java/cn/hutool/extra/spring/SpringCronUtil.java b/hutool-extra/src/main/java/cn/hutool/extra/spring/SpringCronUtil.java index 01947029a..0599a74fc 100644 --- a/hutool-extra/src/main/java/cn/hutool/extra/spring/SpringCronUtil.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/spring/SpringCronUtil.java @@ -97,10 +97,10 @@ public class SpringCronUtil { * @return 是否移除成功,{@code false}表示未找到对应ID的任务 */ public static boolean cancel(Serializable schedulerId) { - ScheduledFuture future = getScheduledFuture(schedulerId); - if (future == null) { + if (!TASK_FUTURE.containsKey(schedulerId)) { return false; } + ScheduledFuture future = TASK_FUTURE.get(schedulerId); boolean cancel = future.cancel(false); if (cancel) { TASK_FUTURE.remove(schedulerId); @@ -121,15 +121,6 @@ public class SpringCronUtil { return taskScheduler; } - /** - * 可在项目中 进行细粒度控制 - * - * @return 获得ScheduledFuture对象 - */ - private static ScheduledFuture getScheduledFuture(Serializable id) { - return TASK_FUTURE.get(id); - } - /** * 获得当前运行的所有任务 * From 0fee267e4a2d2959b390fc59dd707cf6e3d05bd3 Mon Sep 17 00:00:00 2001 From: jcsun <80867809@qq.com> Date: Sun, 13 Mar 2022 18:40:21 +0800 Subject: [PATCH 11/24] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20junit=20=E6=B5=8B?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../extra/spring/SpringCronUtilTest.java | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 hutool-extra/src/test/java/cn/hutool/extra/spring/SpringCronUtilTest.java diff --git a/hutool-extra/src/test/java/cn/hutool/extra/spring/SpringCronUtilTest.java b/hutool-extra/src/test/java/cn/hutool/extra/spring/SpringCronUtilTest.java new file mode 100644 index 000000000..a3eb1e615 --- /dev/null +++ b/hutool-extra/src/test/java/cn/hutool/extra/spring/SpringCronUtilTest.java @@ -0,0 +1,94 @@ +package cn.hutool.extra.spring; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.extra.spring.config.SpringCronConfig; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.scheduling.TaskScheduler; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.io.Serializable; +import java.time.Duration; +import java.time.Instant; +import java.util.List; + +/** + * @author JC + * @date 03/13 + */ +@Slf4j +@RunWith(SpringJUnit4ClassRunner.class) +@SpringBootTest(classes = {SpringCronConfig.class, SpringCronUtil.class}) +public class SpringCronUtilTest { + /** + * 创建一个定时任务 + * 观察日志可进行验证 + */ + @Test + public void registerTask() { + String ID1 = SpringCronUtil.schedule(this::task, "0/1 * * * * ?"); + String ID2 = SpringCronUtil.schedule(888, this::task, "0/1 * * * * ?"); + log.info("taskId: {},{}", ID1, ID2); + } + + /** + * 修改一个定时任务 + */ + @Test + @SneakyThrows + public void updateTask() { + SpringCronUtil.schedule(888, this::task, "0/1 * * * * ?"); + Thread.sleep(5000); + boolean update = SpringCronUtil.update(888, "0/5 * * * * ?"); + log.info("update task result: {}", update); + } + + /** + * 取消一个定时任务 + */ + @Test + @SneakyThrows + public void cancelTask() { + SpringCronUtil.schedule(888, this::task, "0/1 * * * * ?"); + Thread.sleep(5000); + boolean cancel = SpringCronUtil.cancel(888); + log.info("cancel task result: {}", cancel); + } + + /** + * 高级用法 + * 参考: + */ + @Test + public void senior() { + TaskScheduler scheduler = SpringCronUtil.getScheduler(); + // 给定时间 开始, 间隔时间.. + scheduler.scheduleAtFixedRate(this::task, Instant.now(), Duration.ofMinutes(10)); + // ... + } + + /** + * 取消全部定时任务 + * 查看当前所有的任务 + */ + @After + @SneakyThrows + public void cancelAll() { + Thread.sleep(10000); + List allTask = SpringCronUtil.getAllTask(); + log.info("allTask: {}", allTask); + + SpringCronUtil.destroy(); + + allTask = SpringCronUtil.getAllTask(); + log.info("allTask: {}", allTask); + } + + private void task() { + log.info("information only.. (date:{})", DateUtil.now()); + } +} From b730ccf47070986956173fcca25a4e2d90bbe013 Mon Sep 17 00:00:00 2001 From: jcsun <80867809@qq.com> Date: Sun, 13 Mar 2022 18:44:31 +0800 Subject: [PATCH 12/24] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20=E5=8F=82=E8=80=83?= =?UTF-8?q?=E9=93=BE=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/cn/hutool/extra/spring/SpringCronUtil.java | 1 + .../test/java/cn/hutool/extra/spring/SpringCronUtilTest.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/hutool-extra/src/main/java/cn/hutool/extra/spring/SpringCronUtil.java b/hutool-extra/src/main/java/cn/hutool/extra/spring/SpringCronUtil.java index 0599a74fc..29287bdd8 100644 --- a/hutool-extra/src/main/java/cn/hutool/extra/spring/SpringCronUtil.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/spring/SpringCronUtil.java @@ -22,6 +22,7 @@ import java.util.concurrent.ScheduledFuture; *
  • 取消定时任务
  • *
  • 高级操作
  • * + * 参考:Spring doc * * @author JC * @date 03/13 diff --git a/hutool-extra/src/test/java/cn/hutool/extra/spring/SpringCronUtilTest.java b/hutool-extra/src/test/java/cn/hutool/extra/spring/SpringCronUtilTest.java index a3eb1e615..e4959da27 100644 --- a/hutool-extra/src/test/java/cn/hutool/extra/spring/SpringCronUtilTest.java +++ b/hutool-extra/src/test/java/cn/hutool/extra/spring/SpringCronUtilTest.java @@ -61,7 +61,7 @@ public class SpringCronUtilTest { /** * 高级用法 - * 参考: + * 参考:Spring doc */ @Test public void senior() { From c62d4d382acb0b25555e5f1d9a980b787edb2adf Mon Sep 17 00:00:00 2001 From: Husky <2466896229@qq.com> Date: Sun, 13 Mar 2022 23:07:41 +0800 Subject: [PATCH 13/24] =?UTF-8?q?https://gitee.com/dromara/hutool/issues/I?= =?UTF-8?q?4XG4L=E5=88=9D=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/hutool/core/util/ArrayUtil.java | 57 ++++++++------- .../cn/hutool/core/util/ArrayUtilTest.java | 72 ++++++++++++++++--- 2 files changed, 93 insertions(+), 36 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ArrayUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ArrayUtil.java index 01e4ea721..0c67d6522 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/ArrayUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/ArrayUtil.java @@ -369,8 +369,9 @@ public class ArrayUtil extends PrimitiveArrayUtil { } /** - * 将元素值设置为数组的某个位置,根据元素顺序添加
    - * 当给定的index大于数组长度,则追加 + * 将新元素插入到到已有数组中的某个位置
    + * 添加新元素会生成一个新数组或原有数组
    + * 如果插入位置为为负数,那么生成一个由插入元素顺序加已有数组顺序的新数组 * * @param 数组元素类型 * @param buffer 已有数组 @@ -378,31 +379,33 @@ public class ArrayUtil extends PrimitiveArrayUtil { * @param values 新值 * @return 新数组或原有数组 */ + @SuppressWarnings({"unchecked", "SuspiciousSystemArraycopy"}) public static T[] replace(T[] buffer, int index, T... values) { - return index == 0 ? values : replaceBy(buffer, index, values); - } - - /** - * 将元素值设置为数组的某个位置,根据元素顺序添加
    - * 当给定的index大于数组长度,则追加 - * - * @param 数组元素类型 - * @param buffer 已有数组 - * @param index 位置,大于长度追加,否则替换 - * @param values 新值 - * @return 新数组或原有数组 - */ - public static T[] replaceBy(T[] buffer, int index, T... values) { - if (index < buffer.length && buffer.length - index - 1 >= values.length) { - for (int i = index; i < values.length; i++) { - Array.set(buffer, index, values[i]); + if (index < 0) { + return insert(buffer, 0, values); + } + if (isEmpty(buffer) || index == 0 && isNotEmpty(values)) { + return values; + } + if (index >= buffer.length || isEmpty(values)) { + return append(buffer, values); + } + int replaceSpace = buffer.length - index - 1; + if (replaceSpace > values.length) { + for (int i = index - 1; i < values.length; i++) { + Array.set(buffer, i + 1, values[i]); } return buffer; - } else { - final T[] result = (T[]) Array.newInstance(buffer.getClass().getComponentType(), buffer.length - index - 1); - System.arraycopy(buffer, 0, result, 0, buffer.length - index - 1); - return append(result, values); } + int newArrayLength = index + values.length; + final T[] result = (T[]) Array.newInstance(buffer.getClass().getComponentType(), newArrayLength); + System.arraycopy(buffer, 0, result, 0, index); + int valueIndex = 0; + for (int i = index; i < newArrayLength; i++) { + Array.set(result, i, values[valueIndex]); + valueIndex = valueIndex + 1; + } + return result; } /** @@ -1577,7 +1580,7 @@ public class ArrayUtil extends PrimitiveArrayUtil { * @since 4.5.18 */ public static boolean isAllEmpty(Object... args) { - for (Object obj: args) { + for (Object obj : args) { if (false == ObjectUtil.isEmpty(obj)) { return false; } @@ -1795,10 +1798,10 @@ public class ArrayUtil extends PrimitiveArrayUtil { /** * 查找最后一个子数组的开始位置 * - * @param array 数组 + * @param array 数组 * @param endInclude 查找结束的位置(包含) - * @param subArray 子数组 - * @param 数组元素类型 + * @param subArray 子数组 + * @param 数组元素类型 * @return 最后一个子数组的开始位置,即子数字第一个元素在数组中的位置 * @since 5.4.8 */ diff --git a/hutool-core/src/test/java/cn/hutool/core/util/ArrayUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/ArrayUtilTest.java index 3ba18becf..e2ce30b9e 100644 --- a/hutool-core/src/test/java/cn/hutool/core/util/ArrayUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/util/ArrayUtilTest.java @@ -363,7 +363,7 @@ public class ArrayUtilTest { } @Test - public void indexOfSubTest2(){ + public void indexOfSubTest2() { Integer[] a = {0x12, 0x56, 0x34, 0x56, 0x78, 0x9A}; Integer[] b = {0x56, 0x78}; int i = ArrayUtil.indexOfSub(a, b); @@ -401,7 +401,7 @@ public class ArrayUtilTest { } @Test - public void lastIndexOfSubTest2(){ + public void lastIndexOfSubTest2() { Integer[] a = {0x12, 0x56, 0x78, 0x56, 0x21, 0x9A}; Integer[] b = {0x56, 0x78}; int i = ArrayUtil.indexOfSub(a, b); @@ -409,17 +409,17 @@ public class ArrayUtilTest { } @Test - public void reverseTest(){ - int[] a = {1,2,3,4}; + public void reverseTest() { + int[] a = {1, 2, 3, 4}; final int[] reverse = ArrayUtil.reverse(a); - Assert.assertArrayEquals(new int[]{4,3,2,1}, reverse); + Assert.assertArrayEquals(new int[]{4, 3, 2, 1}, reverse); } @Test - public void reverseTest2s(){ - Object[] a = {"1",'2',"3",4}; + public void reverseTest2s() { + Object[] a = {"1", '2', "3", 4}; final Object[] reverse = ArrayUtil.reverse(a); - Assert.assertArrayEquals(new Object[]{4,"3",'2',"1"}, reverse); + Assert.assertArrayEquals(new Object[]{4, "3", '2', "1"}, reverse); } @Test @@ -461,9 +461,63 @@ public class ArrayUtilTest { } @Test - public void getTest(){ + public void getTest() { String[] a = {"a", "b", "c"}; final Object o = ArrayUtil.get(a, -1); Assert.assertEquals("c", o); } + + @Test + public void replaceTest() { + String[] a = {"1", "2", "3", "4"}; + String[] b = {"a", "b", "c"}; + + // 在小于0的位置,-1位置插入,返回b+a + String[] result = ArrayUtil.replace(a, -1, b); + Assert.assertArrayEquals(new String[]{"a", "b", "c", "1", "2", "3", "4"}, result); + + // 在第0个位置插入,即覆盖a,直接返回b + result = ArrayUtil.replace(a, 0, b); + Assert.assertArrayEquals(new String[]{"a", "b", "c"}, result); + + // 在第1个位置插入,即"2"之前 + result = ArrayUtil.replace(a, 1, b); + Assert.assertArrayEquals(new String[]{"1", "a", "b", "c"}, result); + + //上一步测试修改了原数组结构 + String[] c = {"1", "2", "3", "4"}; + String[] d = {"a", "b", "c"}; + + // 在第2个位置插入,即"3"之后 + result = ArrayUtil.replace(c, 2, d); + Assert.assertArrayEquals(new String[]{"1", "2", "a", "b", "c"}, result); + + // 在第3个位置插入,即"4"之后 + result = ArrayUtil.replace(c, 3, d); + Assert.assertArrayEquals(new String[]{"1", "2", "3", "a", "b", "c"}, result); + + // 在第4个位置插入,数组长度为4,在索引4出替换即两个数组相加 + result = ArrayUtil.replace(c, 4, d); + Assert.assertArrayEquals(new String[]{"1", "2", "3", "4", "a", "b", "c"}, result); + + // 在大于3个位置插入,数组长度为4,即两个数组相加 + result = ArrayUtil.replace(c, 5, d); + Assert.assertArrayEquals(new String[]{"1", "2", "3", "4", "a", "b", "c"}, result); + + //上一步测试修改了原数组结构 + String[] e = null; + String[] f = {"a", "b", "c"}; + + // e为null 返回 f + result = ArrayUtil.replace(e, -1, f); + Assert.assertArrayEquals(new String[]{"a", "b", "c"}, result); + + //上一步测试修改了原数组结构 + String[] g = {"a", "b", "c"}; + String[] h = null; + + // h为null 返回 g + result = ArrayUtil.replace(g, 0, h); + Assert.assertArrayEquals(new String[]{"a", "b", "c"}, result); + } } From 7acac417e694287870ea6eda36339c0e4fa3f094 Mon Sep 17 00:00:00 2001 From: Husky <2466896229@qq.com> Date: Sun, 13 Mar 2022 23:08:40 +0800 Subject: [PATCH 14/24] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=97=A0=E7=94=A8?= =?UTF-8?q?=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/test/java/cn/hutool/core/util/ArrayUtilTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/hutool-core/src/test/java/cn/hutool/core/util/ArrayUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/ArrayUtilTest.java index e2ce30b9e..442ce4b91 100644 --- a/hutool-core/src/test/java/cn/hutool/core/util/ArrayUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/util/ArrayUtilTest.java @@ -504,7 +504,6 @@ public class ArrayUtilTest { result = ArrayUtil.replace(c, 5, d); Assert.assertArrayEquals(new String[]{"1", "2", "3", "4", "a", "b", "c"}, result); - //上一步测试修改了原数组结构 String[] e = null; String[] f = {"a", "b", "c"}; @@ -512,7 +511,6 @@ public class ArrayUtilTest { result = ArrayUtil.replace(e, -1, f); Assert.assertArrayEquals(new String[]{"a", "b", "c"}, result); - //上一步测试修改了原数组结构 String[] g = {"a", "b", "c"}; String[] h = null; From b541b70062cc71f704c177b16c5177a01a7e9f0a Mon Sep 17 00:00:00 2001 From: Looly Date: Sun, 13 Mar 2022 23:35:58 +0800 Subject: [PATCH 15/24] add method --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dfefa113b..41f61d48a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ # 🚀Changelog ------------------------------------------------------------------------------------------------------------- -# 5.7.23 (2022-03-09) +# 5.7.23 (2022-03-13) ### 🐣新特性 * 【http 】 HttpRequest.form采用TableMap方式(issue#I4W427@Gitee) @@ -11,6 +11,7 @@ * 【crypto 】 增加XXTEA实现(issue#I4WH2X@Gitee) * 【core 】 增加Table实现(issue#2179@Github) * 【core 】 增加UniqueKeySet(issue#I4WUWR@Gitee) +* 【core 】 阿拉伯数字转换成中文对发票票面金额转换的扩展(pr#570@Gitee) * ### 🐞Bug修复 * 【core 】 修复ObjectUtil.hasNull传入null返回true的问题(pr#555@Gitee) From e38922796444015831319a04d01c85cbb4d87aa3 Mon Sep 17 00:00:00 2001 From: Looly Date: Mon, 14 Mar 2022 00:32:35 +0800 Subject: [PATCH 16/24] fix bug and add method --- CHANGELOG.md | 2 + .../core/convert/NumberChineseFormatter.java | 1 + .../java/cn/hutool/core/text/NamingCase.java | 2 +- .../java/cn/hutool/core/util/ArrayUtil.java | 48 ++++++++++--------- .../cn/hutool/core/text/NamingCaseTest.java | 37 +++++++++++--- .../cn/hutool/core/util/ArrayUtilTest.java | 28 +++++------ .../java/cn/hutool/core/util/StrUtilTest.java | 35 -------------- 7 files changed, 71 insertions(+), 82 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41f61d48a..aa4ee93a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,11 +12,13 @@ * 【core 】 增加Table实现(issue#2179@Github) * 【core 】 增加UniqueKeySet(issue#I4WUWR@Gitee) * 【core 】 阿拉伯数字转换成中文对发票票面金额转换的扩展(pr#570@Gitee) +* 【core 】 ArrayUtil增加replace方法(pr#570@Gitee) * ### 🐞Bug修复 * 【core 】 修复ObjectUtil.hasNull传入null返回true的问题(pr#555@Gitee) * 【core 】 修复NumberConverter对数字转换的问题(issue#I4WPF4@Gitee) * 【core 】 修复ReflectUtil.getMethods获取接口方法问题(issue#I4WUWR@Gitee) +* 【core 】 修复NamingCase中大写转换问题(pr#572@Gitee) ------------------------------------------------------------------------------------------------------------- # 5.7.22 (2022-03-01) diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/NumberChineseFormatter.java b/hutool-core/src/main/java/cn/hutool/core/convert/NumberChineseFormatter.java index 5f8a7b52f..6ec3f0e16 100644 --- a/hutool-core/src/main/java/cn/hutool/core/convert/NumberChineseFormatter.java +++ b/hutool-core/src/main/java/cn/hutool/core/convert/NumberChineseFormatter.java @@ -68,6 +68,7 @@ public class NumberChineseFormatter { * @param unitName 单位名称 如:元、圆 * @return java.lang.String * @author machuanpeng + * @since 5.7.23 */ public static String format(double amount, boolean isUseTraditional, boolean isMoneyMode, String negativeName, String unitName) { if (0 == amount) { diff --git a/hutool-core/src/main/java/cn/hutool/core/text/NamingCase.java b/hutool-core/src/main/java/cn/hutool/core/text/NamingCase.java index a5c7a5d52..56eb04c46 100644 --- a/hutool-core/src/main/java/cn/hutool/core/text/NamingCase.java +++ b/hutool-core/src/main/java/cn/hutool/core/text/NamingCase.java @@ -157,7 +157,7 @@ public class NamingCase { * 将连接符方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 * * @param name 转换前的自定义方式命名的字符串 - * @param symbol 连接符 + * @param symbol 原字符串中的连接符连接符 * @return 转换后的驼峰式命名的字符串 * @since 5.7.17 */ diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ArrayUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ArrayUtil.java index 0c67d6522..399fe571d 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/ArrayUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/ArrayUtil.java @@ -375,36 +375,38 @@ public class ArrayUtil extends PrimitiveArrayUtil { * * @param 数组元素类型 * @param buffer 已有数组 - * @param index 位置,大于长度追加,否则替换 + * @param index 位置,大于长度追加,否则替换,<0表示从头部追加 * @param values 新值 * @return 新数组或原有数组 + * @since 5.7.23 */ - @SuppressWarnings({"unchecked", "SuspiciousSystemArraycopy"}) + @SuppressWarnings({"unchecked"}) public static T[] replace(T[] buffer, int index, T... values) { - if (index < 0) { - return insert(buffer, 0, values); - } - if (isEmpty(buffer) || index == 0 && isNotEmpty(values)) { - return values; - } - if (index >= buffer.length || isEmpty(values)) { - return append(buffer, values); - } - int replaceSpace = buffer.length - index - 1; - if (replaceSpace > values.length) { - for (int i = index - 1; i < values.length; i++) { - Array.set(buffer, i + 1, values[i]); - } + if(isEmpty(values)){ return buffer; } - int newArrayLength = index + values.length; - final T[] result = (T[]) Array.newInstance(buffer.getClass().getComponentType(), newArrayLength); - System.arraycopy(buffer, 0, result, 0, index); - int valueIndex = 0; - for (int i = index; i < newArrayLength; i++) { - Array.set(result, i, values[valueIndex]); - valueIndex = valueIndex + 1; + if(isEmpty(buffer)){ + return values; } + if (index < 0) { + // 从头部追加 + return insert(buffer, 0, values); + } + if (index >= buffer.length) { + // 超出长度,尾部追加 + return append(buffer, values); + } + + if (buffer.length >= values.length + index) { + System.arraycopy(values, 0, buffer, index, values.length); + return buffer; + } + + // 替换长度大于原数组长度,新建数组 + int newArrayLength = index + values.length; + final T[] result = newArray(buffer.getClass().getComponentType(), newArrayLength); + System.arraycopy(buffer, 0, result, 0, index); + System.arraycopy(values, 0, result, index, values.length); return result; } diff --git a/hutool-core/src/test/java/cn/hutool/core/text/NamingCaseTest.java b/hutool-core/src/test/java/cn/hutool/core/text/NamingCaseTest.java index 62545d934..2eec1a248 100644 --- a/hutool-core/src/test/java/cn/hutool/core/text/NamingCaseTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/text/NamingCaseTest.java @@ -1,21 +1,44 @@ package cn.hutool.core.text; import cn.hutool.core.lang.Dict; +import cn.hutool.core.util.CharUtil; import org.junit.Assert; import org.junit.Test; public class NamingCaseTest { + @Test - public void toUnderlineCaseTest(){ - // https://github.com/dromara/hutool/issues/2070 + public void toCamelCaseTest() { Dict.create() - .set("customerNickV2", "customer_nick_v2") - .forEach((key, value) -> Assert.assertEquals(value, NamingCase.toUnderlineCase(key))); + .set("Table_Test_Of_day","tableTestOfDay") + .set("TableTestOfDay","TableTestOfDay") + .set("abc_1d","abc1d") + .forEach((key, value) -> Assert.assertEquals(value, NamingCase.toCamelCase(key))); } @Test - public void toUnderLineCaseTest2(){ - final String wPRunOZTime = NamingCase.toUnderlineCase("wPRunOZTime"); - Assert.assertEquals("w_P_run_OZ_time", wPRunOZTime); + public void toCamelCaseFromDashedTest() { + Dict.create() + .set("Table-Test-Of-day","tableTestOfDay") + .forEach((key, value) -> Assert.assertEquals(value, NamingCase.toCamelCase(key, CharUtil.DASHED))); + } + + @Test + public void toUnderLineCaseTest() { + Dict.create() + .set("Table_Test_Of_day", "table_test_of_day") + .set("_Table_Test_Of_day_", "_table_test_of_day_") + .set("_Table_Test_Of_DAY_", "_table_test_of_DAY_") + .set("_TableTestOfDAYToday", "_table_test_of_DAY_today") + .set("HelloWorld_test", "hello_world_test") + .set("H2", "H2") + .set("H#case", "H#case") + .set("PNLabel", "PN_label") + .set("wPRunOZTime", "w_P_run_OZ_time") + // https://github.com/dromara/hutool/issues/2070 + .set("customerNickV2", "customer_nick_v2") + // https://gitee.com/dromara/hutool/issues/I4X9TT + .set("DEPT_NAME","DEPT_NAME") + .forEach((key, value) -> Assert.assertEquals(value, NamingCase.toUnderlineCase(key))); } } diff --git a/hutool-core/src/test/java/cn/hutool/core/util/ArrayUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/ArrayUtilTest.java index 442ce4b91..92f3603a8 100644 --- a/hutool-core/src/test/java/cn/hutool/core/util/ArrayUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/util/ArrayUtilTest.java @@ -472,36 +472,32 @@ public class ArrayUtilTest { String[] a = {"1", "2", "3", "4"}; String[] b = {"a", "b", "c"}; - // 在小于0的位置,-1位置插入,返回b+a + // 在小于0的位置,-1位置插入,返回b+a,新数组 String[] result = ArrayUtil.replace(a, -1, b); Assert.assertArrayEquals(new String[]{"a", "b", "c", "1", "2", "3", "4"}, result); - // 在第0个位置插入,即覆盖a,直接返回b - result = ArrayUtil.replace(a, 0, b); - Assert.assertArrayEquals(new String[]{"a", "b", "c"}, result); + // 在第0个位置开始替换,返回a + result = ArrayUtil.replace(ArrayUtil.clone(a), 0, b); + Assert.assertArrayEquals(new String[]{"a", "b", "c", "4"}, result); - // 在第1个位置插入,即"2"之前 - result = ArrayUtil.replace(a, 1, b); + // 在第1个位置替换,即"2"开始 + result = ArrayUtil.replace(ArrayUtil.clone(a), 1, b); Assert.assertArrayEquals(new String[]{"1", "a", "b", "c"}, result); - //上一步测试修改了原数组结构 - String[] c = {"1", "2", "3", "4"}; - String[] d = {"a", "b", "c"}; - // 在第2个位置插入,即"3"之后 - result = ArrayUtil.replace(c, 2, d); + result = ArrayUtil.replace(ArrayUtil.clone(a), 2, b); Assert.assertArrayEquals(new String[]{"1", "2", "a", "b", "c"}, result); // 在第3个位置插入,即"4"之后 - result = ArrayUtil.replace(c, 3, d); + result = ArrayUtil.replace(ArrayUtil.clone(a), 3, b); Assert.assertArrayEquals(new String[]{"1", "2", "3", "a", "b", "c"}, result); // 在第4个位置插入,数组长度为4,在索引4出替换即两个数组相加 - result = ArrayUtil.replace(c, 4, d); + result = ArrayUtil.replace(ArrayUtil.clone(a), 4, b); Assert.assertArrayEquals(new String[]{"1", "2", "3", "4", "a", "b", "c"}, result); // 在大于3个位置插入,数组长度为4,即两个数组相加 - result = ArrayUtil.replace(c, 5, d); + result = ArrayUtil.replace(ArrayUtil.clone(a), 5, b); Assert.assertArrayEquals(new String[]{"1", "2", "3", "4", "a", "b", "c"}, result); String[] e = null; @@ -509,13 +505,13 @@ public class ArrayUtilTest { // e为null 返回 f result = ArrayUtil.replace(e, -1, f); - Assert.assertArrayEquals(new String[]{"a", "b", "c"}, result); + Assert.assertArrayEquals(f, result); String[] g = {"a", "b", "c"}; String[] h = null; // h为null 返回 g result = ArrayUtil.replace(g, 0, h); - Assert.assertArrayEquals(new String[]{"a", "b", "c"}, result); + Assert.assertArrayEquals(g, result); } } diff --git a/hutool-core/src/test/java/cn/hutool/core/util/StrUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/StrUtilTest.java index 2dcef2850..514efd089 100644 --- a/hutool-core/src/test/java/cn/hutool/core/util/StrUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/util/StrUtilTest.java @@ -391,41 +391,6 @@ public class StrUtilTest { Assert.assertEquals(text, str); } - @Test - public void toCamelCaseTest() { - String str = "Table_Test_Of_day"; - String result = StrUtil.toCamelCase(str); - Assert.assertEquals("tableTestOfDay", result); - - String str1 = "TableTestOfDay"; - String result1 = StrUtil.toCamelCase(str1); - Assert.assertEquals("TableTestOfDay", result1); - - String abc1d = StrUtil.toCamelCase("abc_1d"); - Assert.assertEquals("abc1d", abc1d); - - - String str2 = "Table-Test-Of-day"; - String result2 = StrUtil.toCamelCase(str2, CharUtil.DASHED); - System.out.println(result2); - Assert.assertEquals("tableTestOfDay", result2); - } - - @Test - public void toUnderLineCaseTest() { - Dict.create() - .set("Table_Test_Of_day", "table_test_of_day") - .set("_Table_Test_Of_day_", "_table_test_of_day_") - .set("_Table_Test_Of_DAY_", "_table_test_of_DAY_") - .set("_TableTestOfDAYToday", "_table_test_of_DAY_today") - .set("HelloWorld_test", "hello_world_test") - .set("H2", "H2") - .set("H#case", "H#case") - .set("PNLabel", "PN_label") - .set("DEPT_NAME","DEPT_NAME") - .forEach((key, value) -> Assert.assertEquals(value, StrUtil.toUnderlineCase(key))); - } - @Test public void containsAnyTest() { //字符 From 911f75e6bdf21df717026b26e6864e0dd1a77bc7 Mon Sep 17 00:00:00 2001 From: Looly Date: Mon, 14 Mar 2022 22:49:32 +0800 Subject: [PATCH 17/24] fix bug --- CHANGELOG.md | 3 ++- hutool-http/src/main/java/cn/hutool/http/HttpRequest.java | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa4ee93a7..03a556481 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ # 🚀Changelog ------------------------------------------------------------------------------------------------------------- -# 5.7.23 (2022-03-13) +# 5.7.23 (2022-03-14) ### 🐣新特性 * 【http 】 HttpRequest.form采用TableMap方式(issue#I4W427@Gitee) @@ -19,6 +19,7 @@ * 【core 】 修复NumberConverter对数字转换的问题(issue#I4WPF4@Gitee) * 【core 】 修复ReflectUtil.getMethods获取接口方法问题(issue#I4WUWR@Gitee) * 【core 】 修复NamingCase中大写转换问题(pr#572@Gitee) +* 【http 】 修复GET重定向时,携带参数问题(issue#2189@Github) ------------------------------------------------------------------------------------------------------------- # 5.7.22 (2022-03-01) diff --git a/hutool-http/src/main/java/cn/hutool/http/HttpRequest.java b/hutool-http/src/main/java/cn/hutool/http/HttpRequest.java index 927c8bd4d..73883f0c7 100644 --- a/hutool-http/src/main/java/cn/hutool/http/HttpRequest.java +++ b/hutool-http/src/main/java/cn/hutool/http/HttpRequest.java @@ -1161,10 +1161,11 @@ public class HttpRequest extends HttpBase { /** * 对于GET请求将参数加到URL中
    - * 此处不对URL中的特殊字符做单独编码 + * 此处不对URL中的特殊字符做单独编码
    + * 对于非rest的GET请求,且处于重定向时,参数丢弃 */ private void urlWithParamIfGet() { - if (Method.GET.equals(method) && false == this.isRest) { + if (Method.GET.equals(method) && false == this.isRest && this.redirectCount > 0) { // 优先使用body形式的参数,不存在使用form if (ArrayUtil.isNotEmpty(this.bodyBytes)) { this.url.getQuery().parse(StrUtil.str(this.bodyBytes, this.charset), this.charset); @@ -1194,7 +1195,7 @@ public class HttpRequest extends HttpBase { if (responseCode != HttpURLConnection.HTTP_OK) { if (HttpStatus.isRedirected(responseCode)) { - setUrl(httpConnection.header(Header.LOCATION)); + setUrl(UrlBuilder.ofHttpWithoutEncode(httpConnection.header(Header.LOCATION))); if (redirectCount < this.maxRedirectCount) { redirectCount++; // 重定向不再走过滤器 From 989f93652fd322e41d67034f40d889dc4d70363c Mon Sep 17 00:00:00 2001 From: Looly Date: Mon, 14 Mar 2022 23:16:25 +0800 Subject: [PATCH 18/24] add config --- CHANGELOG.md | 1 + .../hutool/core/text/csv/CsvBaseReader.java | 2 +- .../cn/hutool/core/text/csv/CsvParser.java | 6 +++--- .../hutool/core/text/csv/CsvReadConfig.java | 21 +++++++++++++++---- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03a556481..6ddda765f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ * 【core 】 增加UniqueKeySet(issue#I4WUWR@Gitee) * 【core 】 阿拉伯数字转换成中文对发票票面金额转换的扩展(pr#570@Gitee) * 【core 】 ArrayUtil增加replace方法(pr#570@Gitee) +* 【core 】 CsvReadConfig增加自定义标题行行号(issue#2180@Github) * ### 🐞Bug修复 * 【core 】 修复ObjectUtil.hasNull传入null返回true的问题(pr#555@Gitee) diff --git a/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvBaseReader.java b/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvBaseReader.java index 84a244e37..2ac2f9186 100644 --- a/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvBaseReader.java +++ b/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvBaseReader.java @@ -177,7 +177,7 @@ public class CsvBaseReader implements Serializable { final CsvParser csvParser = parse(reader); final List rows = new ArrayList<>(); read(csvParser, rows::add); - final List header = config.containsHeader ? csvParser.getHeader() : null; + final List header = config.headerLineNo > -1 ? csvParser.getHeader() : null; return new CsvData(header, rows); } diff --git a/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvParser.java b/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvParser.java index 21d996039..690127f4f 100644 --- a/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvParser.java +++ b/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvParser.java @@ -84,13 +84,13 @@ public final class CsvParser extends ComputeIter implements Closeable, S } /** - * 获取头部字段列表,如果containsHeader设置为false则抛出异常 + * 获取头部字段列表,如果headerLineNo < 0,抛出异常 * * @return 头部列表 * @throws IllegalStateException 如果不解析头部或者没有调用nextRow()方法 */ public List getHeader() { - if (false == config.containsHeader) { + if (config.headerLineNo < 0) { throw new IllegalStateException("No header available - header parsing is disabled"); } if (lineNo < config.beginLineNo) { @@ -152,7 +152,7 @@ public final class CsvParser extends ComputeIter implements Closeable, S } //初始化标题 - if (config.containsHeader && null == header) { + if (lineNo == config.headerLineNo && null == header) { initHeader(currentFields); // 作为标题行后,此行跳过,下一行做为第一行 continue; diff --git a/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvReadConfig.java b/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvReadConfig.java index f9373f8fc..5ad7c7d85 100644 --- a/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvReadConfig.java +++ b/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvReadConfig.java @@ -11,8 +11,8 @@ import java.io.Serializable; public class CsvReadConfig extends CsvConfig implements Serializable { private static final long serialVersionUID = 5396453565371560052L; - /** 是否首行做为标题行,默认false */ - protected boolean containsHeader; + /** 指定标题行号,-1表示无标题行 */ + protected long headerLineNo = -1; /** 是否跳过空白行,默认true */ protected boolean skipEmptyRows = true; /** 每行字段个数不同时是否抛出异常,默认false */ @@ -34,13 +34,26 @@ public class CsvReadConfig extends CsvConfig implements Serializa } /** - * 设置是否首行做为标题行,默认false + * 设置是否首行做为标题行,默认false
    + * 当设置为{@code true}时,默认标题行号是{@link #beginLineNo},{@code false}为-1,表示无行号 * * @param containsHeader 是否首行做为标题行,默认false * @return this + * @see #setHeaderLineNo(long) */ public CsvReadConfig setContainsHeader(boolean containsHeader) { - this.containsHeader = containsHeader; + return setHeaderLineNo(containsHeader ? beginLineNo : -1); + } + + /** + * 设置标题行行号,默认-1,表示无标题行
    + * + * @param headerLineNo 标题行行号,-1表示无标题行 + * @return this + * @since 5.7.23 + */ + public CsvReadConfig setHeaderLineNo(long headerLineNo) { + this.headerLineNo = headerLineNo; return this; } From 1c5e9787339ffb141f6fbe1a626c37ba55c61ddc Mon Sep 17 00:00:00 2001 From: Looly Date: Mon, 14 Mar 2022 23:23:18 +0800 Subject: [PATCH 19/24] fix test --- .../test/java/cn/hutool/core/io/FileCopierTest.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hutool-core/src/test/java/cn/hutool/core/io/FileCopierTest.java b/hutool-core/src/test/java/cn/hutool/core/io/FileCopierTest.java index 80c50288d..4d8403d7e 100644 --- a/hutool-core/src/test/java/cn/hutool/core/io/FileCopierTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/io/FileCopierTest.java @@ -7,35 +7,35 @@ import cn.hutool.core.io.file.FileCopier; /** * 文件拷贝单元测试 - * @author Looly * + * @author Looly */ public class FileCopierTest { - + @Test @Ignore public void dirCopyTest() { FileCopier copier = FileCopier.create("D:\\Java", "e:/eclipse/eclipse2.zip"); copier.copy(); } - + @Test @Ignore public void dirCopyTest2() { //测试带.的文件夹复制 FileCopier copier = FileCopier.create("D:\\workspace\\java\\.metadata", "D:\\workspace\\java\\.metadata\\temp"); copier.copy(); - + FileUtil.copy("D:\\workspace\\java\\looly\\hutool\\.git", "D:\\workspace\\java\\temp", true); } - + @Test(expected = IORuntimeException.class) public void dirCopySubTest() { //测试父目录复制到子目录报错 FileCopier copier = FileCopier.create("D:\\workspace\\java\\.metadata", "D:\\workspace\\java\\.metadata\\temp"); copier.copy(); } - + @Test @Ignore public void copyFileToDirTest() { From e5c253042d75afcd704bf289f104fc9081a1f798 Mon Sep 17 00:00:00 2001 From: Looly Date: Mon, 14 Mar 2022 23:30:28 +0800 Subject: [PATCH 20/24] fix file bug --- CHANGELOG.md | 1 + hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java | 2 +- .../src/main/java/cn/hutool/core/io/file/FileCopier.java | 3 +-- .../src/test/java/cn/hutool/core/io/FileCopierTest.java | 4 ++++ 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ddda765f..3439b0de6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ * 【core 】 修复ReflectUtil.getMethods获取接口方法问题(issue#I4WUWR@Gitee) * 【core 】 修复NamingCase中大写转换问题(pr#572@Gitee) * 【http 】 修复GET重定向时,携带参数问题(issue#2189@Github) +* 【core 】 修复FileUtil、FileCopier相对路径获取父路径错误问题(pr#2188@Github) ------------------------------------------------------------------------------------------------------------- # 5.7.22 (2022-03-01) diff --git a/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java index e0622f7fc..8cd3356f4 100644 --- a/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java @@ -700,7 +700,7 @@ public class FileUtil extends PathUtil { if (null == file) { return null; } - return mkdir(file.getParentFile()); + return mkdir(getParent(file, 1)); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/io/file/FileCopier.java b/hutool-core/src/main/java/cn/hutool/core/io/file/FileCopier.java index 6f133f2e3..3e7df8220 100644 --- a/hutool-core/src/main/java/cn/hutool/core/io/file/FileCopier.java +++ b/hutool-core/src/main/java/cn/hutool/core/io/file/FileCopier.java @@ -267,8 +267,7 @@ public class FileCopier extends SrcToDestCopier{ } }else { //路径不存在则创建父目录 - //noinspection ResultOfMethodCallIgnored - dest.getAbsoluteFile().getParentFile().mkdirs(); + FileUtil.mkParentDirs(dest); } final ArrayList optionList = new ArrayList<>(2); diff --git a/hutool-core/src/test/java/cn/hutool/core/io/FileCopierTest.java b/hutool-core/src/test/java/cn/hutool/core/io/FileCopierTest.java index 6cfaf3763..8419ca29d 100644 --- a/hutool-core/src/test/java/cn/hutool/core/io/FileCopierTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/io/FileCopierTest.java @@ -1,5 +1,6 @@ package cn.hutool.core.io; +import org.junit.Assert; import org.junit.Ignore; import org.junit.Test; @@ -48,8 +49,11 @@ public class FileCopierTest { @Test @Ignore public void copyFileByRelativePath(){ + // https://github.com/dromara/hutool/pull/2188 // 当复制的目标文件位置是相对路径的时候可以通过 FileCopier copier = FileCopier.create(new File("pom.xml"),new File("aaa.txt")); copier.copy(); + final boolean delete = new File("aaa.txt").delete(); + Assert.assertTrue(delete); } } From 652f43efdd9070d08f90a5f580f62463b77c911b Mon Sep 17 00:00:00 2001 From: Looly Date: Tue, 15 Mar 2022 00:07:59 +0800 Subject: [PATCH 21/24] add test --- .../java/cn/hutool/json/IssueI4XFMWTest.java | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 hutool-json/src/test/java/cn/hutool/json/IssueI4XFMWTest.java diff --git a/hutool-json/src/test/java/cn/hutool/json/IssueI4XFMWTest.java b/hutool-json/src/test/java/cn/hutool/json/IssueI4XFMWTest.java new file mode 100644 index 000000000..51cffe12c --- /dev/null +++ b/hutool-json/src/test/java/cn/hutool/json/IssueI4XFMWTest.java @@ -0,0 +1,42 @@ +package cn.hutool.json; + +import cn.hutool.core.annotation.Alias; +import lombok.Data; +import org.junit.Assert; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +/** + * https://gitee.com/dromara/hutool/issues/I4XFMW + */ +public class IssueI4XFMWTest { + + @Test + public void test() { + List entityList = new ArrayList<>(); + TestEntity entityA = new TestEntity(); + entityA.setId("123"); + entityA.setPassword("456"); + entityList.add(entityA); + + TestEntity entityB = new TestEntity(); + entityB.setId("789"); + entityB.setPassword("098"); + entityList.add(entityB); + + String jsonStr = JSONUtil.toJsonStr(entityList); + Assert.assertEquals("[{\"uid\":\"123\",\"password\":\"456\"},{\"uid\":\"789\",\"password\":\"098\"}]", jsonStr); + List testEntities = JSONUtil.toList(jsonStr, TestEntity.class); + Assert.assertEquals("123", testEntities.get(0).getId()); + Assert.assertEquals("789", testEntities.get(1).getId()); + } + + @Data + static class TestEntity { + @Alias("uid") + private String id; + private String password; + } +} From a2c5115831a5ec5f57aac9b22e595968bb154c5b Mon Sep 17 00:00:00 2001 From: Looly Date: Tue, 15 Mar 2022 00:25:27 +0800 Subject: [PATCH 22/24] backuo --- .../hutool/extra/spring/SpringCronUtil.java | 149 ------------------ .../extra/spring/config/SpringCronConfig.java | 31 ---- .../main/resources/META-INF/spring.factories | 4 +- .../extra/spring/SpringCronUtilTest.java | 94 ----------- 4 files changed, 1 insertion(+), 277 deletions(-) delete mode 100644 hutool-extra/src/main/java/cn/hutool/extra/spring/SpringCronUtil.java delete mode 100644 hutool-extra/src/main/java/cn/hutool/extra/spring/config/SpringCronConfig.java delete mode 100644 hutool-extra/src/test/java/cn/hutool/extra/spring/SpringCronUtilTest.java diff --git a/hutool-extra/src/main/java/cn/hutool/extra/spring/SpringCronUtil.java b/hutool-extra/src/main/java/cn/hutool/extra/spring/SpringCronUtil.java deleted file mode 100644 index 29287bdd8..000000000 --- a/hutool-extra/src/main/java/cn/hutool/extra/spring/SpringCronUtil.java +++ /dev/null @@ -1,149 +0,0 @@ -package cn.hutool.extra.spring; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.IdUtil; -import org.springframework.scheduling.TaskScheduler; -import org.springframework.scheduling.support.CronTrigger; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ScheduledFuture; - -/** - * Spring 动态定时任务封装 - *
      - *
    1. 创建定时任务
    2. - *
    3. 修改定时任务
    4. - *
    5. 取消定时任务
    6. - *
    7. 高级操作
    8. - *
    - * 参考:Spring doc - * - * @author JC - * @date 03/13 - */ -@Component -public class SpringCronUtil { - /** - * 任务调度器 - */ - private static TaskScheduler taskScheduler; - - /** - * ID 与 Future 绑定 - */ - private static final Map> TASK_FUTURE = MapUtil.newConcurrentHashMap(); - - /** - * ID 与 Runnable 绑定 - */ - private static final Map TASK_RUNNABLE = MapUtil.newConcurrentHashMap(); - - /** - * 加入定时任务 - * - * @param task 任务 - * @param expression 定时任务执行时间的cron表达式 - * @return 定时任务ID - */ - public static String schedule(Runnable task, String expression) { - String id = IdUtil.fastUUID(); - return schedule(id, task, expression); - } - - /** - * 加入定时任务 - * - * @param id 定时任务ID - * @param expression 定时任务执行时间的cron表达式 - * @param task 任务 - * @return 定时任务ID - */ - public static String schedule(Serializable id, Runnable task, String expression) { - ScheduledFuture schedule = taskScheduler.schedule(task, new CronTrigger(expression)); - TASK_FUTURE.put(id, schedule); - TASK_RUNNABLE.put(id, task); - return id.toString(); - } - - /** - * 修改定时任务 - * - * @param id 定时任务ID - * @param expression 定时任务执行时间的cron表达式 - * @return 是否修改成功,{@code false}表示未找到对应ID的任务 - */ - public static boolean update(Serializable id, String expression) { - if (!TASK_FUTURE.containsKey(id)) { - return false; - } - ScheduledFuture future = TASK_FUTURE.get(id); - if (future == null) { - return false; - } - future.cancel(true); - schedule(id, TASK_RUNNABLE.get(id), expression); - return true; - } - - /** - * 移除任务 - * - * @param schedulerId 任务ID - * @return 是否移除成功,{@code false}表示未找到对应ID的任务 - */ - public static boolean cancel(Serializable schedulerId) { - if (!TASK_FUTURE.containsKey(schedulerId)) { - return false; - } - ScheduledFuture future = TASK_FUTURE.get(schedulerId); - boolean cancel = future.cancel(false); - if (cancel) { - TASK_FUTURE.remove(schedulerId); - TASK_RUNNABLE.remove(schedulerId); - } - return cancel; - } - - @Resource - public void setTaskScheduler(TaskScheduler taskScheduler) { - SpringCronUtil.taskScheduler = taskScheduler; - } - - /** - * @return 获得Scheduler对象 - */ - public static TaskScheduler getScheduler() { - return taskScheduler; - } - - /** - * 获得当前运行的所有任务 - * - * @return 所有任务 - */ - public static List getAllTask() { - if (CollUtil.isNotEmpty(TASK_FUTURE.keySet())) { - return new ArrayList<>(TASK_FUTURE.keySet()); - } - return new ArrayList<>(); - } - - /** - * 取消所有的任务 - */ - public static void destroy() { - for (ScheduledFuture future : TASK_FUTURE.values()) { - if (future != null) { - future.cancel(true); - } - } - TASK_FUTURE.clear(); - TASK_RUNNABLE.clear(); - } -} diff --git a/hutool-extra/src/main/java/cn/hutool/extra/spring/config/SpringCronConfig.java b/hutool-extra/src/main/java/cn/hutool/extra/spring/config/SpringCronConfig.java deleted file mode 100644 index 1f31d0832..000000000 --- a/hutool-extra/src/main/java/cn/hutool/extra/spring/config/SpringCronConfig.java +++ /dev/null @@ -1,31 +0,0 @@ -package cn.hutool.extra.spring.config; - -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.scheduling.TaskScheduler; -import org.springframework.scheduling.annotation.EnableScheduling; -import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; - -/** - * 可自行配置任务线程池, 修改默认参数 - * - * @author JC - * @date 03/13 - */ -@Configuration -@EnableScheduling -public class SpringCronConfig { - @Bean - @ConditionalOnMissingBean(value = TaskScheduler.class) - public TaskScheduler taskScheduler() { - ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); - // 任务线程池初始化 - scheduler.setThreadNamePrefix("TaskScheduler-"); - scheduler.setPoolSize(Runtime.getRuntime().availableProcessors() / 3 + 1); - - // 保证能立刻丢弃运行中的任务 - scheduler.setRemoveOnCancelPolicy(true); - return scheduler; - } -} diff --git a/hutool-extra/src/main/resources/META-INF/spring.factories b/hutool-extra/src/main/resources/META-INF/spring.factories index 0f0032ef8..09fa11dde 100644 --- a/hutool-extra/src/main/resources/META-INF/spring.factories +++ b/hutool-extra/src/main/resources/META-INF/spring.factories @@ -1,5 +1,3 @@ # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ -cn.hutool.extra.spring.SpringUtil,\ -cn.hutool.extra.spring.config.SpringCronConfig,\ -cn.hutool.extra.spring.SpringCronUtil +cn.hutool.extra.spring.SpringUtil diff --git a/hutool-extra/src/test/java/cn/hutool/extra/spring/SpringCronUtilTest.java b/hutool-extra/src/test/java/cn/hutool/extra/spring/SpringCronUtilTest.java deleted file mode 100644 index e4959da27..000000000 --- a/hutool-extra/src/test/java/cn/hutool/extra/spring/SpringCronUtilTest.java +++ /dev/null @@ -1,94 +0,0 @@ -package cn.hutool.extra.spring; - -import cn.hutool.core.date.DateUtil; -import cn.hutool.extra.spring.config.SpringCronConfig; -import lombok.SneakyThrows; -import lombok.extern.slf4j.Slf4j; -import org.junit.After; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.scheduling.TaskScheduler; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -import java.io.Serializable; -import java.time.Duration; -import java.time.Instant; -import java.util.List; - -/** - * @author JC - * @date 03/13 - */ -@Slf4j -@RunWith(SpringJUnit4ClassRunner.class) -@SpringBootTest(classes = {SpringCronConfig.class, SpringCronUtil.class}) -public class SpringCronUtilTest { - /** - * 创建一个定时任务 - * 观察日志可进行验证 - */ - @Test - public void registerTask() { - String ID1 = SpringCronUtil.schedule(this::task, "0/1 * * * * ?"); - String ID2 = SpringCronUtil.schedule(888, this::task, "0/1 * * * * ?"); - log.info("taskId: {},{}", ID1, ID2); - } - - /** - * 修改一个定时任务 - */ - @Test - @SneakyThrows - public void updateTask() { - SpringCronUtil.schedule(888, this::task, "0/1 * * * * ?"); - Thread.sleep(5000); - boolean update = SpringCronUtil.update(888, "0/5 * * * * ?"); - log.info("update task result: {}", update); - } - - /** - * 取消一个定时任务 - */ - @Test - @SneakyThrows - public void cancelTask() { - SpringCronUtil.schedule(888, this::task, "0/1 * * * * ?"); - Thread.sleep(5000); - boolean cancel = SpringCronUtil.cancel(888); - log.info("cancel task result: {}", cancel); - } - - /** - * 高级用法 - * 参考:Spring doc - */ - @Test - public void senior() { - TaskScheduler scheduler = SpringCronUtil.getScheduler(); - // 给定时间 开始, 间隔时间.. - scheduler.scheduleAtFixedRate(this::task, Instant.now(), Duration.ofMinutes(10)); - // ... - } - - /** - * 取消全部定时任务 - * 查看当前所有的任务 - */ - @After - @SneakyThrows - public void cancelAll() { - Thread.sleep(10000); - List allTask = SpringCronUtil.getAllTask(); - log.info("allTask: {}", allTask); - - SpringCronUtil.destroy(); - - allTask = SpringCronUtil.getAllTask(); - log.info("allTask: {}", allTask); - } - - private void task() { - log.info("information only.. (date:{})", DateUtil.now()); - } -} From 3f4079bcede9927b803c47f5b6fcf43597e9788f Mon Sep 17 00:00:00 2001 From: Looly Date: Tue, 15 Mar 2022 01:09:08 +0800 Subject: [PATCH 23/24] update Mongo4.x support --- CHANGELOG.md | 3 +- hutool-db/pom.xml | 10 +- .../cn/hutool/db/nosql/mongo/MongoDS.java | 74 ++-- .../cn/hutool/db/nosql/mongo/MongoDS4.java | 404 ------------------ .../hutool/db/nosql/mongo/MongoFactory.java | 21 +- .../hutool/db/nosql/mongo/MongoFactory4.java | 120 ------ .../java/cn/hutool/db/nosql/MongoDBTest.java | 6 +- 7 files changed, 59 insertions(+), 579 deletions(-) delete mode 100644 hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoDS4.java delete mode 100644 hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoFactory4.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 3439b0de6..e7434e9d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ # 🚀Changelog ------------------------------------------------------------------------------------------------------------- -# 5.7.23 (2022-03-14) +# 5.7.23 (2022-03-15) ### 🐣新特性 * 【http 】 HttpRequest.form采用TableMap方式(issue#I4W427@Gitee) @@ -14,6 +14,7 @@ * 【core 】 阿拉伯数字转换成中文对发票票面金额转换的扩展(pr#570@Gitee) * 【core 】 ArrayUtil增加replace方法(pr#570@Gitee) * 【core 】 CsvReadConfig增加自定义标题行行号(issue#2180@Github) +* 【db 】 增加MongoDB4.x支持(pr#568@Gitee) * ### 🐞Bug修复 * 【core 】 修复ObjectUtil.hasNull传入null返回true的问题(pr#555@Gitee) diff --git a/hutool-db/pom.xml b/hutool-db/pom.xml index f14a0cc1b..ed7e1aed5 100644 --- a/hutool-db/pom.xml +++ b/hutool-db/pom.xml @@ -20,10 +20,9 @@ 0.9.5.5 2.9.0 - 10.0.14 + 10.0.16 1.2.8 2.4.13 - 3.12.10 4.5.0 3.36.0.3 @@ -97,13 +96,6 @@ ${dbcp2.version} true - - - org.mongodb - mongo-java-driver - ${mongo.version} - true - org.mongodb mongodb-driver-sync diff --git a/hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoDS.java b/hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoDS.java index 8158f0d33..65ce4fa7a 100644 --- a/hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoDS.java +++ b/hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoDS.java @@ -6,29 +6,35 @@ import cn.hutool.core.util.StrUtil; import cn.hutool.db.DbRuntimeException; import cn.hutool.log.Log; import cn.hutool.setting.Setting; -import com.mongodb.MongoClient; -import com.mongodb.MongoClientOptions; -import com.mongodb.MongoClientOptions.Builder; +import com.mongodb.MongoClientSettings; import com.mongodb.MongoCredential; import com.mongodb.ServerAddress; +import com.mongodb.client.MongoClient; +import com.mongodb.client.MongoClients; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoDatabase; +import com.mongodb.connection.ConnectionPoolSettings; +import com.mongodb.connection.SocketSettings; import org.bson.Document; import java.io.Closeable; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.concurrent.TimeUnit; /** - * MongoDB工具类 - * - * @author xiaoleilu + * MongoDB4工具类 * + * @author VampireAchao */ public class MongoDS implements Closeable { + private final static Log log = Log.get(); - /** 默认配置文件 */ + /** + * 默认配置文件 + */ public final static String MONGO_CONFIG_PATH = "config/mongo.setting"; // MongoDB配置文件 @@ -41,6 +47,7 @@ public class MongoDS implements Closeable { private MongoClient mongo; // --------------------------------------------------------------------------- Constructor start + /** * 构造MongoDB数据源
    * 调用者必须持有MongoDS实例,否则会被垃圾回收导致写入失败! @@ -58,8 +65,8 @@ public class MongoDS implements Closeable { * 调用者必须持有MongoDS实例,否则会被垃圾回收导致写入失败! * * @param mongoSetting MongoDB的配置文件,如果是null则读取默认配置文件或者使用MongoDB默认客户端配置 - * @param host 主机(域名或者IP) - * @param port 端口 + * @param host 主机(域名或者IP) + * @param port 端口 */ public MongoDS(Setting mongoSetting, String host, int port) { this.setting = mongoSetting; @@ -86,7 +93,7 @@ public class MongoDS implements Closeable { * 官方文档: http://docs.mongodb.org/manual/administration/replica-sets/ * * @param mongoSetting MongoDB的配置文件,必须有 - * @param groups 分组列表,当为null或空时使用无分组配置,一个分组使用单一模式,否则使用副本集模式 + * @param groups 分组列表,当为null或空时使用无分组配置,一个分组使用单一模式,否则使用副本集模式 */ public MongoDS(Setting mongoSetting, String... groups) { if (mongoSetting == null) { @@ -146,11 +153,13 @@ public class MongoDS implements Closeable { final MongoCredential credentail = createCredentail(group); try { - if (null == credentail) { - mongo = new MongoClient(serverAddress, buildMongoClientOptions(group)); - } else { - mongo = new MongoClient(serverAddress, credentail, buildMongoClientOptions(group)); + MongoClientSettings.Builder clusterSettingsBuilder = MongoClientSettings.builder() + .applyToClusterSettings(b -> b.hosts(Collections.singletonList(serverAddress))); + buildMongoClientSettings(clusterSettingsBuilder, group); + if (null != credentail) { + clusterSettingsBuilder.credential(credentail); } + mongo = MongoClients.create(clusterSettingsBuilder.build()); } catch (Exception e) { throw new DbRuntimeException(StrUtil.format("Init MongoDB pool with connection to [{}] error!", serverAddress), e); } @@ -192,11 +201,13 @@ public class MongoDS implements Closeable { final MongoCredential credentail = createCredentail(StrUtil.EMPTY); try { - if (null == credentail) { - mongo = new MongoClient(addrList, buildMongoClientOptions(StrUtil.EMPTY)); - } else { - mongo = new MongoClient(addrList, credentail, buildMongoClientOptions(StrUtil.EMPTY)); + MongoClientSettings.Builder clusterSettingsBuilder = MongoClientSettings.builder() + .applyToClusterSettings(b -> b.hosts(addrList)); + buildMongoClientSettings(clusterSettingsBuilder, StrUtil.EMPTY); + if (null != credentail) { + clusterSettingsBuilder.credential(credentail); } + mongo = MongoClients.create(clusterSettingsBuilder.build()); } catch (Exception e) { log.error(e, "Init MongoDB connection error!"); return; @@ -234,7 +245,7 @@ public class MongoDS implements Closeable { /** * 获得MongoDB中指定集合对象 * - * @param dbName 库名 + * @param dbName 库名 * @param collectionName 集合名 * @return DBCollection */ @@ -248,6 +259,7 @@ public class MongoDS implements Closeable { } // --------------------------------------------------------------------------- Private method start + /** * 创建ServerAddress对象,会读取配置文件中的相关信息 * @@ -291,7 +303,7 @@ public class MongoDS implements Closeable { */ private MongoCredential createCredentail(String group) { final Setting setting = this.setting; - if(null == setting) { + if (null == setting) { return null; } final String user = setting.getStr("user", group, setting.getStr("user")); @@ -316,23 +328,13 @@ public class MongoDS implements Closeable { return MongoCredential.createCredential(userName, database, password.toCharArray()); } - /** - * 构件MongoDB连接选项
    - * - * @param group 分组,当分组对应的选项不存在时会读取根选项,如果也不存在使用默认值 - * @return MongoClientOptions - */ - private MongoClientOptions buildMongoClientOptions(String group) { - return buildMongoClientOptions(MongoClientOptions.builder(), group).build(); - } - /** * 构件MongoDB连接选项
    * * @param group 分组,当分组对应的选项不存在时会读取根选项,如果也不存在使用默认值 * @return Builder */ - private Builder buildMongoClientOptions(Builder builder, String group) { + private MongoClientSettings.Builder buildMongoClientSettings(MongoClientSettings.Builder builder, String group) { if (setting == null) { return builder; } @@ -348,8 +350,9 @@ public class MongoDS implements Closeable { if (StrUtil.isBlank(group) == false && connectionsPerHost == null) { connectionsPerHost = setting.getInt("connectionsPerHost"); } + ConnectionPoolSettings.Builder connectionPoolSettingsBuilder = ConnectionPoolSettings.builder(); if (connectionsPerHost != null) { - builder.connectionsPerHost(connectionsPerHost); + connectionPoolSettingsBuilder.maxSize(connectionsPerHost); log.debug("MongoDB connectionsPerHost: {}", connectionsPerHost); } @@ -359,9 +362,10 @@ public class MongoDS implements Closeable { setting.getInt("connectTimeout"); } if (connectTimeout != null) { - builder.connectTimeout(connectTimeout); + connectionPoolSettingsBuilder.maxWaitTime(connectTimeout, TimeUnit.MILLISECONDS); log.debug("MongoDB connectTimeout: {}", connectTimeout); } + builder.applyToConnectionPoolSettings(b -> b.applySettings(connectionPoolSettingsBuilder.build())); // 套接字超时时间;该值会被传递给Socket.setSoTimeout(int)。默以为0(无穷) --int Integer socketTimeout = setting.getInt(group + "socketTimeout"); @@ -369,7 +373,8 @@ public class MongoDS implements Closeable { setting.getInt("socketTimeout"); } if (socketTimeout != null) { - builder.socketTimeout(socketTimeout); + SocketSettings socketSettings = SocketSettings.builder().connectTimeout(socketTimeout, TimeUnit.MILLISECONDS).build(); + builder.applyToSocketSettings(b -> b.applySettings(socketSettings)); log.debug("MongoDB socketTimeout: {}", socketTimeout); } @@ -388,4 +393,5 @@ public class MongoDS implements Closeable { return this.setting; } // --------------------------------------------------------------------------- Private method end + } diff --git a/hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoDS4.java b/hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoDS4.java deleted file mode 100644 index 0b2ff0f62..000000000 --- a/hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoDS4.java +++ /dev/null @@ -1,404 +0,0 @@ -package cn.hutool.db.nosql.mongo; - -import cn.hutool.core.exceptions.NotInitedException; -import cn.hutool.core.net.NetUtil; -import cn.hutool.core.util.StrUtil; -import cn.hutool.db.DbRuntimeException; -import cn.hutool.log.Log; -import cn.hutool.setting.Setting; -import com.mongodb.MongoClientSettings; -import com.mongodb.MongoCredential; -import com.mongodb.MongoDriverInformation; -import com.mongodb.ServerAddress; -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoCollection; -import com.mongodb.client.MongoDatabase; -import com.mongodb.client.internal.MongoClientImpl; -import com.mongodb.connection.ConnectionPoolSettings; -import com.mongodb.connection.SocketSettings; -import org.bson.Document; - -import java.io.Closeable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.TimeUnit; - -/** - * MongoDB4工具类 - * - * @author VampireAchao - */ -public class MongoDS4 implements Closeable { - - private final static Log log = Log.get(); - - /** - * 默认配置文件 - */ - public final static String MONGO_CONFIG_PATH = "config/mongo.setting"; - - // MongoDB配置文件 - private Setting setting; - // MongoDB实例连接列表 - private String[] groups; - // MongoDB单点连接信息 - private ServerAddress serverAddress; - // MongoDB客户端对象 - private MongoClient mongo; - - // --------------------------------------------------------------------------- Constructor start - - /** - * 构造MongoDB数据源
    - * 调用者必须持有MongoDS实例,否则会被垃圾回收导致写入失败! - * - * @param host 主机(域名或者IP) - * @param port 端口 - */ - public MongoDS4(String host, int port) { - this.serverAddress = createServerAddress(host, port); - initSingle(); - } - - /** - * 构造MongoDB数据源
    - * 调用者必须持有MongoDS实例,否则会被垃圾回收导致写入失败! - * - * @param mongoSetting MongoDB的配置文件,如果是null则读取默认配置文件或者使用MongoDB默认客户端配置 - * @param host 主机(域名或者IP) - * @param port 端口 - */ - public MongoDS4(Setting mongoSetting, String host, int port) { - this.setting = mongoSetting; - this.serverAddress = createServerAddress(host, port); - initSingle(); - } - - /** - * 构造MongoDB数据源
    - * 当提供多个数据源时,这些数据源将为一个副本集或者多个mongos
    - * 调用者必须持有MongoDS实例,否则会被垃圾回收导致写入失败! 官方文档: http://docs.mongodb.org/manual/administration/replica-sets/ - * - * @param groups 分组列表,当为null或空时使用无分组配置,一个分组使用单一模式,否则使用副本集模式 - */ - public MongoDS4(String... groups) { - this.groups = groups; - init(); - } - - /** - * 构造MongoDB数据源
    - * 当提供多个数据源时,这些数据源将为一个副本集或者mongos
    - * 调用者必须持有MongoDS实例,否则会被垃圾回收导致写入失败!
    - * 官方文档: http://docs.mongodb.org/manual/administration/replica-sets/ - * - * @param mongoSetting MongoDB的配置文件,必须有 - * @param groups 分组列表,当为null或空时使用无分组配置,一个分组使用单一模式,否则使用副本集模式 - */ - public MongoDS4(Setting mongoSetting, String... groups) { - if (mongoSetting == null) { - throw new DbRuntimeException("Mongo setting is null!"); - } - this.setting = mongoSetting; - this.groups = groups; - init(); - } - // --------------------------------------------------------------------------- Constructor end - - /** - * 初始化,当给定分组数大于一个时使用 - */ - public void init() { - if (groups != null && groups.length > 1) { - initCloud(); - } else { - initSingle(); - } - } - - /** - * 初始化
    - * 设定文件中的host和端口有三种形式: - * - *
    -	 * host = host:port
    -	 * 
    - * - *
    -	 * host = host
    -	 * port = port
    -	 * 
    - * - *
    -	 * host = host
    -	 * 
    - */ - synchronized public void initSingle() { - if (setting == null) { - try { - setting = new Setting(MONGO_CONFIG_PATH, true); - } catch (Exception e) { - // 在single模式下,可以没有配置文件。 - } - } - - String group = StrUtil.EMPTY; - if (null == this.serverAddress) { - //存在唯一分组 - if (groups != null && groups.length == 1) { - group = groups[0]; - } - serverAddress = createServerAddress(group); - } - - final MongoCredential credentail = createCredentail(group); - try { - MongoClientSettings.Builder clusterSettingsBuilder = MongoClientSettings.builder().applyToClusterSettings(b -> b.hosts(Collections.singletonList(serverAddress))); - if (null != credentail) { - clusterSettingsBuilder.credential(credentail); - } - mongo = new MongoClientImpl(clusterSettingsBuilder.build(), MongoDriverInformation.builder().build()); - } catch (Exception e) { - throw new DbRuntimeException(StrUtil.format("Init MongoDB pool with connection to [{}] error!", serverAddress), e); - } - - log.info("Init MongoDB pool with connection to [{}]", serverAddress); - } - - /** - * 初始化集群
    - * 集群的其它客户端设定参数使用全局设定
    - * 集群中每一个实例成员用一个group表示,例如: - * - *
    -	 * user = test1
    -	 * pass = 123456
    -	 * database = test
    -	 * [db0]
    -	 * host = 192.168.1.1:27117
    -	 * [db1]
    -	 * host = 192.168.1.1:27118
    -	 * [db2]
    -	 * host = 192.168.1.1:27119
    -	 * 
    - */ - synchronized public void initCloud() { - if (groups == null || groups.length == 0) { - throw new DbRuntimeException("Please give replication set groups!"); - } - - if (setting == null) { - // 若未指定配置文件,则使用默认配置文件 - setting = new Setting(MONGO_CONFIG_PATH, true); - } - - final List addrList = new ArrayList<>(); - for (String group : groups) { - addrList.add(createServerAddress(group)); - } - - final MongoCredential credentail = createCredentail(StrUtil.EMPTY); - try { - MongoClientSettings.Builder clusterSettingsBuilder = MongoClientSettings.builder().applyToClusterSettings(b -> b.hosts(addrList)); - if (null != credentail) { - clusterSettingsBuilder.credential(credentail); - } - mongo = new MongoClientImpl(clusterSettingsBuilder.build(), MongoDriverInformation.builder().build()); - } catch (Exception e) { - log.error(e, "Init MongoDB connection error!"); - return; - } - - log.info("Init MongoDB cloud Set pool with connection to {}", addrList); - } - - /** - * 设定MongoDB配置文件 - * - * @param setting 配置文件 - */ - public void setSetting(Setting setting) { - this.setting = setting; - } - - /** - * @return 获得MongoDB客户端对象 - */ - public MongoClient getMongo() { - return mongo; - } - - /** - * 获得DB - * - * @param dbName DB - * @return DB - */ - public MongoDatabase getDb(String dbName) { - return mongo.getDatabase(dbName); - } - - /** - * 获得MongoDB中指定集合对象 - * - * @param dbName 库名 - * @param collectionName 集合名 - * @return DBCollection - */ - public MongoCollection getCollection(String dbName, String collectionName) { - return getDb(dbName).getCollection(collectionName); - } - - @Override - public void close() { - mongo.close(); - } - - // --------------------------------------------------------------------------- Private method start - - /** - * 创建ServerAddress对象,会读取配置文件中的相关信息 - * - * @param group 分组,如果为{@code null}或者""默认为无分组 - * @return ServerAddress - */ - private ServerAddress createServerAddress(String group) { - final Setting setting = checkSetting(); - - if (group == null) { - group = StrUtil.EMPTY; - } - - final String tmpHost = setting.getByGroup("host", group); - if (StrUtil.isBlank(tmpHost)) { - throw new NotInitedException("Host name is empy of group: {}", group); - } - - final int defaultPort = setting.getInt("port", group, 27017); - return new ServerAddress(NetUtil.buildInetSocketAddress(tmpHost, defaultPort)); - } - - /** - * 创建ServerAddress对象 - * - * @param host 主机域名或者IP(如果为空默认127.0.0.1) - * @param port 端口(如果为空默认为) - * @return ServerAddress - */ - private ServerAddress createServerAddress(String host, int port) { - return new ServerAddress(host, port); - } - - /** - * 创建{@link MongoCredential},用于服务端验证
    - * 此方法会首先读取指定分组下的属性,用户没有定义则读取空分组下的属性 - * - * @param group 分组 - * @return {@link MongoCredential},如果用户未指定用户名密码返回null - * @since 4.1.20 - */ - private MongoCredential createCredentail(String group) { - final Setting setting = this.setting; - if (null == setting) { - return null; - } - final String user = setting.getStr("user", group, setting.getStr("user")); - final String pass = setting.getStr("pass", group, setting.getStr("pass")); - final String database = setting.getStr("database", group, setting.getStr("database")); - return createCredentail(user, database, pass); - } - - /** - * 创建{@link MongoCredential},用于服务端验证 - * - * @param userName 用户名 - * @param database 数据库名 - * @param password 密码 - * @return {@link MongoCredential} - * @since 4.1.20 - */ - private MongoCredential createCredentail(String userName, String database, String password) { - if (StrUtil.hasEmpty(userName, database, database)) { - return null; - } - return MongoCredential.createCredential(userName, database, password.toCharArray()); - } - - /** - * 构件MongoDB连接选项
    - * - * @param group 分组,当分组对应的选项不存在时会读取根选项,如果也不存在使用默认值 - * @return MongoClientOptions - */ - private MongoClientSettings buildMongoClientOptions(String group) { - return buildMongoClientOptions(MongoClientSettings.builder(), group).build(); - } - - /** - * 构件MongoDB连接选项
    - * - * @param group 分组,当分组对应的选项不存在时会读取根选项,如果也不存在使用默认值 - * @return Builder - */ - private MongoClientSettings.Builder buildMongoClientOptions(MongoClientSettings.Builder builder, String group) { - if (setting == null) { - return builder; - } - - if (StrUtil.isEmpty(group)) { - group = StrUtil.EMPTY; - } else { - group = group + StrUtil.DOT; - } - - // 每个主机答应的连接数(每个主机的连接池大小),当连接池被用光时,会被阻塞住 - Integer connectionsPerHost = setting.getInt(group + "connectionsPerHost"); - if (StrUtil.isBlank(group) == false && connectionsPerHost == null) { - connectionsPerHost = setting.getInt("connectionsPerHost"); - } - ConnectionPoolSettings.Builder connectionPoolSettingsBuilder = ConnectionPoolSettings.builder(); - if (connectionsPerHost != null) { - connectionPoolSettingsBuilder.maxSize(connectionsPerHost); - log.debug("MongoDB connectionsPerHost: {}", connectionsPerHost); - } - - // 被阻塞线程从连接池获取连接的最长等待时间(ms) --int - Integer connectTimeout = setting.getInt(group + "connectTimeout"); - if (StrUtil.isBlank(group) == false && connectTimeout == null) { - setting.getInt("connectTimeout"); - } - if (connectTimeout != null) { - connectionPoolSettingsBuilder.maxWaitTime(connectTimeout, TimeUnit.MILLISECONDS); - log.debug("MongoDB connectTimeout: {}", connectTimeout); - } - builder.applyToConnectionPoolSettings(b -> b.applySettings(connectionPoolSettingsBuilder.build())); - - // 套接字超时时间;该值会被传递给Socket.setSoTimeout(int)。默以为0(无穷) --int - Integer socketTimeout = setting.getInt(group + "socketTimeout"); - if (StrUtil.isBlank(group) == false && socketTimeout == null) { - setting.getInt("socketTimeout"); - } - if (socketTimeout != null) { - SocketSettings socketSettings = SocketSettings.builder().connectTimeout(socketTimeout, TimeUnit.MILLISECONDS).build(); - builder.applyToSocketSettings(b -> b.applySettings(socketSettings)); - log.debug("MongoDB socketTimeout: {}", socketTimeout); - } - - return builder; - } - - /** - * 检查Setting配置文件 - * - * @return Setting配置文件 - */ - private Setting checkSetting() { - if (null == this.setting) { - throw new DbRuntimeException("Please indicate setting file or create default [{}]", MONGO_CONFIG_PATH); - } - return this.setting; - } - // --------------------------------------------------------------------------- Private method end - -} diff --git a/hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoFactory.java b/hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoFactory.java index 23fdf8aad..25717bcb7 100644 --- a/hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoFactory.java +++ b/hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoFactory.java @@ -10,16 +10,20 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** - * MongoDB工厂类,用于创建 - * @author looly + * {@link MongoDS}工厂类,用于创建 * + * @author Looly, VampireAchao */ public class MongoFactory { - /** 各分组做组合key的时候分隔符 */ + /** + * 各分组做组合key的时候分隔符 + */ private final static String GROUP_SEPRATER = ","; - /** 数据源池 */ + /** + * 数据源池 + */ private static final Map DS_MAP = new ConcurrentHashMap<>(); // JVM关闭前关闭MongoDB连接 @@ -28,6 +32,7 @@ public class MongoFactory { } // ------------------------------------------------------------------------ Get DS start + /** * 获取MongoDB数据源
    * @@ -80,7 +85,7 @@ public class MongoFactory { * 获取MongoDB数据源
    * * @param setting 设定文件 - * @param groups 分组列表 + * @param groups 分组列表 * @return MongoDB连接 */ public static MongoDS getDS(Setting setting, String... groups) { @@ -99,7 +104,7 @@ public class MongoFactory { * 获取MongoDB数据源
    * * @param setting 配置文件 - * @param groups 分组列表 + * @param groups 分组列表 * @return MongoDB连接 */ public static MongoDS getDS(Setting setting, Collection groups) { @@ -111,8 +116,8 @@ public class MongoFactory { * 关闭全部连接 */ public static void closeAll() { - if(MapUtil.isNotEmpty(DS_MAP)){ - for(MongoDS ds : DS_MAP.values()) { + if (MapUtil.isNotEmpty(DS_MAP)) { + for (MongoDS ds : DS_MAP.values()) { ds.close(); } DS_MAP.clear(); diff --git a/hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoFactory4.java b/hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoFactory4.java deleted file mode 100644 index a099118a4..000000000 --- a/hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoFactory4.java +++ /dev/null @@ -1,120 +0,0 @@ -package cn.hutool.db.nosql.mongo; - -import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.ArrayUtil; -import cn.hutool.core.util.RuntimeUtil; -import cn.hutool.setting.Setting; - -import java.util.Collection; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * MongoDB4工厂类,用于创建 - * @author VampireAchao - */ -public class MongoFactory4 { - - /** 各分组做组合key的时候分隔符 */ - private final static String GROUP_SEPRATER = ","; - - /** 数据源池 */ - private static final Map DS_MAP = new ConcurrentHashMap<>(); - - // JVM关闭前关闭MongoDB连接 - static { - RuntimeUtil.addShutdownHook(MongoFactory4::closeAll); - } - - // ------------------------------------------------------------------------ Get DS start - /** - * 获取MongoDB数据源
    - * - * @param host 主机 - * @param port 端口 - * @return MongoDB连接 - */ - public static MongoDS4 getDS(String host, int port) { - final String key = host + ":" + port; - MongoDS4 ds = DS_MAP.get(key); - if (null == ds) { - // 没有在池中加入之 - ds = new MongoDS4(host, port); - DS_MAP.put(key, ds); - } - - return ds; - } - - /** - * 获取MongoDB数据源
    - * 多个分组名对应的连接组成集群 - * - * @param groups 分组列表 - * @return MongoDB连接 - */ - public static MongoDS4 getDS(String... groups) { - final String key = ArrayUtil.join(groups, GROUP_SEPRATER); - MongoDS4 ds = DS_MAP.get(key); - if (null == ds) { - // 没有在池中加入之 - ds = new MongoDS4(groups); - DS_MAP.put(key, ds); - } - - return ds; - } - - /** - * 获取MongoDB数据源
    - * - * @param groups 分组列表 - * @return MongoDB连接 - */ - public static MongoDS4 getDS(Collection groups) { - return getDS(groups.toArray(new String[0])); - } - - /** - * 获取MongoDB数据源
    - * - * @param setting 设定文件 - * @param groups 分组列表 - * @return MongoDB连接 - */ - public static MongoDS4 getDS(Setting setting, String... groups) { - final String key = setting.getSettingPath() + GROUP_SEPRATER + ArrayUtil.join(groups, GROUP_SEPRATER); - MongoDS4 ds = DS_MAP.get(key); - if (null == ds) { - // 没有在池中加入之 - ds = new MongoDS4(setting, groups); - DS_MAP.put(key, ds); - } - - return ds; - } - - /** - * 获取MongoDB数据源
    - * - * @param setting 配置文件 - * @param groups 分组列表 - * @return MongoDB连接 - */ - public static MongoDS4 getDS(Setting setting, Collection groups) { - return getDS(setting, groups.toArray(new String[0])); - } - // ------------------------------------------------------------------------ Get DS ends - - /** - * 关闭全部连接 - */ - public static void closeAll() { - if(MapUtil.isNotEmpty(DS_MAP)){ - for(MongoDS4 ds : DS_MAP.values()) { - ds.close(); - } - DS_MAP.clear(); - } - } -} diff --git a/hutool-db/src/test/java/cn/hutool/db/nosql/MongoDBTest.java b/hutool-db/src/test/java/cn/hutool/db/nosql/MongoDBTest.java index 67ffadd9f..866ba0b4c 100644 --- a/hutool-db/src/test/java/cn/hutool/db/nosql/MongoDBTest.java +++ b/hutool-db/src/test/java/cn/hutool/db/nosql/MongoDBTest.java @@ -1,6 +1,6 @@ package cn.hutool.db.nosql; -import cn.hutool.db.nosql.mongo.MongoFactory4; +import cn.hutool.db.nosql.mongo.MongoFactory; import com.mongodb.client.MongoDatabase; import org.junit.Assert; import org.junit.Ignore; @@ -13,8 +13,8 @@ public class MongoDBTest { @Test @Ignore - public void redisDSTest() { - MongoDatabase db = MongoFactory4.getDS("master").getDb("test"); + public void mongoDSTest() { + MongoDatabase db = MongoFactory.getDS("master").getDb("test"); Assert.assertEquals("test", db.getName()); } } From 23e21f8f9f50f6a10e6177f1c97f7ddf7e14ffcc Mon Sep 17 00:00:00 2001 From: Looly Date: Tue, 15 Mar 2022 01:14:05 +0800 Subject: [PATCH 24/24] fix test --- hutool-db/src/test/resources/config/mongo.setting | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hutool-db/src/test/resources/config/mongo.setting b/hutool-db/src/test/resources/config/mongo.setting index dc5ae2b33..b50b4d026 100644 --- a/hutool-db/src/test/resources/config/mongo.setting +++ b/hutool-db/src/test/resources/config/mongo.setting @@ -13,8 +13,8 @@ socketKeepAlive=false #---------------------------------- MongoDB实例连接 [master] -host = 127.0.0.1:27017 +host = localhost:27017 [slave] -host = 127.0.0.1:27018 +host = localhost:27018 #-----------------------------------------------------