mirror of
https://gitee.com/dromara/hutool.git
synced 2025-04-05 17:37:59 +08:00
Merge branch 'v5-dev' of https://gitee.com/hellozrh/hutool into v5-dev
This commit is contained in:
commit
0da4339f51
@ -3,7 +3,7 @@
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
|
||||
# 5.8.5.M1 (2022-07-17)
|
||||
# 5.8.5.M1 (2022-07-20)
|
||||
|
||||
### ❌不兼容特性
|
||||
* 【core 】 合成注解相关功能重构,增加@Link及其子注解(pr#702@Gitee)
|
||||
@ -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,9 @@
|
||||
* 【core 】 修复当时间戳为Integer时时间转换问题(pr#2449@Github)
|
||||
* 【core 】 修复bmp文件判断问题(issue#I5H93G@Gitee)
|
||||
* 【core 】 修复CombinationAnnotationElement造成递归循环(issue#I5FQGW@Gitee)
|
||||
* 【core 】 修复Dict缺少putIfAbsent、computeIfAbsent问题(issue#I5FQGW@Gitee)
|
||||
* 【core 】 修复Console.log应该把异常信息输出位置错误问题(pr#716@Gitee)
|
||||
* 【core 】 修复UrlBuilder无法配置末尾追加“/”问题(issue#2459@Github)
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
@ -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<Annotation> scanMetaAnnotation(Class<? extends Annotation> 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<Annotation> 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<Annotation> 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 <T extends Annotation> List<T> getAllSynthesizedAnnotations(AnnotatedElement annotatedEle, Class<T> 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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -60,9 +60,9 @@ public abstract class AbstractTypeAnnotationScanner<T extends AbstractTypeAnnota
|
||||
* 构造一个类注解扫描器
|
||||
*
|
||||
* @param includeSuperClass 是否允许扫描父类
|
||||
* @param includeInterfaces 是否允许扫描父接口
|
||||
* @param filter 过滤器
|
||||
* @param excludeTypes 不包含的类型
|
||||
* @param includeInterfaces 是否允许扫描父接口
|
||||
* @param filter 过滤器
|
||||
* @param excludeTypes 不包含的类型
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected AbstractTypeAnnotationScanner(boolean includeSuperClass, boolean includeInterfaces, Predicate<Class<?>> filter, Set<Class<?>> excludeTypes) {
|
||||
@ -186,7 +186,7 @@ public abstract class AbstractTypeAnnotationScanner<T extends AbstractTypeAnnota
|
||||
// 处理层级索引和注解
|
||||
final Annotation[] targetAnnotations = getAnnotationsFromTargetClass(annotatedEle, index, targetClass);
|
||||
for (final Annotation annotation : targetAnnotations) {
|
||||
if (AnnotationUtil.isNotJdkMateAnnotation(annotation.annotationType()) || filter.test(annotation)) {
|
||||
if (AnnotationUtil.isNotJdkMateAnnotation(annotation.annotationType()) && filter.test(annotation)) {
|
||||
consumer.accept(index, annotation);
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@ -16,16 +17,120 @@ import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 注解扫描器,用于从支持的可注解元素上获取所需注解
|
||||
* <p>注解扫描器,用于从支持的可注解元素上获取所需注解
|
||||
*
|
||||
* <p>默认提供了以下扫描方式:
|
||||
* <ul>
|
||||
* <li>{@link #NOTHING}:什么都不做,什么注解都不扫描;</li>
|
||||
* <li>{@link #DIRECTLY}:扫描元素本身直接声明的注解,包括父类带有{@link Inherited}、被传递到元素上的注解;</li>
|
||||
* <li>
|
||||
* {@link #DIRECTLY_AND_META_ANNOTATION}:扫描元素本身直接声明的注解,包括父类带有{@link Inherited}、被传递到元素上的注解,
|
||||
* 以及这些注解的元注解;
|
||||
* </li>
|
||||
* <li>{@link #SUPERCLASS}:扫描元素本身以及父类的层级结构中声明的注解;</li>
|
||||
* <li>{@link #SUPERCLASS_AND_META_ANNOTATION}:扫描元素本身以及父类的层级结构中声明的注解,以及这些注解的元注解;</li>
|
||||
* <li>{@link #INTERFACE}:扫描元素本身以及父接口的层级结构中声明的注解;</li>
|
||||
* <li>{@link #INTERFACE_AND_META_ANNOTATION}:扫描元素本身以及父接口的层级结构中声明的注解,以及这些注解的元注解;</li>
|
||||
* <li>{@link #TYPE_HIERARCHY}:扫描元素本身以及父类、父接口的层级结构中声明的注解;</li>
|
||||
* <li>{@link #TYPE_HIERARCHY_AND_META_ANNOTATION}:扫描元素本身以及父接口、父接口的层级结构中声明的注解,以及这些注解的元注解;</li>
|
||||
* </ul>
|
||||
*
|
||||
* @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<Annotation> 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<Annotation> 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<Annotation> getIfSupport(AnnotatedElement annotatedEle) {
|
||||
default List<Annotation> 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<Annotation> 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<Annotation> 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());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<Integer, Annotation> consumer, AnnotatedElement annotatedEle, Predicate<Annotation> filter) {
|
||||
filter = ObjectUtil.defaultIfNull(filter, t -> true);
|
||||
Stream.of(annotatedEle.getAnnotations())
|
||||
.filter(filter)
|
||||
.forEach(annotation -> consumer.accept(0, annotation));
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
/**
|
||||
* <p>通用注解扫描器,支持按不同的层级结构扫描{@link AnnotatedElement}上的注解。
|
||||
*
|
||||
* <p>当{@link AnnotatedElement}类型不同时,“层级结构”指向的对象将有所区别:
|
||||
* <ul>
|
||||
* <li>
|
||||
* 当元素为{@link Method}时,此处层级结构指声明方法的类的层级结构,
|
||||
* 扫描器将从层级结构中寻找与该方法签名相同的方法,并对其进行扫描;
|
||||
* </li>
|
||||
* <li>
|
||||
* 当元素为{@link Class}时,此处层级结构即指类本身与其父类、父接口共同构成的层级结构,
|
||||
* 扫描器将扫描层级结构中类、接口声明的注解;
|
||||
* </li>
|
||||
* <li>当元素不为{@link Method}或{@link Class}时,则其层级结构仅有其本身一层;</li>
|
||||
* </ul>
|
||||
* 此外,扫描器支持在获取到层级结构中的注解对象后,再对注解对象的元注解进行扫描。
|
||||
*
|
||||
* @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<Integer, Annotation> consumer, AnnotatedElement annotatedEle, Predicate<Annotation> 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<Integer, Annotation> consumer,
|
||||
AnnotatedElement annotatedEle,
|
||||
Predicate<Annotation> filter) {
|
||||
// 扫描类上注解
|
||||
final ListValueMap<Integer, Annotation> 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);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -20,14 +20,10 @@ import java.util.stream.Stream;
|
||||
public class MethodAnnotationScanner extends AbstractTypeAnnotationScanner<MethodAnnotationScanner> implements AnnotationScanner {
|
||||
|
||||
/**
|
||||
* 构造一个方法注解扫描器
|
||||
*
|
||||
* @param scanSameSignatureMethod 是否扫描类层级结构中具有相同方法签名的方法
|
||||
* @param filter 过滤器
|
||||
* @param excludeTypes 不包含的类型
|
||||
* 构造一个类注解扫描器,仅扫描该方法上直接声明的注解
|
||||
*/
|
||||
public MethodAnnotationScanner(boolean scanSameSignatureMethod, Predicate<Class<?>> filter, Set<Class<?>> excludeTypes) {
|
||||
super(scanSameSignatureMethod, scanSameSignatureMethod, filter, excludeTypes);
|
||||
public MethodAnnotationScanner() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -40,10 +36,26 @@ public class MethodAnnotationScanner extends AbstractTypeAnnotationScanner<Metho
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造一个类注解扫描器,仅扫描该方法上直接声明的注解
|
||||
* 构造一个方法注解扫描器
|
||||
*
|
||||
* @param scanSameSignatureMethod 是否扫描类层级结构中具有相同方法签名的方法
|
||||
* @param filter 过滤器
|
||||
* @param excludeTypes 不包含的类型
|
||||
*/
|
||||
public MethodAnnotationScanner() {
|
||||
this(false);
|
||||
public MethodAnnotationScanner(boolean scanSameSignatureMethod, Predicate<Class<?>> filter, Set<Class<?>> excludeTypes) {
|
||||
super(scanSameSignatureMethod, scanSameSignatureMethod, filter, excludeTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造一个方法注解扫描器
|
||||
*
|
||||
* @param includeSuperClass 是否允许扫描父类中具有相同方法签名的方法
|
||||
* @param includeInterfaces 是否允许扫描父接口中具有相同方法签名的方法
|
||||
* @param filter 过滤器
|
||||
* @param excludeTypes 不包含的类型
|
||||
*/
|
||||
public MethodAnnotationScanner(boolean includeSuperClass, boolean includeInterfaces, Predicate<Class<?>> filter, Set<Class<?>> excludeTypes) {
|
||||
super(includeSuperClass, includeInterfaces, filter, excludeTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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<String, Object> implements BasicTypeGett
|
||||
public Object merge(final String key, final Object value, final BiFunction<? super Object, ? super Object, ?> 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<? super String, ?> mappingFunction) {
|
||||
return super.computeIfAbsent(customKey(key), mappingFunction);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------- Override default methods end
|
||||
|
||||
/**
|
||||
|
@ -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<K, V> extends MapWrapper<K, V> {
|
||||
public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> 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<? super K, ? extends V> mappingFunction) {
|
||||
return super.computeIfAbsent(customKey(key), mappingFunction);
|
||||
}
|
||||
//---------------------------------------------------------------------------- Override default methods end
|
||||
|
||||
/**
|
||||
|
@ -119,7 +119,7 @@ public final class UrlBuilder implements Builder<String> {
|
||||
/**
|
||||
* 使用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<String> {
|
||||
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<String> {
|
||||
final StringBuilder fileBuilder = new StringBuilder();
|
||||
|
||||
// path
|
||||
fileBuilder.append(StrUtil.blankToDefault(getPathStr(), StrUtil.SLASH));
|
||||
fileBuilder.append(getPathStr());
|
||||
|
||||
// query
|
||||
final String query = getQueryStr();
|
||||
|
@ -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();
|
||||
|
@ -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<Annotation> 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<Integer, List<Annotation>> 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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<Annotation> annotations = scanner.getAnnotations(ClassForTest.class);
|
||||
Assert.assertEquals(1, annotations.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void scanDirectlyAndMetaAnnotationTest() {
|
||||
final GenericAnnotationScanner scanner = new GenericAnnotationScanner(true, false, false);
|
||||
final List<Annotation> annotations = scanner.getAnnotations(ClassForTest.class);
|
||||
Assert.assertEquals(2, annotations.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void scanSuperclassTest() {
|
||||
final GenericAnnotationScanner scanner = new GenericAnnotationScanner(false, true, false);
|
||||
final List<Annotation> annotations = scanner.getAnnotations(ClassForTest.class);
|
||||
Assert.assertEquals(2, annotations.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void scanSuperclassAndMetaAnnotationTest() {
|
||||
final GenericAnnotationScanner scanner = new GenericAnnotationScanner(true, true, false);
|
||||
final List<Annotation> annotations = scanner.getAnnotations(ClassForTest.class);
|
||||
Assert.assertEquals(4, annotations.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void scanInterfaceTest() {
|
||||
final GenericAnnotationScanner scanner = new GenericAnnotationScanner(false, false, true);
|
||||
final List<Annotation> annotations = scanner.getAnnotations(ClassForTest.class);
|
||||
Assert.assertEquals(2, annotations.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void scanInterfaceAndMetaAnnotationTest() {
|
||||
final GenericAnnotationScanner scanner = new GenericAnnotationScanner(true, false, true);
|
||||
final List<Annotation> annotations = scanner.getAnnotations(ClassForTest.class);
|
||||
Assert.assertEquals(4, annotations.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void scanTypeHierarchyTest() {
|
||||
final GenericAnnotationScanner scanner = new GenericAnnotationScanner(false, true, true);
|
||||
final List<Annotation> annotations = scanner.getAnnotations(ClassForTest.class);
|
||||
Assert.assertEquals(3, annotations.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void scanTypeHierarchyAndMetaAnnotationTest() {
|
||||
final GenericAnnotationScanner scanner = new GenericAnnotationScanner(true, true, true);
|
||||
final List<Annotation> 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 { }
|
||||
|
||||
}
|
@ -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<String, Object> params = new LinkedHashMap<>();
|
||||
final String url = "http://127.0.0.1/devicerecord/list";
|
||||
final HashMap<String, Object> 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());
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user