diff --git a/CHANGELOG.md b/CHANGELOG.md index 208ee2fe9..11ddb22fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,22 @@ ------------------------------------------------------------------------------------------------------------- +# 5.7.20 (2022-01-14) + +### 🐣新特性 +* 【core 】 增加对null值友好的groupingBy操作的Collector实现,可指定map类型(pr#498@Gitee) +* 【core 】 增加KetamaHash(issue#2084@Github) +* 【crypto 】 增加SignUtil +* 【json 】 JSONGetter增加getBeanList方法 +* 【core 】 ObjectUtil 添加三个defaultIfXxxx方法,用于节省CPU及内存损耗。(pr#2094@Github) +* +### 🐞Bug修复 +* 【core 】 修复setter重载导致匹配错误(issue#2082@Github) +* 【core 】 修复RegexPool汉字匹配范围小问题(pr#2081@Github) +* 【core 】 修复OS中的拼写错误(pr#500@Gitee) +* 【core 】 修复CustomKeyMap的merge失效问题(issue#2086@Github) + +------------------------------------------------------------------------------------------------------------- # 5.7.19 (2022-01-07) ### 🐣新特性 diff --git a/README-EN.md b/README-EN.md index 07ec93b86..b55c1128d 100644 --- a/README-EN.md +++ b/README-EN.md @@ -142,18 +142,18 @@ We provide the T-Shirt and Sweater with Hutool Logo, please visit the shop: cn.hutool hutool-all - 5.7.19 + 5.7.20 ``` ### 🍐Gradle ``` -implementation 'cn.hutool:hutool-all:5.7.19' +implementation 'cn.hutool:hutool-all:5.7.20' ``` ## 📥Download -- [Maven Repo](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.7.19/) +- [Maven Repo](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.7.20/) > 🔔️note: > Hutool 5.x supports JDK8+ and is not tested on Android platforms, and cannot guarantee that all tool classes or tool methods are available. diff --git a/README.md b/README.md index aa2154e23..8aeb70262 100644 --- a/README.md +++ b/README.md @@ -142,20 +142,20 @@ Hutool的存在就是为了减少代码搜索成本,避免网络上参差不 cn.hutool hutool-all - 5.7.19 + 5.7.20 ``` ### 🍐Gradle ``` -implementation 'cn.hutool:hutool-all:5.7.19' +implementation 'cn.hutool:hutool-all:5.7.20' ``` ### 📥下载jar 点击以下链接,下载`hutool-all-X.X.X.jar`即可: -- [Maven中央库](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.7.19/) +- [Maven中央库](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.7.20/) > 🔔️注意 > Hutool 5.x支持JDK8+,对Android平台没有测试,不能保证所有工具类或工具方法可用。 diff --git a/bin/version.txt b/bin/version.txt index 1ccc37576..b0917fb0e 100755 --- a/bin/version.txt +++ b/bin/version.txt @@ -1 +1 @@ -5.7.19 +5.7.20 diff --git a/docs/js/version.js b/docs/js/version.js index 90d3c3b3a..978cfac6d 100644 --- a/docs/js/version.js +++ b/docs/js/version.js @@ -1 +1 @@ -var version = '5.7.19' \ No newline at end of file +var version = '5.7.20' \ No newline at end of file diff --git a/hutool-all/pom.xml b/hutool-all/pom.xml index 9dbbfdc1f..10a7032a4 100644 --- a/hutool-all/pom.xml +++ b/hutool-all/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.19 + 5.7.20-SNAPSHOT hutool-all diff --git a/hutool-aop/pom.xml b/hutool-aop/pom.xml index 35e36768f..edc46b7a7 100644 --- a/hutool-aop/pom.xml +++ b/hutool-aop/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.19 + 5.7.20-SNAPSHOT hutool-aop diff --git a/hutool-bloomFilter/pom.xml b/hutool-bloomFilter/pom.xml index 82e8163aa..abeaec1c1 100644 --- a/hutool-bloomFilter/pom.xml +++ b/hutool-bloomFilter/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.19 + 5.7.20-SNAPSHOT hutool-bloomFilter diff --git a/hutool-bom/pom.xml b/hutool-bom/pom.xml index 2db026910..3b8ad4fd4 100644 --- a/hutool-bom/pom.xml +++ b/hutool-bom/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.19 + 5.7.20-SNAPSHOT hutool-bom diff --git a/hutool-cache/pom.xml b/hutool-cache/pom.xml index 91a94dc0d..d544b5527 100644 --- a/hutool-cache/pom.xml +++ b/hutool-cache/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.19 + 5.7.20-SNAPSHOT hutool-cache diff --git a/hutool-captcha/pom.xml b/hutool-captcha/pom.xml index 4bd96f6e5..5dd8f9f15 100644 --- a/hutool-captcha/pom.xml +++ b/hutool-captcha/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.19 + 5.7.20-SNAPSHOT hutool-captcha diff --git a/hutool-core/pom.xml b/hutool-core/pom.xml index 2548d3862..0129bcab8 100644 --- a/hutool-core/pom.xml +++ b/hutool-core/pom.xml @@ -9,7 +9,7 @@ cn.hutool hutool-parent - 5.7.19 + 5.7.20-SNAPSHOT hutool-core diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/BeanDesc.java b/hutool-core/src/main/java/cn/hutool/core/bean/BeanDesc.java index 65411b497..976f40d29 100644 --- a/hutool-core/src/main/java/cn/hutool/core/bean/BeanDesc.java +++ b/hutool-core/src/main/java/cn/hutool/core/bean/BeanDesc.java @@ -140,14 +140,12 @@ public class BeanDesc implements Serializable { * @return this */ private BeanDesc init() { - final Method[] methods = ReflectUtil.getMethods(this.beanClass); + final Method[] gettersAndSetters = ReflectUtil.getMethods(this.beanClass, ReflectUtil::isGetterOrSetterIgnoreCase); PropDesc prop; for (Field field : ReflectUtil.getFields(this.beanClass)) { - if (false == ModifierUtil.isStatic(field) && - // 排除对象子类 - false == "this$0".equals(field.getName())) { - //只针对非static属性 - prop = createProp(field, methods); + // 排除静态属性和对象子类 + if (false == ModifierUtil.isStatic(field) && false == ReflectUtil.isOuterClassField(field)) { + prop = createProp(field, gettersAndSetters); // 只有不存在时才放入,防止父类属性覆盖子类属性 this.propMap.putIfAbsent(prop.getFieldName(), prop); } @@ -190,12 +188,12 @@ public class BeanDesc implements Serializable { /** * 查找字段对应的Getter和Setter方法 * - * @param field 字段 - * @param methods 类中所有的方法 - * @param ignoreCase 是否忽略大小写匹配 + * @param field 字段 + * @param gettersOrSetters 类中所有的Getter或Setter方法 + * @param ignoreCase 是否忽略大小写匹配 * @return PropDesc */ - private PropDesc findProp(Field field, Method[] methods, boolean ignoreCase) { + private PropDesc findProp(Field field, Method[] gettersOrSetters, boolean ignoreCase) { final String fieldName = field.getName(); final Class fieldType = field.getType(); final boolean isBooleanField = BooleanUtil.isBoolean(fieldType); @@ -203,24 +201,19 @@ public class BeanDesc implements Serializable { Method getter = null; Method setter = null; String methodName; - Class[] parameterTypes; - for (Method method : methods) { - parameterTypes = method.getParameterTypes(); - if (parameterTypes.length > 1) { - // 多于1个参数说明非Getter或Setter - continue; - } - + for (Method method : gettersOrSetters) { methodName = method.getName(); - if (parameterTypes.length == 0) { + if (method.getParameterCount() == 0) { // 无参数,可能为Getter方法 if (isMatchGetter(methodName, fieldName, isBooleanField, ignoreCase)) { // 方法名与字段名匹配,则为Getter方法 getter = method; } } else if (isMatchSetter(methodName, fieldName, isBooleanField, ignoreCase)) { - // 只有一个参数的情况下方法名与字段名对应匹配,则为Setter方法 - setter = method; + // setter方法的参数类型和字段类型必须一致,或参数类型是字段类型的子类 + if(fieldType.isAssignableFrom(method.getParameterTypes()[0])){ + setter = method; + } } if (null != getter && null != setter) { // 如果Getter和Setter方法都找到了,不再继续寻找 @@ -261,15 +254,6 @@ public class BeanDesc implements Serializable { handledFieldName = StrUtil.upperFirst(fieldName); } - if (false == methodName.startsWith("get") && false == methodName.startsWith("is")) { - // 非标准Getter方法 - return false; - } - if ("getclass".equals(methodName)) { - //跳过getClass方法 - return false; - } - // 针对Boolean类型特殊检查 if (isBooleanField) { if (fieldName.startsWith("is")) { diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java b/hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java index 796124648..b66b15f0d 100644 --- a/hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java @@ -88,9 +88,8 @@ public class BeanUtil { */ public static boolean hasSetter(Class clazz) { if (ClassUtil.isNormalClass(clazz)) { - final Method[] methods = clazz.getMethods(); - for (Method method : methods) { - if (method.getParameterTypes().length == 1 && method.getName().startsWith("set")) { + for (Method method : clazz.getMethods()) { + if (method.getParameterCount() == 1 && method.getName().startsWith("set")) { // 检测包含标准的setXXX方法即视为标准的JavaBean return true; } @@ -110,7 +109,7 @@ public class BeanUtil { public static boolean hasGetter(Class clazz) { if (ClassUtil.isNormalClass(clazz)) { for (Method method : clazz.getMethods()) { - if (method.getParameterTypes().length == 0) { + if (method.getParameterCount() == 0) { if (method.getName().startsWith("get") || method.getName().startsWith("is")) { return true; } @@ -739,7 +738,7 @@ public class BeanUtil { * @param copyOptions 拷贝选项,见 {@link CopyOptions} */ public static void copyProperties(Object source, Object target, CopyOptions copyOptions) { - BeanCopier.create(source, target, ObjectUtil.defaultIfNull(copyOptions, CopyOptions.create())).copy(); + BeanCopier.create(source, target, ObjectUtil.defaultIfNull(copyOptions, CopyOptions::create)).copy(); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/CollStreamUtil.java b/hutool-core/src/main/java/cn/hutool/core/collection/CollStreamUtil.java index 51a9c3eee..ef3893d6d 100644 --- a/hutool-core/src/main/java/cn/hutool/core/collection/CollStreamUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/collection/CollStreamUtil.java @@ -3,6 +3,7 @@ package cn.hutool.core.collection; import cn.hutool.core.lang.Opt; import cn.hutool.core.map.MapUtil; +import cn.hutool.core.stream.CollectorUtil; import cn.hutool.core.stream.StreamUtil; import java.util.Collection; @@ -161,7 +162,7 @@ public class CollStreamUtil { if (CollUtil.isEmpty(collection)) { return Collections.emptyMap(); } - return groupBy(collection, key1, Collectors.groupingBy(key2, Collectors.toList()), isParallel); + return groupBy(collection, key1, CollectorUtil.groupingBy(key2, Collectors.toList()), isParallel); } /** @@ -236,7 +237,7 @@ public class CollStreamUtil { if (CollUtil.isEmpty(collection)) { return Collections.emptyMap(); } - return groupBy(collection, key, Collectors.mapping(value, Collectors.toList()), isParallel); + return groupBy(collection, key, Collectors.mapping(v -> Opt.ofNullable(v).map(value).orElse(null), Collectors.toList()), isParallel); } /** @@ -276,7 +277,7 @@ public class CollStreamUtil { if (CollUtil.isEmpty(collection)) { return Collections.emptyMap(); } - return StreamUtil.of(collection, isParallel).collect(Collectors.groupingBy(key, downstream)); + return StreamUtil.of(collection, isParallel).collect(CollectorUtil.groupingBy(key, downstream)); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/compiler/JavaClassFileManager.java b/hutool-core/src/main/java/cn/hutool/core/compiler/JavaClassFileManager.java index 1150f9e5c..861883710 100644 --- a/hutool-core/src/main/java/cn/hutool/core/compiler/JavaClassFileManager.java +++ b/hutool-core/src/main/java/cn/hutool/core/compiler/JavaClassFileManager.java @@ -44,7 +44,7 @@ class JavaClassFileManager extends ForwardingJavaFileManager { */ protected JavaClassFileManager(ClassLoader parent, JavaFileManager fileManager) { super(fileManager); - this.parent = ObjectUtil.defaultIfNull(parent, ClassLoaderUtil.getClassLoader()); + this.parent = ObjectUtil.defaultIfNull(parent, ClassLoaderUtil::getClassLoader); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/compiler/JavaSourceCompiler.java b/hutool-core/src/main/java/cn/hutool/core/compiler/JavaSourceCompiler.java index 95030aa1b..39e08d28f 100644 --- a/hutool-core/src/main/java/cn/hutool/core/compiler/JavaSourceCompiler.java +++ b/hutool-core/src/main/java/cn/hutool/core/compiler/JavaSourceCompiler.java @@ -89,7 +89,7 @@ public class JavaSourceCompiler { * @param parent 父类加载器,null则使用默认类加载器 */ private JavaSourceCompiler(ClassLoader parent) { - this.parentClassLoader = ObjectUtil.defaultIfNull(parent, ClassLoaderUtil.getClassLoader()); + this.parentClassLoader = ObjectUtil.defaultIfNull(parent, ClassLoaderUtil::getClassLoader); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/impl/TemporalAccessorConverter.java b/hutool-core/src/main/java/cn/hutool/core/convert/impl/TemporalAccessorConverter.java index 76c25b966..22d5186cf 100644 --- a/hutool-core/src/main/java/cn/hutool/core/convert/impl/TemporalAccessorConverter.java +++ b/hutool-core/src/main/java/cn/hutool/core/convert/impl/TemporalAccessorConverter.java @@ -227,7 +227,7 @@ public class TemporalAccessorConverter extends AbstractConverter File writeLines(Collection list) throws IORuntimeException { + public File writeLines(Iterable list) throws IORuntimeException { return writeLines(list, false); } @@ -177,7 +176,7 @@ public class FileWriter extends FileWrapper { * @return 目标文件 * @throws IORuntimeException IO异常 */ - public File appendLines(Collection list) throws IORuntimeException { + public File appendLines(Iterable list) throws IORuntimeException { return writeLines(list, true); } @@ -190,7 +189,7 @@ public class FileWriter extends FileWrapper { * @return 目标文件 * @throws IORuntimeException IO异常 */ - public File writeLines(Collection list, boolean isAppend) throws IORuntimeException { + public File writeLines(Iterable list, boolean isAppend) throws IORuntimeException { return writeLines(list, null, isAppend); } @@ -205,12 +204,22 @@ public class FileWriter extends FileWrapper { * @throws IORuntimeException IO异常 * @since 3.1.0 */ - public File writeLines(Collection list, LineSeparator lineSeparator, boolean isAppend) throws IORuntimeException { + public File writeLines(Iterable list, LineSeparator lineSeparator, boolean isAppend) throws IORuntimeException { try (PrintWriter writer = getPrintWriter(isAppend)) { + boolean isFirst = true; for (T t : list) { if (null != t) { + if(isFirst){ + isFirst = false; + if(isAppend && FileUtil.isNotEmpty(this.file)){ + // 追加模式下且文件非空,补充换行符 + printNewLine(writer, lineSeparator); + } + } else{ + printNewLine(writer, lineSeparator); + } writer.print(t); - printNewLine(writer, lineSeparator); + writer.flush(); } } diff --git a/hutool-core/src/main/java/cn/hutool/core/io/resource/ClassPathResource.java b/hutool-core/src/main/java/cn/hutool/core/io/resource/ClassPathResource.java index 42503175a..96af500a2 100644 --- a/hutool-core/src/main/java/cn/hutool/core/io/resource/ClassPathResource.java +++ b/hutool-core/src/main/java/cn/hutool/core/io/resource/ClassPathResource.java @@ -69,7 +69,7 @@ public class ClassPathResource extends UrlResource { this.path = path; this.name = StrUtil.isBlank(path) ? null : FileUtil.getName(path); - this.classLoader = ObjectUtil.defaultIfNull(classLoader, ClassUtil.getClassLoader()); + this.classLoader = ObjectUtil.defaultIfNull(classLoader, ClassUtil::getClassLoader); this.clazz = clazz; initUrl(); } diff --git a/hutool-core/src/main/java/cn/hutool/core/io/resource/FileResource.java b/hutool-core/src/main/java/cn/hutool/core/io/resource/FileResource.java index 9bac9521c..cb4179b95 100644 --- a/hutool-core/src/main/java/cn/hutool/core/io/resource/FileResource.java +++ b/hutool-core/src/main/java/cn/hutool/core/io/resource/FileResource.java @@ -60,7 +60,7 @@ public class FileResource implements Resource, Serializable { public FileResource(File file, String fileName) { Assert.notNull(file, "File must be not null !"); this.file = file; - this.name = ObjectUtil.defaultIfNull(fileName, file.getName()); + this.name = ObjectUtil.defaultIfNull(fileName, file::getName); } // ----------------------------------------------------------------------- Constructor end diff --git a/hutool-core/src/main/java/cn/hutool/core/io/resource/UrlResource.java b/hutool-core/src/main/java/cn/hutool/core/io/resource/UrlResource.java index 0a7800611..fd6b23044 100644 --- a/hutool-core/src/main/java/cn/hutool/core/io/resource/UrlResource.java +++ b/hutool-core/src/main/java/cn/hutool/core/io/resource/UrlResource.java @@ -36,7 +36,7 @@ public class UrlResource implements Resource, Serializable{ */ public UrlResource(URL url, String name) { this.url = url; - this.name = ObjectUtil.defaultIfNull(name, (null != url) ? FileUtil.getName(url.getPath()) : null); + this.name = ObjectUtil.defaultIfNull(name, () -> (null != url ? FileUtil.getName(url.getPath()) : null)); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/RegexPool.java b/hutool-core/src/main/java/cn/hutool/core/lang/RegexPool.java index 7d1295e0c..ef21d49d9 100755 --- a/hutool-core/src/main/java/cn/hutool/core/lang/RegexPool.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/RegexPool.java @@ -20,9 +20,10 @@ public interface RegexPool { */ String WORD = "[a-zA-Z]+"; /** - * 单个中文汉字 + * 单个中文汉字
+ * 参照维基百科汉字Unicode范围(https://zh.wikipedia.org/wiki/%E6%B1%89%E5%AD%97 页面右侧) */ - String CHINESE = "[\u4E00-\u9FFF]"; + String CHINESE = "[\u2E80-\u2EFF\u2F00-\u2FDF\u31C0-\u31EF\u3400-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF\uD840\uDC00-\uD869\uDEDF\uD869\uDF00-\uD86D\uDF3F\uD86D\uDF40-\uD86E\uDC1F\uD86E\uDC20-\uD873\uDEAF\uD87E\uDC00-\uD87E\uDE1F]"; /** * 中文汉字 */ diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/ResourceClassLoader.java b/hutool-core/src/main/java/cn/hutool/core/lang/ResourceClassLoader.java index 383f6b98b..509efa967 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/ResourceClassLoader.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/ResourceClassLoader.java @@ -30,8 +30,8 @@ public class ResourceClassLoader extends SecureClassLoader { * @param resourceMap 资源map */ public ResourceClassLoader(ClassLoader parentClassLoader, Map resourceMap) { - super(ObjectUtil.defaultIfNull(parentClassLoader, ClassLoaderUtil.getClassLoader())); - this.resourceMap = ObjectUtil.defaultIfNull(resourceMap, new HashMap<>()); + super(ObjectUtil.defaultIfNull(parentClassLoader, ClassLoaderUtil::getClassLoader)); + this.resourceMap = ObjectUtil.defaultIfNull(resourceMap, HashMap::new); this.cacheClassMap = new HashMap<>(); } diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/hash/KetamaHash.java b/hutool-core/src/main/java/cn/hutool/core/lang/hash/KetamaHash.java new file mode 100755 index 000000000..bc2f3cbf2 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/lang/hash/KetamaHash.java @@ -0,0 +1,51 @@ +package cn.hutool.core.lang.hash; + +import cn.hutool.core.exceptions.UtilException; +import cn.hutool.core.util.StrUtil; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * Ketama算法,用于在一致性Hash中快速定位服务器位置 + * + * @author looly + * @since 5.7.20 + */ +public class KetamaHash implements Hash64, Hash32 { + + @Override + public long hash64(String key) { + byte[] bKey = md5(key); + return ((long) (bKey[3] & 0xFF) << 24) + | ((long) (bKey[2] & 0xFF) << 16) + | ((long) (bKey[1] & 0xFF) << 8) + | (bKey[0] & 0xFF); + } + + @Override + public int hash32(String key) { + return (int) (hash64(key) & 0xffffffffL); + } + + @Override + public Number hash(String key) { + return hash64(key); + } + + /** + * 计算MD5值,使用UTF-8编码 + * + * @param key 被计算的键 + * @return MD5值 + */ + private static byte[] md5(String key) { + final MessageDigest md5; + try { + md5 = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException e) { + throw new UtilException("MD5 algorithm not suooport!", e); + } + return md5.digest(StrUtil.utf8Bytes(key)); + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/reflect/MethodHandleUtil.java b/hutool-core/src/main/java/cn/hutool/core/lang/reflect/MethodHandleUtil.java index 781208744..fbfa2ded3 100755 --- a/hutool-core/src/main/java/cn/hutool/core/lang/reflect/MethodHandleUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/reflect/MethodHandleUtil.java @@ -12,6 +12,7 @@ import java.lang.reflect.Method; /** * 方法句柄{@link MethodHandle}封装工具类
+ * 方法句柄是一个有类型的,可以直接执行的指向底层方法、构造器、field等的引用,可以简单理解为函数指针,它是一种更加底层的查找、调整和调用方法的机制。 * 参考: *