From b83be4056798172159020517caeba4ab0c5ffb83 Mon Sep 17 00:00:00 2001 From: gonggy <514592053@qq.com> Date: Tue, 19 Jul 2022 09:18:46 +0800 Subject: [PATCH 1/6] =?UTF-8?q?Dict=E5=A2=9E=E5=8A=A0=E8=A6=86=E7=9B=96put?= =?UTF-8?q?IfAbsent=E5=92=8CcomputeIfAbsent=E7=9A=84=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/cn/hutool/core/lang/Dict.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/Dict.java b/hutool-core/src/main/java/cn/hutool/core/lang/Dict.java index 14424aa7b..fc9d32616 100755 --- a/hutool-core/src/main/java/cn/hutool/core/lang/Dict.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/Dict.java @@ -20,6 +20,7 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; import java.util.function.BiFunction; +import java.util.function.Function; /** * 字典对象,扩充了HashMap中的方法 @@ -633,6 +634,17 @@ public class Dict extends LinkedHashMap implements BasicTypeGett public Object merge(final String key, final Object value, final BiFunction remappingFunction) { return super.merge(customKey(key), value, remappingFunction); } + + @Override + public Object putIfAbsent(String key, Object value) { + return super.putIfAbsent(customKey(key), value); + } + + @Override + public Object computeIfAbsent(String key, Function mappingFunction) { + return super.computeIfAbsent(customKey(key), mappingFunction); + } + //---------------------------------------------------------------------------- Override default methods end /** From 13b5b32384658cc0af032e87c27a91b816e3d19e Mon Sep 17 00:00:00 2001 From: huangchengxing <841396397@qq.com> Date: Tue, 19 Jul 2022 09:53:49 +0800 Subject: [PATCH 2/6] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=85=83=E7=B4=A0?= =?UTF-8?q?=E6=89=AB=E6=8F=8F=E5=99=A8=E4=B8=8E=E9=80=9A=E7=94=A8=E6=B3=A8?= =?UTF-8?q?=E8=A7=A3=E6=89=AB=E6=8F=8F=E5=99=A8=EF=BC=8C=E5=9C=A8Annotatio?= =?UTF-8?q?nScanner=E4=B8=AD=E7=BB=9F=E4=B8=80=E6=8F=90=E4=BE=9B=E5=B7=B2?= =?UTF-8?q?=E7=BB=8F=E9=85=8D=E7=BD=AE=E5=A5=BD=E6=89=AB=E6=8F=8F=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=E7=9A=84=E9=9D=99=E6=80=81=E6=89=AB=E6=8F=8F=E5=99=A8?= =?UTF-8?q?=E5=AE=9E=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/annotation/AnnotationUtil.java | 35 ++-- .../AbstractTypeAnnotationScanner.java | 8 +- .../annotation/scanner/AnnotationScanner.java | 144 ++++++++++++----- .../scanner/ElementAnnotationScanner.java | 44 ++++++ .../scanner/EmptyAnnotationScanner.java | 2 - .../scanner/GenericAnnotationScanner.java | 149 ++++++++++++++++++ .../scanner/MethodAnnotationScanner.java | 32 ++-- .../scanner/ElementAnnotationScannerTest.java | 60 +++++++ .../scanner/GenericAnnotationScannerTest.java | 85 ++++++++++ 9 files changed, 488 insertions(+), 71 deletions(-) create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/scanner/ElementAnnotationScanner.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/scanner/GenericAnnotationScanner.java create mode 100644 hutool-core/src/test/java/cn/hutool/core/annotation/scanner/ElementAnnotationScannerTest.java create mode 100644 hutool-core/src/test/java/cn/hutool/core/annotation/scanner/GenericAnnotationScannerTest.java diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationUtil.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationUtil.java index bb0756f58..f16aeb605 100755 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationUtil.java @@ -1,6 +1,9 @@ package cn.hutool.core.annotation; -import cn.hutool.core.annotation.scanner.*; +import cn.hutool.core.annotation.scanner.AnnotationScanner; +import cn.hutool.core.annotation.scanner.MetaAnnotationScanner; +import cn.hutool.core.annotation.scanner.MethodAnnotationScanner; +import cn.hutool.core.annotation.scanner.TypeAnnotationScanner; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.exceptions.UtilException; import cn.hutool.core.lang.Opt; @@ -334,7 +337,7 @@ public class AnnotationUtil { * @see MetaAnnotationScanner */ public static List scanMetaAnnotation(Class annotationType) { - return new MetaAnnotationScanner().getIfSupport(annotationType); + return AnnotationScanner.DIRECTLY_AND_META_ANNOTATION.getAnnotationsIfSupport(annotationType); } /** @@ -363,7 +366,7 @@ public class AnnotationUtil { * @see TypeAnnotationScanner */ public static List scanClass(Class targetClass) { - return new TypeAnnotationScanner().getIfSupport(targetClass); + return AnnotationScanner.TYPE_HIERARCHY.getAnnotationsIfSupport(targetClass); } /** @@ -391,7 +394,7 @@ public class AnnotationUtil { * @see MethodAnnotationScanner */ public static List scanMethod(Method method) { - return new MethodAnnotationScanner(true).getIfSupport(method); + return AnnotationScanner.TYPE_HIERARCHY.getAnnotationsIfSupport(method); } /** @@ -478,14 +481,12 @@ public class AnnotationUtil { if (ObjectUtil.isNotNull(target)) { return target; } - AnnotationScanner[] scanners = new AnnotationScanner[]{ - new MetaAnnotationScanner(), new TypeAnnotationScanner(), new MethodAnnotationScanner(), new FieldAnnotationScanner() - }; - return AnnotationScanner.scanByAnySupported(annotatedEle, scanners).stream() - .map(annotation -> getSynthesizedAnnotation(annotationType, annotation)) - .filter(Objects::nonNull) - .findFirst() - .orElse(null); + return AnnotationScanner.DIRECTLY + .getAnnotationsIfSupport(annotatedEle).stream() + .map(annotation -> getSynthesizedAnnotation(annotationType, annotation)) + .filter(Objects::nonNull) + .findFirst() + .orElse(null); } /** @@ -511,10 +512,8 @@ public class AnnotationUtil { * @see SynthesizedAggregateAnnotation */ public static List getAllSynthesizedAnnotations(AnnotatedElement annotatedEle, Class annotationType) { - AnnotationScanner[] scanners = new AnnotationScanner[]{ - new MetaAnnotationScanner(), new TypeAnnotationScanner(), new MethodAnnotationScanner(), new FieldAnnotationScanner() - }; - return AnnotationScanner.scanByAnySupported(annotatedEle, scanners).stream() + return AnnotationScanner.DIRECTLY + .getAnnotationsIfSupport(annotatedEle).stream() .map(annotation -> getSynthesizedAnnotation(annotationType, annotation)) .filter(Objects::nonNull) .collect(Collectors.toList()); @@ -527,7 +526,7 @@ public class AnnotationUtil { * @return 聚合注解 */ public static SynthesizedAggregateAnnotation aggregatingFromAnnotation(Annotation... annotations) { - return new GenericSynthesizedAggregateAnnotation(Arrays.asList(annotations), EmptyAnnotationScanner.INSTANCE); + return new GenericSynthesizedAggregateAnnotation(Arrays.asList(annotations), AnnotationScanner.NOTHING); } /** @@ -537,7 +536,7 @@ public class AnnotationUtil { * @return 聚合注解 */ public static SynthesizedAggregateAnnotation aggregatingFromAnnotationWithMeta(Annotation... annotations) { - return new GenericSynthesizedAggregateAnnotation(Arrays.asList(annotations), new MetaAnnotationScanner()); + return new GenericSynthesizedAggregateAnnotation(Arrays.asList(annotations), AnnotationScanner.DIRECTLY_AND_META_ANNOTATION); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/AbstractTypeAnnotationScanner.java b/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/AbstractTypeAnnotationScanner.java index 8c8ba7ad9..b90035baa 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/AbstractTypeAnnotationScanner.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/AbstractTypeAnnotationScanner.java @@ -60,9 +60,9 @@ public abstract class AbstractTypeAnnotationScanner> filter, Set> excludeTypes) { @@ -186,7 +186,7 @@ public abstract class AbstractTypeAnnotationScanner注解扫描器,用于从支持的可注解元素上获取所需注解 + * + *

默认提供了以下扫描方式: + *

    + *
  • {@link #NOTHING}:什么都不做,什么注解都不扫描;
  • + *
  • {@link #DIRECTLY}:扫描元素本身直接声明的注解,包括父类带有{@link Inherited}、被传递到元素上的注解;
  • + *
  • + * {@link #DIRECTLY_AND_META_ANNOTATION}:扫描元素本身直接声明的注解,包括父类带有{@link Inherited}、被传递到元素上的注解, + * 以及这些注解的元注解; + *
  • + *
  • {@link #SUPERCLASS}:扫描元素本身以及父类的层级结构中声明的注解;
  • + *
  • {@link #SUPERCLASS_AND_META_ANNOTATION}:扫描元素本身以及父类的层级结构中声明的注解,以及这些注解的元注解;
  • + *
  • {@link #INTERFACE}:扫描元素本身以及父接口的层级结构中声明的注解;
  • + *
  • {@link #INTERFACE_AND_META_ANNOTATION}:扫描元素本身以及父接口的层级结构中声明的注解,以及这些注解的元注解;
  • + *
  • {@link #TYPE_HIERARCHY}:扫描元素本身以及父类、父接口的层级结构中声明的注解;
  • + *
  • {@link #TYPE_HIERARCHY_AND_META_ANNOTATION}:扫描元素本身以及父接口、父接口的层级结构中声明的注解,以及这些注解的元注解;
  • + *
* * @author huangchengxing * @see TypeAnnotationScanner * @see MethodAnnotationScanner * @see FieldAnnotationScanner * @see MetaAnnotationScanner + * @see ElementAnnotationScanner + * @see GenericAnnotationScanner */ public interface AnnotationScanner { + // ============================ 预置的扫描器实例 ============================ + + /** + * 不扫描任何注解 + */ + AnnotationScanner NOTHING = new EmptyAnnotationScanner(); + + /** + * 扫描元素本身直接声明的注解,包括父类带有{@link Inherited}、被传递到元素上的注解的扫描器 + */ + AnnotationScanner DIRECTLY = new GenericAnnotationScanner(false, false, false); + + /** + * 扫描元素本身直接声明的注解,包括父类带有{@link Inherited}、被传递到元素上的注解,以及这些注解的元注解的扫描器 + */ + AnnotationScanner DIRECTLY_AND_META_ANNOTATION = new GenericAnnotationScanner(true, false, false); + + /** + * 扫描元素本身以及父类的层级结构中声明的注解的扫描器 + */ + AnnotationScanner SUPERCLASS = new GenericAnnotationScanner(false, true, false); + + /** + * 扫描元素本身以及父类的层级结构中声明的注解,以及这些注解的元注解的扫描器 + */ + AnnotationScanner SUPERCLASS_AND_META_ANNOTATION = new GenericAnnotationScanner(true, true, false); + + /** + * 扫描元素本身以及父接口的层级结构中声明的注解的扫描器 + */ + AnnotationScanner INTERFACE = new GenericAnnotationScanner(false, false, true); + + /** + * 扫描元素本身以及父接口的层级结构中声明的注解,以及这些注解的元注解的扫描器 + */ + AnnotationScanner INTERFACE_AND_META_ANNOTATION = new GenericAnnotationScanner(true, false, true); + + /** + * 扫描元素本身以及父类、父接口的层级结构中声明的注解的扫描器 + */ + AnnotationScanner TYPE_HIERARCHY = new GenericAnnotationScanner(false, true, true); + + /** + * 扫描元素本身以及父接口、父接口的层级结构中声明的注解,以及这些注解的元注解的扫描器 + */ + AnnotationScanner TYPE_HIERARCHY_AND_META_ANNOTATION = new GenericAnnotationScanner(true, true, true); + + // ============================ 静态方法 ============================ + + /** + * 给定一组扫描器,使用第一个支持处理该类型元素的扫描器获取元素上可能存在的注解 + * + * @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission + * @param scanners 注解扫描器 + * @return 注解 + */ + static List scanByAnySupported(AnnotatedElement annotatedEle, AnnotationScanner... scanners) { + if (ObjectUtil.isNull(annotatedEle) && ArrayUtil.isNotEmpty(scanners)) { + return Collections.emptyList(); + } + return Stream.of(scanners) + .filter(scanner -> scanner.support(annotatedEle)) + .findFirst() + .map(scanner -> scanner.getAnnotations(annotatedEle)) + .orElseGet(Collections::emptyList); + } + + /** + * 根据指定的扫描器,扫描元素上可能存在的注解 + * + * @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission + * @param scanners 注解扫描器 + * @return 注解 + */ + static List scanByAllSupported(AnnotatedElement annotatedEle, AnnotationScanner... scanners) { + if (ObjectUtil.isNull(annotatedEle) && ArrayUtil.isNotEmpty(scanners)) { + return Collections.emptyList(); + } + return Stream.of(scanners) + .map(scanner -> scanner.getAnnotationsIfSupport(annotatedEle)) + .flatMap(Collection::stream) + .collect(Collectors.toList()); + } + + // ============================ 抽象方法 ============================ + /** * 判断是否支持扫描该注解元素 * @@ -56,7 +161,7 @@ public interface AnnotationScanner { * @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission * @return 注解 */ - default List getIfSupport(AnnotatedElement annotatedEle) { + default List getAnnotationsIfSupport(AnnotatedElement annotatedEle) { return support(annotatedEle) ? getAnnotations(annotatedEle) : Collections.emptyList(); } @@ -90,39 +195,4 @@ public interface AnnotationScanner { } } - /** - * 给定一组扫描器,使用第一个支持处理该类型元素的扫描器获取元素上可能存在的注解 - * - * @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission - * @param scanners 注解扫描器 - * @return 注解 - */ - static List scanByAnySupported(AnnotatedElement annotatedEle, AnnotationScanner... scanners) { - if (ObjectUtil.isNull(annotatedEle) && ArrayUtil.isNotEmpty(scanners)) { - return Collections.emptyList(); - } - return Stream.of(scanners) - .filter(scanner -> scanner.support(annotatedEle)) - .findFirst() - .map(scanner -> scanner.getAnnotations(annotatedEle)) - .orElseGet(Collections::emptyList); - } - - /** - * 根据指定的扫描器,扫描元素上可能存在的注解 - * - * @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission - * @param scanners 注解扫描器 - * @return 注解 - */ - static List scanByAllScanner(AnnotatedElement annotatedEle, AnnotationScanner... scanners) { - if (ObjectUtil.isNull(annotatedEle) && ArrayUtil.isNotEmpty(scanners)) { - return Collections.emptyList(); - } - return Stream.of(scanners) - .map(scanner -> scanner.getIfSupport(annotatedEle)) - .flatMap(Collection::stream) - .collect(Collectors.toList()); - } - } diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/ElementAnnotationScanner.java b/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/ElementAnnotationScanner.java new file mode 100644 index 000000000..7907c160e --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/ElementAnnotationScanner.java @@ -0,0 +1,44 @@ +package cn.hutool.core.annotation.scanner; + +import cn.hutool.core.util.ObjectUtil; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; +import java.util.function.BiConsumer; +import java.util.function.Predicate; +import java.util.stream.Stream; + +/** + * 扫描{@link AnnotatedElement}上的注解,不支持处理层级对象 + * + * @author huangchengxing + */ +public class ElementAnnotationScanner implements AnnotationScanner { + + /** + * 判断是否支持扫描该注解元素,仅当注解元素不为空时返回{@code true} + * + * @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission + * @return 是否支持扫描该注解元素 + */ + @Override + public boolean support(AnnotatedElement annotatedEle) { + return ObjectUtil.isNotNull(annotatedEle); + } + + /** + * 扫描{@link AnnotatedElement}上直接声明的注解,调用前需要确保调用{@link #support(AnnotatedElement)}返回为true + * + * @param consumer 对获取到的注解和注解对应的层级索引的处理 + * @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission + * @param filter 注解过滤器,无法通过过滤器的注解不会被处理。该参数允许为空。 + */ + @Override + public void scan(BiConsumer consumer, AnnotatedElement annotatedEle, Predicate filter) { + filter = ObjectUtil.defaultIfNull(filter, t -> true); + Stream.of(annotatedEle.getAnnotations()) + .filter(filter) + .forEach(annotation -> consumer.accept(0, annotation)); + } + +} diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/EmptyAnnotationScanner.java b/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/EmptyAnnotationScanner.java index 62dc560fa..972d1d758 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/EmptyAnnotationScanner.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/EmptyAnnotationScanner.java @@ -14,8 +14,6 @@ import java.util.function.Predicate; */ public class EmptyAnnotationScanner implements AnnotationScanner { - public static final EmptyAnnotationScanner INSTANCE = new EmptyAnnotationScanner(); - @Override public boolean support(AnnotatedElement annotatedEle) { return true; diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/GenericAnnotationScanner.java b/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/GenericAnnotationScanner.java new file mode 100644 index 000000000..9056af9cd --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/GenericAnnotationScanner.java @@ -0,0 +1,149 @@ +package cn.hutool.core.annotation.scanner; + +import cn.hutool.core.map.multi.ListValueMap; +import cn.hutool.core.util.ObjectUtil; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.function.BiConsumer; +import java.util.function.Predicate; + +/** + *

通用注解扫描器,支持按不同的层级结构扫描{@link AnnotatedElement}上的注解。 + * + *

当{@link AnnotatedElement}类型不同时,“层级结构”指向的对象将有所区别: + *

    + *
  • + * 当元素为{@link Method}时,此处层级结构指声明方法的类的层级结构, + * 扫描器将从层级结构中寻找与该方法签名相同的方法,并对其进行扫描; + *
  • + *
  • + * 当元素为{@link Class}时,此处层级结构即指类本身与其父类、父接口共同构成的层级结构, + * 扫描器将扫描层级结构中类、接口声明的注解; + *
  • + *
  • 当元素不为{@link Method}或{@link Class}时,则其层级结构仅有其本身一层;
  • + *
+ * 此外,扫描器支持在获取到层级结构中的注解对象后,再对注解对象的元注解进行扫描。 + * + * @author huangchengxing + * @see TypeAnnotationScanner + * @see MethodAnnotationScanner + * @see MetaAnnotationScanner + * @see ElementAnnotationScanner + */ +public class GenericAnnotationScanner implements AnnotationScanner { + + /** + * 类型扫描器 + */ + private final AnnotationScanner typeScanner; + + /** + * 方法扫描器 + */ + private final AnnotationScanner methodScanner; + + /** + * 元注解扫描器 + */ + private final AnnotationScanner metaScanner; + + /** + * 普通元素扫描器 + */ + private final AnnotationScanner elementScanner; + + /** + * 通用注解扫描器支持扫描所有类型的{@link AnnotatedElement} + * + * @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission + * @return 是否支持扫描该注解元素 + */ + @Override + public boolean support(AnnotatedElement annotatedEle) { + return true; + } + + /** + * 构造一个通用注解扫描器 + * + * @param enableScanMetaAnnotation 是否扫描注解上的元注解 + * @param enableScanSupperClass 是否扫描父类 + * @param enableScanSupperInterface 是否扫描父接口 + */ + public GenericAnnotationScanner( + boolean enableScanMetaAnnotation, + boolean enableScanSupperClass, + boolean enableScanSupperInterface) { + + this.metaScanner = enableScanMetaAnnotation ? new MetaAnnotationScanner() : new EmptyAnnotationScanner(); + this.typeScanner = new TypeAnnotationScanner( + enableScanSupperClass, enableScanSupperInterface, a -> true, Collections.emptySet() + ); + this.methodScanner = new MethodAnnotationScanner( + enableScanSupperClass, enableScanSupperInterface, a -> true, Collections.emptySet() + ); + this.elementScanner = new ElementAnnotationScanner(); + } + + /** + * 扫描注解元素的层级结构(若存在),然后对获取到的注解和注解对应的层级索引进行处理 + * + * @param consumer 对获取到的注解和注解对应的层级索引的处理 + * @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission + * @param filter 注解过滤器,无法通过过滤器的注解不会被处理。该参数允许为空。 + */ + @Override + public void scan(BiConsumer consumer, AnnotatedElement annotatedEle, Predicate filter) { + filter = ObjectUtil.defaultIfNull(filter, t -> true); + if (ObjectUtil.isNull(annotatedEle)) { + return; + } + // 注解元素是类 + if (annotatedEle instanceof Class) { + scanElements(typeScanner, consumer, annotatedEle, filter); + } + // 注解元素是方法 + else if (annotatedEle instanceof Method) { + scanElements(methodScanner, consumer, annotatedEle, filter); + } + // 注解元素是其他类型 + else { + scanElements(elementScanner, consumer, annotatedEle, filter); + } + } + + /** + * 扫描注解类的层级结构(若存在),然后对获取到的注解和注解对应的层级索引进行处理 + * + * @param scanner 使用的扫描器 + * @param consumer 对获取到的注解和注解对应的层级索引的处理 + * @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission + * @param filter 注解过滤器,无法通过过滤器的注解不会被处理。该参数允许为空。 + */ + private void scanElements( + AnnotationScanner scanner, + BiConsumer consumer, + AnnotatedElement annotatedEle, + Predicate filter) { + // 扫描类上注解 + final ListValueMap classAnnotations = new ListValueMap<>(new LinkedHashMap<>()); + scanner.scan((index, annotation) -> { + if (filter.test(annotation)) { + classAnnotations.putValue(index, annotation); + } + }, annotatedEle, filter); + + // 扫描元注解 + classAnnotations.forEach((index, annotations) -> + annotations.forEach(annotation -> { + consumer.accept(index, annotation); + metaScanner.scan(consumer, annotation.annotationType(), filter); + }) + ); + } + +} diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/MethodAnnotationScanner.java b/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/MethodAnnotationScanner.java index 18be3ea1b..a494c31fb 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/MethodAnnotationScanner.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/MethodAnnotationScanner.java @@ -20,14 +20,10 @@ import java.util.stream.Stream; public class MethodAnnotationScanner extends AbstractTypeAnnotationScanner implements AnnotationScanner { /** - * 构造一个方法注解扫描器 - * - * @param scanSameSignatureMethod 是否扫描类层级结构中具有相同方法签名的方法 - * @param filter 过滤器 - * @param excludeTypes 不包含的类型 + * 构造一个类注解扫描器,仅扫描该方法上直接声明的注解 */ - public MethodAnnotationScanner(boolean scanSameSignatureMethod, Predicate> filter, Set> excludeTypes) { - super(scanSameSignatureMethod, scanSameSignatureMethod, filter, excludeTypes); + public MethodAnnotationScanner() { + this(false); } /** @@ -40,10 +36,26 @@ public class MethodAnnotationScanner extends AbstractTypeAnnotationScanner> filter, Set> excludeTypes) { + super(scanSameSignatureMethod, scanSameSignatureMethod, filter, excludeTypes); + } + + /** + * 构造一个方法注解扫描器 + * + * @param includeSuperClass 是否允许扫描父类中具有相同方法签名的方法 + * @param includeInterfaces 是否允许扫描父接口中具有相同方法签名的方法 + * @param filter 过滤器 + * @param excludeTypes 不包含的类型 + */ + public MethodAnnotationScanner(boolean includeSuperClass, boolean includeInterfaces, Predicate> filter, Set> excludeTypes) { + super(includeSuperClass, includeInterfaces, filter, excludeTypes); } /** diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/scanner/ElementAnnotationScannerTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/scanner/ElementAnnotationScannerTest.java new file mode 100644 index 000000000..df14b847b --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/scanner/ElementAnnotationScannerTest.java @@ -0,0 +1,60 @@ +package cn.hutool.core.annotation.scanner; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ReflectUtil; +import org.junit.Assert; +import org.junit.Test; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ElementAnnotationScannerTest { + + @Test + public void supportTest() { + final ElementAnnotationScanner scanner = new ElementAnnotationScanner(); + Assert.assertTrue(scanner.support(ReflectUtil.getField(FieldAnnotationScannerTest.Example.class, "id"))); + Assert.assertTrue(scanner.support(ReflectUtil.getMethod(FieldAnnotationScannerTest.Example.class, "getId"))); + Assert.assertFalse(scanner.support(null)); + Assert.assertTrue(scanner.support(FieldAnnotationScannerTest.Example.class)); + } + + @Test + public void getAnnotationsTest() { + final ElementAnnotationScanner scanner = new ElementAnnotationScanner(); + final Field field = ReflectUtil.getField(FieldAnnotationScannerTest.Example.class, "id"); + Assert.assertNotNull(field); + Assert.assertTrue(scanner.support(field)); + List annotations = scanner.getAnnotations(field); + Assert.assertEquals(1, annotations.size()); + Assert.assertEquals(AnnotationForScannerTest.class, CollUtil.getFirst(annotations).annotationType()); + } + + @Test + public void scanTest() { + final ElementAnnotationScanner scanner = new ElementAnnotationScanner(); + final Field field = ReflectUtil.getField(FieldAnnotationScannerTest.Example.class, "id"); + final Map> map = new HashMap<>(); + scanner.scan( + (index, annotation) -> map.computeIfAbsent(index, i -> new ArrayList<>()).add(annotation), + field, null + ); + Assert.assertEquals(1, map.size()); + Assert.assertEquals(1, map.get(0).size()); + Assert.assertEquals(AnnotationForScannerTest.class, map.get(0).get(0).annotationType()); + } + + public static class Example { + @AnnotationForScannerTest + private Integer id; + + public Integer getId() { + return id; + } + } + +} diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/scanner/GenericAnnotationScannerTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/scanner/GenericAnnotationScannerTest.java new file mode 100644 index 000000000..8b4707750 --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/scanner/GenericAnnotationScannerTest.java @@ -0,0 +1,85 @@ +package cn.hutool.core.annotation.scanner; + +import org.junit.Assert; +import org.junit.Test; + +import java.lang.annotation.*; +import java.util.List; + +public class GenericAnnotationScannerTest { + + @Test + public void scanDirectlyTest() { + final GenericAnnotationScanner scanner = new GenericAnnotationScanner(false, false, false); + final List annotations = scanner.getAnnotations(ClassForTest.class); + Assert.assertEquals(1, annotations.size()); + } + + @Test + public void scanDirectlyAndMetaAnnotationTest() { + final GenericAnnotationScanner scanner = new GenericAnnotationScanner(true, false, false); + final List annotations = scanner.getAnnotations(ClassForTest.class); + Assert.assertEquals(2, annotations.size()); + } + + @Test + public void scanSuperclassTest() { + final GenericAnnotationScanner scanner = new GenericAnnotationScanner(false, true, false); + final List annotations = scanner.getAnnotations(ClassForTest.class); + Assert.assertEquals(2, annotations.size()); + } + + @Test + public void scanSuperclassAndMetaAnnotationTest() { + final GenericAnnotationScanner scanner = new GenericAnnotationScanner(true, true, false); + final List annotations = scanner.getAnnotations(ClassForTest.class); + Assert.assertEquals(4, annotations.size()); + } + + @Test + public void scanInterfaceTest() { + final GenericAnnotationScanner scanner = new GenericAnnotationScanner(false, false, true); + final List annotations = scanner.getAnnotations(ClassForTest.class); + Assert.assertEquals(2, annotations.size()); + } + + @Test + public void scanInterfaceAndMetaAnnotationTest() { + final GenericAnnotationScanner scanner = new GenericAnnotationScanner(true, false, true); + final List annotations = scanner.getAnnotations(ClassForTest.class); + Assert.assertEquals(4, annotations.size()); + } + + @Test + public void scanTypeHierarchyTest() { + final GenericAnnotationScanner scanner = new GenericAnnotationScanner(false, true, true); + final List annotations = scanner.getAnnotations(ClassForTest.class); + Assert.assertEquals(3, annotations.size()); + } + + @Test + public void scanTypeHierarchyAndMetaAnnotationTest() { + final GenericAnnotationScanner scanner = new GenericAnnotationScanner(true, true, true); + final List annotations = scanner.getAnnotations(ClassForTest.class); + Assert.assertEquals(6, annotations.size()); + } + + @Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.METHOD}) + @Retention(RetentionPolicy.RUNTIME) + public @interface MetaAnnotationForTest { } + + @MetaAnnotationForTest + @Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.METHOD}) + @Retention(RetentionPolicy.RUNTIME) + public @interface AnnotationForTest { } + + @AnnotationForTest + static class ClassForTest extends SupperForTest implements InterfaceForTest { } + + @AnnotationForTest + static class SupperForTest { } + + @AnnotationForTest + interface InterfaceForTest { } + +} From e3b296e89d843657ee4d8feb428ffdf7ee66f8ce Mon Sep 17 00:00:00 2001 From: Looly Date: Tue, 19 Jul 2022 10:57:18 +0800 Subject: [PATCH 3/6] =?UTF-8?q?=20=E4=BF=AE=E5=A4=8DDict=E7=BC=BA=E5=B0=91?= =?UTF-8?q?putIfAbsent=E3=80=81computeIfAbsent=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 ++ .../src/main/java/cn/hutool/core/map/TransMap.java | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68281999a..689a4ab6f 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ * 【core 】 ReUtil增加getAllGroups重载(pr#2455@Github) * 【core 】 PageUtil#totalPage增加totalCount为long类型的重载方法(pr#2442@Github) * 【crypto 】 PemUtil.readPemPrivateKey支持pkcs#1格式,增加OpensslKeyUtil(pr#2456@Github) +* 【core 】 添加了通用的注解扫描器 `GenericAnnotationScanner`,并在 `AnnotationScanner` 接口中统一提供了提前配置好的扫描器静态实例(pr#715@Github) * ### 🐞Bug修复 * 【core 】 修复CollUtil里面关于可变参数传null造成的crash问题(pr#2428@Github) @@ -32,6 +33,7 @@ * 【core 】 修复当时间戳为Integer时时间转换问题(pr#2449@Github) * 【core 】 修复bmp文件判断问题(issue#I5H93G@Gitee) * 【core 】 修复CombinationAnnotationElement造成递归循环(issue#I5FQGW@Gitee) +* 【core 】 修复Dict缺少putIfAbsent、computeIfAbsent问题(issue#I5FQGW@Gitee) ------------------------------------------------------------------------------------------------------------- diff --git a/hutool-core/src/main/java/cn/hutool/core/map/TransMap.java b/hutool-core/src/main/java/cn/hutool/core/map/TransMap.java index e6608aec9..08ea433b8 100755 --- a/hutool-core/src/main/java/cn/hutool/core/map/TransMap.java +++ b/hutool-core/src/main/java/cn/hutool/core/map/TransMap.java @@ -2,6 +2,7 @@ package cn.hutool.core.map; import java.util.Map; import java.util.function.BiFunction; +import java.util.function.Function; import java.util.function.Supplier; /** @@ -98,6 +99,16 @@ public abstract class TransMap extends MapWrapper { public V merge(K key, V value, BiFunction remappingFunction) { return super.merge(customKey(key), customValue(value), (v1, v2) -> remappingFunction.apply(customValue(v1), customValue(v2))); } + + @Override + public V putIfAbsent(K key, V value) { + return super.putIfAbsent(customKey(key), customValue(value)); + } + + @Override + public V computeIfAbsent(final K key, final Function mappingFunction) { + return super.computeIfAbsent(customKey(key), mappingFunction); + } //---------------------------------------------------------------------------- Override default methods end /** From 35cdf306d807167f802f32a46eb1cfaa963ee7e7 Mon Sep 17 00:00:00 2001 From: gonggy <514592053@qq.com> Date: Tue, 19 Jul 2022 19:44:28 +0800 Subject: [PATCH 4/6] =?UTF-8?q?Console.log=E5=BA=94=E8=AF=A5=E6=8A=8A?= =?UTF-8?q?=E5=BC=82=E5=B8=B8=E4=BF=A1=E6=81=AF=E6=89=93=E5=8D=B0=E5=88=B0?= =?UTF-8?q?out=E8=80=8C=E4=B8=8D=E6=98=AFerr?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hutool-core/src/main/java/cn/hutool/core/lang/Console.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/Console.java b/hutool-core/src/main/java/cn/hutool/core/lang/Console.java index 07cca75c7..643f9cb0c 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/Console.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/Console.java @@ -86,7 +86,7 @@ public class Console { out.println(StrUtil.format(template, values)); if (null != t) { //noinspection CallToPrintStackTrace - t.printStackTrace(); + t.printStackTrace(out); out.flush(); } } From 1fcee082632e889d39939e522c42c4b4d057a63c Mon Sep 17 00:00:00 2001 From: Looly Date: Wed, 20 Jul 2022 12:16:59 +0800 Subject: [PATCH 5/6] =?UTF-8?q?=E4=BF=AE=E5=A4=8DConsole.log=E5=BA=94?= =?UTF-8?q?=E8=AF=A5=E6=8A=8A=E5=BC=82=E5=B8=B8=E4=BF=A1=E6=81=AF=E8=BE=93?= =?UTF-8?q?=E5=87=BA=E4=BD=8D=E7=BD=AE=E9=94=99=E8=AF=AF=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 689a4ab6f..ec5382341 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ------------------------------------------------------------------------------------------------------------- -# 5.8.5.M1 (2022-07-17) +# 5.8.5.M1 (2022-07-20) ### ❌不兼容特性 * 【core 】 合成注解相关功能重构,增加@Link及其子注解(pr#702@Gitee) @@ -34,6 +34,7 @@ * 【core 】 修复bmp文件判断问题(issue#I5H93G@Gitee) * 【core 】 修复CombinationAnnotationElement造成递归循环(issue#I5FQGW@Gitee) * 【core 】 修复Dict缺少putIfAbsent、computeIfAbsent问题(issue#I5FQGW@Gitee) +* 【core 】 修复Console.log应该把异常信息输出位置错误问题(pr#716@Gitee) ------------------------------------------------------------------------------------------------------------- From 88c36b8bfa2e0a1160d5cff140c6ecf3f4dccd66 Mon Sep 17 00:00:00 2001 From: Looly Date: Wed, 20 Jul 2022 13:13:14 +0800 Subject: [PATCH 6/6] =?UTF-8?q?=E4=BF=AE=E5=A4=8DUrlBuilder=E6=97=A0?= =?UTF-8?q?=E6=B3=95=E9=85=8D=E7=BD=AE=E6=9C=AB=E5=B0=BE=E8=BF=BD=E5=8A=A0?= =?UTF-8?q?=E2=80=9C/=E2=80=9D=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + .../cn/hutool/core/net/url/UrlBuilder.java | 20 +++++- .../java/cn/hutool/core/net/url/UrlPath.java | 20 +++--- .../cn/hutool/core/net/UrlBuilderTest.java | 69 +++++++++++-------- 4 files changed, 71 insertions(+), 39 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec5382341..5fd4d3e80 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ * 【core 】 修复CombinationAnnotationElement造成递归循环(issue#I5FQGW@Gitee) * 【core 】 修复Dict缺少putIfAbsent、computeIfAbsent问题(issue#I5FQGW@Gitee) * 【core 】 修复Console.log应该把异常信息输出位置错误问题(pr#716@Gitee) +* 【core 】 修复UrlBuilder无法配置末尾追加“/”问题(issue#2459@Github) ------------------------------------------------------------------------------------------------------------- diff --git a/hutool-core/src/main/java/cn/hutool/core/net/url/UrlBuilder.java b/hutool-core/src/main/java/cn/hutool/core/net/url/UrlBuilder.java index ccf54a4ee..76199023a 100755 --- a/hutool-core/src/main/java/cn/hutool/core/net/url/UrlBuilder.java +++ b/hutool-core/src/main/java/cn/hutool/core/net/url/UrlBuilder.java @@ -119,7 +119,7 @@ public final class UrlBuilder implements Builder { /** * 使用URL字符串构建UrlBuilder,默认使用UTF-8编码 * - * @param url URL字符串 + * @param url URL字符串 * @return UrlBuilder */ public static UrlBuilder of(String url) { @@ -318,6 +318,22 @@ public final class UrlBuilder implements Builder { return null == this.path ? StrUtil.SLASH : this.path.build(charset, this.needEncodePercent); } + /** + * 是否path的末尾加 / + * + * @param withEngTag 是否path的末尾加 / + * @return this + * @since 5.8.5 + */ + public UrlBuilder setWithEndTag(boolean withEngTag) { + if (null == this.path) { + this.path = new UrlPath(); + } + + this.path.setWithEndTag(withEngTag); + return this; + } + /** * 设置路径,例如/aa/bb/cc,将覆盖之前所有的path相关设置 * @@ -501,7 +517,7 @@ public final class UrlBuilder implements Builder { final StringBuilder fileBuilder = new StringBuilder(); // path - fileBuilder.append(StrUtil.blankToDefault(getPathStr(), StrUtil.SLASH)); + fileBuilder.append(getPathStr()); // query final String query = getQueryStr(); diff --git a/hutool-core/src/main/java/cn/hutool/core/net/url/UrlPath.java b/hutool-core/src/main/java/cn/hutool/core/net/url/UrlPath.java index 39a04ba71..868025993 100644 --- a/hutool-core/src/main/java/cn/hutool/core/net/url/UrlPath.java +++ b/hutool-core/src/main/java/cn/hutool/core/net/url/UrlPath.java @@ -142,12 +142,13 @@ public class UrlPath { */ public String build(Charset charset, boolean encodePercent) { if (CollUtil.isEmpty(this.segments)) { - return StrUtil.EMPTY; + // 没有节点的path取决于是否末尾追加/,如果不追加返回空串,否则返回/ + return withEngTag ? StrUtil.SLASH : StrUtil.EMPTY; } final char[] safeChars = encodePercent ? null : new char[]{'%'}; final StringBuilder builder = new StringBuilder(); - for (String segment : segments) { + for (final String segment : segments) { if(builder.length() == 0){ // 根据https://www.ietf.org/rfc/rfc3986.html#section-3.3定义 // path的第一部分不允许有":",其余部分允许 @@ -157,12 +158,15 @@ public class UrlPath { builder.append(CharUtil.SLASH).append(RFC3986.SEGMENT.encode(segment, charset, safeChars)); } } - if (StrUtil.isEmpty(builder)) { - // 空白追加是保证以/开头 - builder.append(CharUtil.SLASH); - }else if (withEngTag && false == StrUtil.endWith(builder, CharUtil.SLASH)) { - // 尾部没有/则追加,否则不追加 - builder.append(CharUtil.SLASH); + + if(withEngTag){ + if (StrUtil.isEmpty(builder)) { + // 空白追加是保证以/开头 + builder.append(CharUtil.SLASH); + }else if (false == StrUtil.endWith(builder, CharUtil.SLASH)) { + // 尾部没有/则追加,否则不追加 + builder.append(CharUtil.SLASH); + } } return builder.toString(); diff --git a/hutool-core/src/test/java/cn/hutool/core/net/UrlBuilderTest.java b/hutool-core/src/test/java/cn/hutool/core/net/UrlBuilderTest.java index 7d54c4503..5f869e70e 100644 --- a/hutool-core/src/test/java/cn/hutool/core/net/UrlBuilderTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/net/UrlBuilderTest.java @@ -16,20 +16,31 @@ public class UrlBuilderTest { @Test public void buildTest() { - String buildUrl = UrlBuilder.create().setHost("www.hutool.cn").build(); + final String buildUrl = UrlBuilder.create().setHost("www.hutool.cn").build(); Assert.assertEquals("http://www.hutool.cn/", buildUrl); } + @Test + public void buildWithoutSlashTest(){ + // https://github.com/dromara/hutool/issues/2459 + String buildUrl = UrlBuilder.create().setScheme("http").setHost("192.168.1.1").setPort(8080).setWithEndTag(false).build(); + Assert.assertEquals("http://192.168.1.1:8080", buildUrl); + + buildUrl = UrlBuilder.create().setScheme("http").setHost("192.168.1.1").setPort(8080).addQuery("url", "http://192.168.1.1/test/1") + .setWithEndTag(false).build(); + Assert.assertEquals("http://192.168.1.1:8080?url=http://192.168.1.1/test/1", buildUrl); + } + @Test public void buildTest2() { // path中的+不做处理 - String buildUrl = UrlBuilder.ofHttp("http://www.hutool.cn/+8618888888888", CharsetUtil.CHARSET_UTF_8).build(); + final String buildUrl = UrlBuilder.ofHttp("http://www.hutool.cn/+8618888888888", CharsetUtil.CHARSET_UTF_8).build(); Assert.assertEquals("http://www.hutool.cn/+8618888888888", buildUrl); } @Test public void testHost() { - String buildUrl = UrlBuilder.create() + final String buildUrl = UrlBuilder.create() .setScheme("https") .setHost("www.hutool.cn").build(); Assert.assertEquals("https://www.hutool.cn/", buildUrl); @@ -37,7 +48,7 @@ public class UrlBuilderTest { @Test public void testHostPort() { - String buildUrl = UrlBuilder.create() + final String buildUrl = UrlBuilder.create() .setScheme("https") .setHost("www.hutool.cn") .setPort(8080) @@ -87,7 +98,7 @@ public class UrlBuilderTest { @Test public void testFragment() { - String buildUrl = new UrlBuilder() + final String buildUrl = new UrlBuilder() .setScheme("https") .setHost("www.hutool.cn") .setFragment("abc").build(); @@ -96,7 +107,7 @@ public class UrlBuilderTest { @Test public void testChineseFragment() { - String buildUrl = new UrlBuilder() + final String buildUrl = new UrlBuilder() .setScheme("https") .setHost("www.hutool.cn") .setFragment("测试").build(); @@ -105,7 +116,7 @@ public class UrlBuilderTest { @Test public void testChineseFragmentWithPath() { - String buildUrl = new UrlBuilder() + final String buildUrl = new UrlBuilder() .setScheme("https") .setHost("www.hutool.cn") .addPath("/s") @@ -115,7 +126,7 @@ public class UrlBuilderTest { @Test public void testChineseFragmentWithPathAndQuery() { - String buildUrl = new UrlBuilder() + final String buildUrl = new UrlBuilder() .setScheme("https") .setHost("www.hutool.cn") .addPath("/s") @@ -194,7 +205,7 @@ public class UrlBuilderTest { @Test public void weixinUrlTest(){ - String urlStr = "https://mp.weixin.qq.com/s?" + + final String urlStr = "https://mp.weixin.qq.com/s?" + "__biz=MzI5NjkyNTIxMg==" + "&mid=100000465" + "&idx=1" + @@ -240,14 +251,14 @@ public class UrlBuilderTest { @Test public void toURITest() throws URISyntaxException { - String webUrl = "http://exmple.com/patha/pathb?a=123"; // 报错数据 + final String webUrl = "http://exmple.com/patha/pathb?a=123"; // 报错数据 final UrlBuilder urlBuilder = UrlBuilder.of(webUrl, StandardCharsets.UTF_8); Assert.assertEquals(new URI(webUrl), urlBuilder.toURI()); } @Test public void testEncodeInQuery() { - String webUrl = "http://exmple.com/patha/pathb?a=123&b=4?6&c=789"; // b=4?6 参数中有未编码的? + final String webUrl = "http://exmple.com/patha/pathb?a=123&b=4?6&c=789"; // b=4?6 参数中有未编码的? final UrlBuilder urlBuilder = UrlBuilder.of(webUrl, StandardCharsets.UTF_8); Assert.assertEquals("a=123&b=4?6&c=789", urlBuilder.getQueryStr()); } @@ -271,11 +282,11 @@ public class UrlBuilderTest { @Test public void gimg2Test(){ - String url = "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic.jj20.com%2Fup%2Fallimg%2F1114%2F0H320120Z3%2F200H3120Z3-6-1200.jpg&refer=http%3A%2F%2Fpic.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1621996490&t=8c384c2823ea453da15a1b9cd5183eea"; + final String url = "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic.jj20.com%2Fup%2Fallimg%2F1114%2F0H320120Z3%2F200H3120Z3-6-1200.jpg&refer=http%3A%2F%2Fpic.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1621996490&t=8c384c2823ea453da15a1b9cd5183eea"; final UrlBuilder urlBuilder = UrlBuilder.of(url); // PATH除了第一个path外,:是允许的 - String url2 = "https://gimg2.baidu.com/image_search/src=http:%2F%2Fpic.jj20.com%2Fup%2Fallimg%2F1114%2F0H320120Z3%2F200H3120Z3-6-1200.jpg&refer=http:%2F%2Fpic.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1621996490&t=8c384c2823ea453da15a1b9cd5183eea"; + final String url2 = "https://gimg2.baidu.com/image_search/src=http:%2F%2Fpic.jj20.com%2Fup%2Fallimg%2F1114%2F0H320120Z3%2F200H3120Z3-6-1200.jpg&refer=http:%2F%2Fpic.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1621996490&t=8c384c2823ea453da15a1b9cd5183eea"; Assert.assertEquals(url2, urlBuilder.toString()); } @@ -283,7 +294,7 @@ public class UrlBuilderTest { public void fragmentEncodeTest(){ // https://gitee.com/dromara/hutool/issues/I49KAL // 见:https://stackoverflow.com/questions/26088849/url-fragment-allowed-characters - String url = "https://hutool.cn/docs/#/?id=简介"; + final String url = "https://hutool.cn/docs/#/?id=简介"; UrlBuilder urlBuilder = UrlBuilder.ofHttp(url); Assert.assertEquals("https://hutool.cn/docs/#/?id=%E7%AE%80%E4%BB%8B", urlBuilder.toString()); @@ -296,14 +307,14 @@ public class UrlBuilderTest { // https://github.com/dromara/hutool/issues/1904 // 在query中,"/"是不可转义字符 // 见:https://www.rfc-editor.org/rfc/rfc3986.html#section-3.4 - String url = "https://invoice.maycur.com/2b27a802-8423-4d41-86f5-63a6b259f61e.xlsx?download/2b27a802-8423-4d41-86f5-63a6b259f61e.xlsx&e=1630491088"; + final String url = "https://invoice.maycur.com/2b27a802-8423-4d41-86f5-63a6b259f61e.xlsx?download/2b27a802-8423-4d41-86f5-63a6b259f61e.xlsx&e=1630491088"; final UrlBuilder urlBuilder = UrlBuilder.ofHttp(url); Assert.assertEquals(url, urlBuilder.toString()); } @Test public void addPathEncodeTest(){ - String url = UrlBuilder.create() + final String url = UrlBuilder.create() .setScheme("https") .setHost("domain.cn") .addPath("api") @@ -317,7 +328,7 @@ public class UrlBuilderTest { @Test public void addPathEncodeTest2(){ // https://github.com/dromara/hutool/issues/1912 - String url = UrlBuilder.create() + final String url = UrlBuilder.create() .setScheme("https") .setHost("domain.cn") .addPath("/api/xxx/bbb") @@ -328,14 +339,14 @@ public class UrlBuilderTest { @Test public void percent2BTest(){ - String url = "http://xxx.cn/a?Signature=3R013Bj9Uq4YeISzAs2iC%2BTVCL8%3D"; + final String url = "http://xxx.cn/a?Signature=3R013Bj9Uq4YeISzAs2iC%2BTVCL8%3D"; final UrlBuilder of = UrlBuilder.ofHttpWithoutEncode(url); Assert.assertEquals(url, of.toString()); } @Test public void paramTest(){ - String url = "http://ci.xiaohongshu.com/spectrum/c136c98aa2047babe25b994a26ffa7b492bd8058?imageMogr2/thumbnail/x800/format/jpg"; + final String url = "http://ci.xiaohongshu.com/spectrum/c136c98aa2047babe25b994a26ffa7b492bd8058?imageMogr2/thumbnail/x800/format/jpg"; final UrlBuilder builder = UrlBuilder.ofHttp(url); Assert.assertEquals(url, builder.toString()); } @@ -343,7 +354,7 @@ public class UrlBuilderTest { @Test public void fragmentTest(){ // https://gitee.com/dromara/hutool/issues/I49KAL#note_8060874 - String url = "https://www.hutool.cn/#/a/b?timestamp=1640391380204"; + final String url = "https://www.hutool.cn/#/a/b?timestamp=1640391380204"; final UrlBuilder builder = UrlBuilder.ofHttp(url); Assert.assertEquals(url, builder.toString()); @@ -352,7 +363,7 @@ public class UrlBuilderTest { @Test public void fragmentAppendParamTest(){ // https://gitee.com/dromara/hutool/issues/I49KAL#note_8060874 - String url = "https://www.hutool.cn/#/a/b"; + final String url = "https://www.hutool.cn/#/a/b"; final UrlBuilder builder = UrlBuilder.ofHttp(url); builder.setFragment(builder.getFragment() + "?timestamp=1640391380204"); Assert.assertEquals("https://www.hutool.cn/#/a/b?timestamp=1640391380204", builder.toString()); @@ -360,7 +371,7 @@ public class UrlBuilderTest { @Test public void paramWithPlusTest(){ - String url = "http://127.0.0.1/?" + + final String url = "http://127.0.0.1/?" + "Expires=1642734164&" + "security-token=CAIS+AF1q6Ft5B2yfSjIr5fYEeju1b1ggpPee2KGpjlgQtdfl43urjz2IHtKdXRvBu8Xs" + "/4wnmxX7f4YlqB6T55OSAmcNZEoPwKpT4zmMeT7oMWQweEurv" + @@ -376,7 +387,7 @@ public class UrlBuilderTest { @Test public void issueI4Z2ETTest(){ // =是url参数值中的合法字符,但是某些URL强制编码了 - String url = "http://dsl-fd.dslbuy.com/fssc/1647947565522.pdf?" + + final String url = "http://dsl-fd.dslbuy.com/fssc/1647947565522.pdf?" + "Expires=1647949365" + "&OSSAccessKeyId=STS.NTZ9hvqPSLG8ENknz2YaByLKj" + "&Signature=oYUu26JufAyPY4PdzaOp1x4sr4Q%3D"; @@ -387,22 +398,22 @@ public class UrlBuilderTest { @Test public void issue2215Test(){ - String url = "https://hutool.cn/v1/104303371/messages:send"; + final String url = "https://hutool.cn/v1/104303371/messages:send"; final String build = UrlBuilder.of(url).build(); Assert.assertEquals(url, build); } @Test public void issuesI4Z2ETTest(){ - String url = "http://hutool.cn/2022/03/09/123.zip?Expires=1648704684&OSSAccessKeyId=LTAI4FncgaVtwZGBnYHHi8ox&Signature=%2BK%2B%3D"; + final String url = "http://hutool.cn/2022/03/09/123.zip?Expires=1648704684&OSSAccessKeyId=LTAI4FncgaVtwZGBnYHHi8ox&Signature=%2BK%2B%3D"; final String build = UrlBuilder.of(url, null).build(); Assert.assertEquals(url, build); } @Test public void issueI50NHQTest(){ - String url = "http://127.0.0.1/devicerecord/list"; - HashMap params = new LinkedHashMap<>(); + final String url = "http://127.0.0.1/devicerecord/list"; + final HashMap params = new LinkedHashMap<>(); params.put("start", "2022-03-31 00:00:00"); params.put("end", "2022-03-31 23:59:59"); params.put("page", 1); @@ -422,7 +433,7 @@ public class UrlBuilderTest { public void issue2243Test(){ // https://github.com/dromara/hutool/issues/2243 // 如果用户已经做了%编码,不应该重复编码 - String url = "https://hutool.cn/v1.0?privateNum=%2B8616512884988"; + final String url = "https://hutool.cn/v1.0?privateNum=%2B8616512884988"; final String s = UrlBuilder.of(url, null).setCharset(CharsetUtil.CHARSET_UTF_8).toString(); Assert.assertEquals(url, s); } @@ -430,7 +441,7 @@ public class UrlBuilderTest { @Test public void issueI51T0VTest(){ // &自动转换为& - String url = "https://hutool.cn/a.mp3?Expires=1652423884&key=JMv2rKNc7Pz&sign=12zva00BpVqgZcX1wcb%2BrmN7H3E%3D"; + final String url = "https://hutool.cn/a.mp3?Expires=1652423884&key=JMv2rKNc7Pz&sign=12zva00BpVqgZcX1wcb%2BrmN7H3E%3D"; final UrlBuilder of = UrlBuilder.of(url, null); Assert.assertEquals(url.replace("&", "&"), of.toString()); }