, MetaAnnotation> getMetaAnnotationMap() {
+ return metaAnnotationMap;
+ }
+
+ /**
+ * 获取根注解类型
+ *
+ * @return java.lang.Class extends java.lang.annotation.Annotation>
+ */
+ @Override
+ public Class extends Annotation> annotationType() {
+ return getSource().annotationType();
+ }
+
+ /**
+ * 根据指定的属性名与属性类型获取对应的属性值,若存在{@link Alias}则获取{@link Alias#value()}指定的别名属性的值
+ * 当不同层级的注解之间存在同名同类型属性时,将优先获取更接近根注解的属性
+ *
+ * @param attributeName 属性名
+ */
+ public Object getAttribute(String attributeName, Class> attributeType) {
+ Map, Object> values = attributeCaches.computeIfAbsent(attributeName, t -> MapUtil.newHashMap());
+ return values.computeIfAbsent(attributeType, a -> metaAnnotationMap.values()
+ .stream()
+ .filter(ma -> ma.hasAttribute(attributeName, attributeType)) // 集合默认是根据distance有序的,故此处无需再排序
+ .findFirst()
+ .map(ma -> ma.getAttribute(attributeName))
+ .orElse(null)
+ );
+ }
+
+ /**
+ * 若合成注解在存在指定元注解,则使用动态代理生成一个对应的注解实例
+ *
+ * @param annotationType 注解类型
+ * @param 注解类型
+ * @return 注解
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public T getAnnotation(Class annotationType) {
+ if (metaAnnotationMap.containsKey(annotationType)) {
+ return (T) Proxy.newProxyInstance(
+ annotationType.getClassLoader(),
+ new Class[]{annotationType, Synthesized.class},
+ new SyntheticAnnotationProxy<>(this, annotationType)
+ );
+ }
+ return null;
+ }
+
+ /**
+ * 获取全部注解
+ *
+ * @return java.lang.annotation.Annotation[]
+ */
+ @Override
+ public Annotation[] getAnnotations() {
+ return getMetaAnnotationMap().values().toArray(new MetaAnnotation[0]);
+ }
+
+ /**
+ * 获取根注解直接声明的注解,该方法正常情况下当只返回原注解
+ *
+ * @return 直接声明注解
+ */
+ @Override
+ public Annotation[] getDeclaredAnnotations() {
+ return new Annotation[]{ getSource() };
+ }
+
+ /**
+ * 广度优先遍历并缓存该根注解上的全部元注解
+ */
+ private void loadMetaAnnotations() {
+ // 若该注解已经是合成注解,则直接使用已解析好的元注解信息
+ if (source instanceof SyntheticAnnotation.Synthesized) {
+ this.metaAnnotationMap.putAll(((Synthesized)source).getMetaAnnotationMap());
+ return;
+ }
+ // 扫描元注解
+ metaAnnotationMap.put(source.annotationType(), new MetaAnnotation(source, 0));
+ new MateAnnotationScanner().scan(
+ (index, annotation) -> metaAnnotationMap.computeIfAbsent(
+ // 当出现重复的注解时,由于后添加的注解必然层级更高,优先级更低,因此当直接忽略
+ annotation.annotationType(), t -> new MetaAnnotation(annotation, index)
+ ),
+ source.annotationType(), null
+ );
+ }
+
+ /**
+ * 元注解包装类
+ *
+ * @author huangchengxing
+ */
+ static class MetaAnnotation implements Annotation {
+
+ private final Annotation annotation;
+ private final Map attributeMethodCaches;
+ private final int distance;
+
+ public MetaAnnotation(Annotation annotation, int distance) {
+ this.annotation = annotation;
+ this.distance = distance;
+ this.attributeMethodCaches = AnnotationUtil.getAttributeMethods(annotation.annotationType());
+ }
+
+ /**
+ * 获取注解类型
+ *
+ * @return 注解类型
+ */
+ @Override
+ public Class extends Annotation> annotationType() {
+ return annotation.annotationType();
+ }
+
+ /**
+ * 获取元注解
+ *
+ * @return 元注解
+ */
+ public Annotation get() {
+ return annotation;
+ }
+
+ /**
+ * 获取根注解到元注解的距离
+ *
+ * @return 根注解到元注解的距离
+ */
+ public int getDistance() {
+ return distance;
+ }
+
+ /**
+ * 元注解是否存在该属性
+ *
+ * @param attributeName 属性名
+ * @return 是否存在该属性
+ */
+ public boolean hasAttribute(String attributeName) {
+ return attributeMethodCaches.containsKey(attributeName);
+ }
+
+ /**
+ * 元注解是否存在该属性,且该属性的值类型是指定类型或其子类
+ *
+ * @param attributeName 属性名
+ * @param returnType 返回值类型
+ * @return 是否存在该属性
+ */
+ public boolean hasAttribute(String attributeName, Class> returnType) {
+ return Opt.ofNullable(attributeMethodCaches.get(attributeName))
+ .filter(method -> ClassUtil.isAssignable(returnType, method.getReturnType()))
+ .isPresent();
+ }
+
+ /**
+ * 获取元注解的属性值
+ *
+ * @param attributeName 属性名
+ * @return 元注解的属性值
+ */
+ public Object getAttribute(String attributeName) {
+ return Opt.ofNullable(attributeMethodCaches.get(attributeName))
+ .map(method -> ReflectUtil.invoke(annotation, method))
+ .orElse(null);
+ }
+
+ }
+
+ /**
+ * 表示一个已经被合成的注解
+ *
+ * @author huangchengxing
+ */
+ interface Synthesized {
+
+ /**
+ * 获取合成注解中已解析的元注解信息
+ *
+ * @return 合成注解中已解析的元注解信息
+ */
+ Map, MetaAnnotation> getMetaAnnotationMap();
+
+ static boolean isMetaAnnotationMapMethod(Method method) {
+ return StrUtil.equals("getMetaAnnotationMap", method.getName());
+ }
+
+ }
+
+ /**
+ * 合成注解代理类
+ *
+ * @author huangchengxing
+ */
+ static class SyntheticAnnotationProxy implements Annotation, InvocationHandler {
+
+ private final Class annotationType;
+ private final SyntheticAnnotation> syntheticAnnotation;
+
+ public SyntheticAnnotationProxy(SyntheticAnnotation> syntheticAnnotation, Class annotationType) {
+ this.syntheticAnnotation = syntheticAnnotation;
+ this.annotationType = annotationType;
+ }
+
+ @Override
+ public Class extends Annotation> annotationType() {
+ return annotationType;
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ if (Synthesized.isMetaAnnotationMapMethod(method)) {
+ return syntheticAnnotation.getMetaAnnotationMap();
+ }
+ if (ReflectUtil.isHashCodeMethod(method)) {
+ return getHashCode();
+ }
+ if (ReflectUtil.isToStringMethod(method)) {
+ return getToString();
+ }
+ return ObjectUtil.defaultIfNull(
+ syntheticAnnotation.getAttribute(method.getName(), method.getReturnType()),
+ () -> ReflectUtil.invoke(this, method, args)
+ );
+ }
+
+ /**
+ * 获取toString值
+ *
+ * @return toString值
+ */
+ private String getToString() {
+ String attributes = Stream.of(annotationType().getDeclaredMethods())
+ .filter(AnnotationUtil::isAttributeMethod)
+ .map(method -> StrUtil.format("{}={}", method.getName(), syntheticAnnotation.getAttribute(method.getName(), method.getReturnType())))
+ .collect(Collectors.joining(", "));
+ return StrUtil.format("@{}({})", annotationType().getName(), attributes);
+ }
+
+ /**
+ * 获取hashcode值
+ *
+ * @return hashcode值
+ */
+ private int getHashCode() {
+ return Objects.hash((Object)syntheticAnnotation.getAnnotations());
+ }
+
+ }
+}
diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/AnnotationScanner.java b/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/AnnotationScanner.java
new file mode 100644
index 000000000..135165634
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/AnnotationScanner.java
@@ -0,0 +1,90 @@
+package cn.hutool.core.annotation.scanner;
+
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.ObjectUtil;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * 注解扫描器,用于从支持的可注解元素上获取所需注解
+ *
+ * @author huangchengxing
+ * @see TypeAnnotationScanner
+ * @see MethodAnnotationScanner
+ * @see FieldAnnotationScanner
+ * @see MateAnnotationScanner
+ */
+public interface AnnotationScanner {
+
+ /**
+ * 是否支持扫描该可注解元素
+ *
+ * @param annotatedElement 可注解元素
+ * @return 是否支持扫描该可注解元素
+ */
+ default boolean support(AnnotatedElement annotatedElement) {
+ return false;
+ }
+
+ /**
+ * 获取可注解元素上的全部注解。调用该方法前,需要确保调用{@link #support(AnnotatedElement)}返回为true
+ *
+ * @param annotatedElement 可注解元素
+ * @return 元素上的注解
+ */
+ List getAnnotations(AnnotatedElement annotatedElement);
+
+ /**
+ * 若{@link #support(AnnotatedElement)}返回{@code true},
+ * 则调用并返回{@link #getAnnotations(AnnotatedElement)}结果,
+ * 否则返回{@link Collections#emptyList()}
+ *
+ * @param annotatedElement 元素
+ * @return 元素上的注解
+ */
+ default List getIfSupport(AnnotatedElement annotatedElement) {
+ return support(annotatedElement) ? getAnnotations(annotatedElement) : Collections.emptyList();
+ }
+
+ /**
+ * 给定一组扫描器,使用第一个支持处理该类型元素的扫描器获取元素上可能存在的注解
+ *
+ * @param annotatedElement 可注解元素
+ * @param scanners 注解扫描器
+ * @return 注解
+ */
+ static List scanByAnySupported(AnnotatedElement annotatedElement, AnnotationScanner... scanners) {
+ if (ObjectUtil.isNull(annotatedElement) && ArrayUtil.isNotEmpty(scanners)) {
+ return Collections.emptyList();
+ }
+ return Stream.of(scanners)
+ .filter(scanner -> scanner.support(annotatedElement))
+ .findFirst()
+ .map(scanner -> scanner.getAnnotations(annotatedElement))
+ .orElseGet(Collections::emptyList);
+ }
+
+ /**
+ * 根据指定的扫描器,扫描元素上可能存在的注解
+ *
+ * @param annotatedElement 可注解元素
+ * @param scanners 注解扫描器
+ * @return 注解
+ */
+ static List scanByAllScanner(AnnotatedElement annotatedElement, AnnotationScanner... scanners) {
+ if (ObjectUtil.isNull(annotatedElement) && ArrayUtil.isNotEmpty(scanners)) {
+ return Collections.emptyList();
+ }
+ return Stream.of(scanners)
+ .map(scanner -> scanner.getIfSupport(annotatedElement))
+ .flatMap(Collection::stream)
+ .collect(Collectors.toList());
+ }
+
+}
diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/FieldAnnotationScanner.java b/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/FieldAnnotationScanner.java
new file mode 100644
index 000000000..119c51ecb
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/FieldAnnotationScanner.java
@@ -0,0 +1,27 @@
+package cn.hutool.core.annotation.scanner;
+
+import cn.hutool.core.collection.CollUtil;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Field;
+import java.util.List;
+
+/**
+ * 扫描{@link Field}上的注解
+ *
+ * @author huangchengxing
+ */
+public class FieldAnnotationScanner implements AnnotationScanner {
+
+ @Override
+ public boolean support(AnnotatedElement annotatedElement) {
+ return annotatedElement instanceof Field;
+ }
+
+ @Override
+ public List getAnnotations(AnnotatedElement annotatedElement) {
+ return CollUtil.newArrayList(annotatedElement.getAnnotations());
+ }
+
+}
diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/MateAnnotationScanner.java b/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/MateAnnotationScanner.java
new file mode 100644
index 000000000..1a6f8d1ac
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/MateAnnotationScanner.java
@@ -0,0 +1,95 @@
+package cn.hutool.core.annotation.scanner;
+
+import cn.hutool.core.annotation.AnnotationUtil;
+import cn.hutool.core.collection.CollStreamUtil;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ClassUtil;
+import cn.hutool.core.util.ObjectUtil;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.List;
+import java.util.function.BiConsumer;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * 扫描注解类上存在的注解,支持处理枚举实例或枚举类型
+ * 需要注意,当待解析是枚举类时,有可能与{@link TypeAnnotationScanner}冲突
+ *
+ * @author huangchengxing
+ * @see TypeAnnotationScanner
+ */
+public class MateAnnotationScanner implements AnnotationScanner {
+
+ /**
+ * 获取当前注解的元注解后,是否继续递归扫描的元注解的元注解
+ */
+ private final boolean includeSupperMetaAnnotation;
+
+ /**
+ * 构造
+ *
+ * @param includeSupperMetaAnnotation 获取当前注解的元注解后,是否继续递归扫描的元注解的元注解
+ */
+ public MateAnnotationScanner(boolean includeSupperMetaAnnotation) {
+ this.includeSupperMetaAnnotation = includeSupperMetaAnnotation;
+ }
+
+ /**
+ * 构造一个元注解扫描器,默认在扫描当前注解上的元注解后,并继续递归扫描元注解
+ */
+ public MateAnnotationScanner() {
+ this(true);
+ }
+
+ @Override
+ public boolean support(AnnotatedElement annotatedElement) {
+ return (annotatedElement instanceof Class && ClassUtil.isAssignable(Annotation.class, (Class>)annotatedElement));
+ }
+
+ /**
+ * 按广度优先扫描指定注解上的元注解,对扫描到的注解与层级索引进行操作
+ *
+ * @param consumer 当前层级索引与操作
+ * @param source 源注解
+ * @param filter 过滤器
+ * @author huangchengxing
+ * @date 2022/6/14 13:28
+ */
+ public void scan(BiConsumer consumer, Class extends Annotation> source, Predicate filter) {
+ filter = ObjectUtil.defaultIfNull(filter, t -> true);
+ Deque>> deque = CollUtil.newLinkedList(CollUtil.newArrayList(source));
+ int distance = 0;
+ do {
+ List> annotationTypes = deque.removeFirst();
+ for (Class extends Annotation> type : annotationTypes) {
+ List metaAnnotations = Stream.of(type.getAnnotations())
+ .filter(a -> !AnnotationUtil.isJdkMateAnnotation(a.annotationType()))
+ .filter(filter)
+ .collect(Collectors.toList());
+ for (Annotation metaAnnotation : metaAnnotations) {
+ consumer.accept(distance, metaAnnotation);
+ }
+ deque.addLast(CollStreamUtil.toList(metaAnnotations, Annotation::annotationType));
+ }
+ distance++;
+ } while (includeSupperMetaAnnotation && !deque.isEmpty());
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public List getAnnotations(AnnotatedElement annotatedElement) {
+ List annotations = new ArrayList<>();
+ scan(
+ (index, annotation) -> annotations.add(annotation),
+ (Class extends Annotation>)annotatedElement,
+ annotation -> ObjectUtil.notEqual(annotation, annotatedElement)
+ );
+ return annotations;
+ }
+
+}
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
new file mode 100644
index 000000000..f8de322b8
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/MethodAnnotationScanner.java
@@ -0,0 +1,27 @@
+package cn.hutool.core.annotation.scanner;
+
+import cn.hutool.core.collection.CollUtil;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Method;
+import java.util.List;
+
+/**
+ * 扫描{@link Method}上的注解
+ *
+ * @author huangchengxing
+ */
+public class MethodAnnotationScanner implements AnnotationScanner {
+
+ @Override
+ public boolean support(AnnotatedElement annotatedElement) {
+ return annotatedElement instanceof Method;
+ }
+
+ @Override
+ public List getAnnotations(AnnotatedElement annotatedElement) {
+ return CollUtil.newArrayList(annotatedElement.getAnnotations());
+ }
+
+}
diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/TypeAnnotationScanner.java b/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/TypeAnnotationScanner.java
new file mode 100644
index 000000000..f833a3492
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/TypeAnnotationScanner.java
@@ -0,0 +1,224 @@
+package cn.hutool.core.annotation.scanner;
+
+import cn.hutool.core.annotation.AnnotationUtil;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.lang.Assert;
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.ObjectUtil;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Proxy;
+import java.util.*;
+import java.util.function.Predicate;
+import java.util.function.UnaryOperator;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * 扫描{@link Class}上的注解
+ *
+ * @author huangchengxing
+ */
+public class TypeAnnotationScanner implements AnnotationScanner {
+
+ /**
+ * 是否允许扫描父类
+ */
+ private boolean includeSupperClass;
+
+ /**
+ * 是否允许扫描父接口
+ */
+ private boolean includeInterfaces;
+
+ /**
+ * 过滤器,若类型无法通过该过滤器,则该类型及其树结构将直接不被查找
+ */
+ private Predicate> filter;
+
+ /**
+ * 排除的类型,以上类型及其树结构将直接不被查找
+ */
+ private final Set> excludeTypes;
+
+ /**
+ * 转换器
+ */
+ private final List>> converters;
+
+ /**
+ * 是否有转换器
+ */
+ private boolean hasConverters;
+
+ /**
+ * 构造一个类注解扫描器
+ *
+ * @param includeSupperClass 是否允许扫描父类
+ * @param includeInterfaces 是否允许扫描父接口
+ */
+ public TypeAnnotationScanner(boolean includeSupperClass, boolean includeInterfaces, Predicate> filter, Set> excludeTypes) {
+ Assert.notNull(filter, "filter must not null");
+ Assert.notNull(excludeTypes, "excludeTypes must not null");
+ this.includeSupperClass = includeSupperClass;
+ this.includeInterfaces = includeInterfaces;
+ this.filter = filter;
+ this.excludeTypes = excludeTypes;
+ this.converters = new ArrayList<>();
+ }
+
+ /**
+ * 构建一个类注解扫描器,默认允许扫描指定元素的父类以及父接口
+ */
+ public TypeAnnotationScanner() {
+ this(true, true, t -> true, CollUtil.newHashSet());
+ }
+
+ /**
+ * 是否允许扫描父类
+ *
+ * @return 是否允许扫描父类
+ */
+ public boolean isIncludeSupperClass() {
+ return includeSupperClass;
+ }
+
+ /**
+ * 是否允许扫描父接口
+ *
+ * @return 是否允许扫描父接口
+ */
+ public boolean isIncludeInterfaces() {
+ return includeInterfaces;
+ }
+
+ /**
+ * 设置过滤器,若类型无法通过该过滤器,则该类型及其树结构将直接不被查找
+ *
+ * @param filter 过滤器
+ * @return 当前实例
+ */
+ public TypeAnnotationScanner setFilter(Predicate> filter) {
+ Assert.notNull(filter, "filter must not null");
+ this.filter = filter;
+ return this;
+ }
+
+ /**
+ * 添加不扫描的类型,该类型及其树结构将直接不被查找
+ *
+ * @param excludeTypes 不扫描的类型
+ * @return 当前实例
+ */
+ public TypeAnnotationScanner addExcludeTypes(Class>... excludeTypes) {
+ CollUtil.addAll(this.excludeTypes, excludeTypes);
+ return this;
+ }
+
+ /**
+ * 添加转换器
+ *
+ * @param converter 转换器
+ * @return 当前实例
+ * @see JdkProxyClassConverter
+ */
+ public TypeAnnotationScanner addConverters(UnaryOperator> converter) {
+ Assert.notNull(converter, "converter must not null");
+ this.converters.add(converter);
+ if (!this.hasConverters) {
+ this.hasConverters = true;
+ }
+ return this;
+ }
+
+ /**
+ * 是否允许扫描父类
+ *
+ * @param includeSupperClass 是否
+ * @return 当前实例
+ */
+ public TypeAnnotationScanner setIncludeSupperClass(boolean includeSupperClass) {
+ this.includeSupperClass = includeSupperClass;
+ return this;
+ }
+
+ /**
+ * 是否允许扫描父接口
+ *
+ * @param includeInterfaces 是否
+ * @return 当前实例
+ */
+ public TypeAnnotationScanner setIncludeInterfaces(boolean includeInterfaces) {
+ this.includeInterfaces = includeInterfaces;
+ return this;
+ }
+
+ @Override
+ public boolean support(AnnotatedElement annotatedElement) {
+ return annotatedElement instanceof Class;
+ }
+
+ @Override
+ public List getAnnotations(AnnotatedElement annotatedElement) {
+ return scan((Class>)annotatedElement).stream()
+ .map(Class::getAnnotations)
+ .flatMap(Stream::of)
+ .filter(a -> !AnnotationUtil.isJdkMateAnnotation(a.annotationType()))
+ .collect(Collectors.toList());
+ }
+
+ private Class> convert(Class> target) {
+ if (hasConverters) {
+ converters.forEach(c -> c.apply(target));
+ }
+ return target;
+ }
+
+ /**
+ * 递归遍历当前类、父类及其实现的父接口
+ *
+ * @param targetClass 类
+ */
+ private Set> scan(Class> targetClass) {
+ Deque> classDeque = CollUtil.newLinkedList(targetClass);
+ Set> accessedTypes = new HashSet<>();
+ while (!classDeque.isEmpty()) {
+ Class> target = convert(classDeque.removeFirst());
+ // 若当前类已经访问过,则无需再次处理
+ if (ObjectUtil.isNull(target) || accessedTypes.contains(target) || excludeTypes.contains(target) || filter.negate().test(target)) {
+ continue;
+ }
+ accessedTypes.add(target);
+
+ // 扫描父类
+ if (includeSupperClass) {
+ Class> superClass = target.getSuperclass();
+ if (!ObjectUtil.equals(superClass, Object.class) && ObjectUtil.isNotNull(superClass)) {
+ classDeque.addLast(superClass);
+ }
+ }
+
+ // 扫描接口
+ if (includeInterfaces) {
+ Class>[] interfaces = target.getInterfaces();
+ if (ArrayUtil.isNotEmpty(interfaces)) {
+ CollUtil.addAll(classDeque, interfaces);
+ }
+ }
+ }
+ return accessedTypes;
+ }
+
+ /**
+ * 若类型为jdk代理类,则尝试转换为原始被代理类
+ */
+ public static class JdkProxyClassConverter implements UnaryOperator> {
+
+ @Override
+ public Class> apply(Class> sourceClass) {
+ return Proxy.isProxyClass(sourceClass) ? sourceClass.getSuperclass() : sourceClass;
+ }
+ }
+
+}
diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/SyntheticAnnotationTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/SyntheticAnnotationTest.java
new file mode 100644
index 000000000..8e1db5d3f
--- /dev/null
+++ b/hutool-core/src/test/java/cn/hutool/core/annotation/SyntheticAnnotationTest.java
@@ -0,0 +1,87 @@
+package cn.hutool.core.annotation;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.lang.annotation.*;
+import java.util.Map;
+
+/**
+ * 合成注解{@link SyntheticAnnotation}的测试用例
+ *
+ * @author huangchengxing
+ */
+public class SyntheticAnnotationTest {
+
+ @Test
+ public void testSynthesisAnnotation() {
+ ChildAnnotation rootAnnotation = AnnotatedClass.class.getAnnotation(ChildAnnotation.class);
+ SyntheticAnnotation syntheticAnnotation = SyntheticAnnotation.of(rootAnnotation);
+ Assert.assertEquals(syntheticAnnotation.getSource(), rootAnnotation);
+ Assert.assertEquals(syntheticAnnotation.annotationType(), rootAnnotation.annotationType());
+ Assert.assertEquals(1, syntheticAnnotation.getDeclaredAnnotations().length);
+ Assert.assertEquals(syntheticAnnotation.getDeclaredAnnotations()[0], rootAnnotation);
+ Assert.assertEquals(3, syntheticAnnotation.getAnnotations().length);
+
+ Assert.assertEquals(syntheticAnnotation.getAttribute("childValue", String.class), "Child!");
+ Assert.assertEquals(syntheticAnnotation.getAttribute("childValueAlias", String.class), "Child!");
+ Assert.assertEquals(syntheticAnnotation.getAttribute("parentValue", String.class), "Child's Parent!");
+ Assert.assertEquals(syntheticAnnotation.getAttribute("grandParentValue", String.class), "Child's GrandParent!");
+
+ Map, SyntheticAnnotation.MetaAnnotation> annotationMap = syntheticAnnotation.getMetaAnnotationMap();
+ ChildAnnotation childAnnotation = syntheticAnnotation.getAnnotation(ChildAnnotation.class);
+ Assert.assertTrue(syntheticAnnotation.isAnnotationPresent(ChildAnnotation.class));
+ Assert.assertNotNull(childAnnotation);
+ Assert.assertEquals(childAnnotation.childValue(), "Child!");
+ Assert.assertEquals(childAnnotation.childValueAlias(), "Child!");
+ Assert.assertEquals(childAnnotation.grandParentType(), Integer.class);
+ Assert.assertEquals(annotationMap, SyntheticAnnotation.of(childAnnotation).getMetaAnnotationMap());
+
+ ParentAnnotation parentAnnotation = syntheticAnnotation.getAnnotation(ParentAnnotation.class);
+ Assert.assertTrue(syntheticAnnotation.isAnnotationPresent(ParentAnnotation.class));
+ Assert.assertNotNull(parentAnnotation);
+ Assert.assertEquals(parentAnnotation.parentValue(), "Child's Parent!");
+ Assert.assertEquals(parentAnnotation.grandParentType(), "java.lang.Void");
+ Assert.assertEquals(annotationMap, SyntheticAnnotation.of(parentAnnotation).getMetaAnnotationMap());
+
+ GrandParentAnnotation grandParentAnnotation = syntheticAnnotation.getAnnotation(GrandParentAnnotation.class);
+ Assert.assertTrue(syntheticAnnotation.isAnnotationPresent(GrandParentAnnotation.class));
+ Assert.assertNotNull(grandParentAnnotation);
+ Assert.assertEquals(grandParentAnnotation.grandParentValue(), "Child's GrandParent!");
+ Assert.assertEquals(grandParentAnnotation.grandParentType(), Integer.class);
+ Assert.assertEquals(annotationMap, SyntheticAnnotation.of(grandParentAnnotation).getMetaAnnotationMap());
+ }
+
+ // 注解结构如下:
+ // AnnotatedClass -> @ChildAnnotation -> @ParentAnnotation -> @GrandParentAnnotation
+ // -> @GrandParentAnnotation
+ @ChildAnnotation(childValueAlias = "Child!", grandParentType = Integer.class)
+ static class AnnotatedClass {}
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ ElementType.ANNOTATION_TYPE })
+ @interface GrandParentAnnotation {
+ String grandParentValue() default "";
+ Class> grandParentType() default Void.class;
+ }
+
+ @GrandParentAnnotation(grandParentValue = "Parent's GrandParent!") // 覆盖元注解@GrandParentAnnotation的属性
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ ElementType.TYPE })
+ @interface ParentAnnotation {
+ String parentValue() default "";
+ String grandParentType() default "java.lang.Void";
+ }
+
+ @GrandParentAnnotation(grandParentValue = "Child's GrandParent!") // 重复的元注解,靠近根注解的优先级高
+ @ParentAnnotation(parentValue = "Child's Parent!") // 覆盖元注解@ParentAnnotation的属性
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ ElementType.METHOD, ElementType.TYPE })
+ @interface ChildAnnotation {
+ String childValueAlias() default "";
+ @Alias("childValueAlias")
+ String childValue() default "";
+ Class> grandParentType() default Void.class;
+ }
+
+}
diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/scanner/AnnotationForScannerTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/scanner/AnnotationForScannerTest.java
new file mode 100644
index 000000000..d032a516b
--- /dev/null
+++ b/hutool-core/src/test/java/cn/hutool/core/annotation/scanner/AnnotationForScannerTest.java
@@ -0,0 +1,15 @@
+package cn.hutool.core.annotation.scanner;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @author huangchengxing
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.METHOD, ElementType.FIELD })
+@interface AnnotationForScannerTest {
+
+}
diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/scanner/FieldAnnotationScannerTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/scanner/FieldAnnotationScannerTest.java
new file mode 100644
index 000000000..59af613bd
--- /dev/null
+++ b/hutool-core/src/test/java/cn/hutool/core/annotation/scanner/FieldAnnotationScannerTest.java
@@ -0,0 +1,33 @@
+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.List;
+
+/**
+ * @author huangchengxing
+ */
+public class FieldAnnotationScannerTest {
+
+ @Test
+ public void testFieldAnnotationScanner() {
+ FieldAnnotationScanner scanner = new FieldAnnotationScanner();
+ Field field = ReflectUtil.getField(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());
+ }
+
+ public static class Example {
+ @AnnotationForScannerTest
+ private Integer id;
+ }
+
+}
diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/scanner/MateAnnotationScannerTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/scanner/MateAnnotationScannerTest.java
new file mode 100644
index 000000000..a787670de
--- /dev/null
+++ b/hutool-core/src/test/java/cn/hutool/core/annotation/scanner/MateAnnotationScannerTest.java
@@ -0,0 +1,55 @@
+package cn.hutool.core.annotation.scanner;
+
+import cn.hutool.core.collection.CollUtil;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.lang.annotation.*;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author huangchengxing
+ * @date 2022/06/10 16:51
+ */
+public class MateAnnotationScannerTest {
+
+ @Test
+ public void testMateAnnotationScanner() {
+ AnnotationScanner scanner = new MateAnnotationScanner();
+ Assert.assertTrue(scanner.support(AnnotationForScannerTest3.class));
+ Map, Annotation> annotations = CollUtil.toMap(scanner.getAnnotations(AnnotationForScannerTest3.class), new HashMap<>(), Annotation::annotationType);
+ Assert.assertEquals(3, annotations.size());
+ Assert.assertTrue(annotations.containsKey(AnnotationForScannerTest.class));
+ Assert.assertTrue(annotations.containsKey(AnnotationForScannerTest1.class));
+ Assert.assertTrue(annotations.containsKey(AnnotationForScannerTest2.class));
+ Assert.assertFalse(annotations.containsKey(AnnotationForScannerTest3.class));
+
+ scanner = new MateAnnotationScanner(false);
+ Assert.assertTrue(scanner.support(AnnotationForScannerTest3.class));
+ annotations = CollUtil.toMap(scanner.getAnnotations(AnnotationForScannerTest3.class), new HashMap<>(), Annotation::annotationType);
+ Assert.assertEquals(1, annotations.size());
+ Assert.assertTrue(annotations.containsKey(AnnotationForScannerTest2.class));
+ Assert.assertFalse(annotations.containsKey(AnnotationForScannerTest.class));
+ Assert.assertFalse(annotations.containsKey(AnnotationForScannerTest1.class));
+ Assert.assertFalse(annotations.containsKey(AnnotationForScannerTest3.class));
+ }
+
+ @AnnotationForScannerTest3
+ static class Example {}
+
+ @AnnotationForScannerTest
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.METHOD, ElementType.FIELD })
+ @interface AnnotationForScannerTest1 {}
+
+ @AnnotationForScannerTest1
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.METHOD, ElementType.FIELD })
+ @interface AnnotationForScannerTest2 {}
+
+ @AnnotationForScannerTest2
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.METHOD, ElementType.FIELD })
+ @interface AnnotationForScannerTest3 {}
+}
diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/scanner/MethodAnnotationScannerTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/scanner/MethodAnnotationScannerTest.java
new file mode 100644
index 000000000..25798c84d
--- /dev/null
+++ b/hutool-core/src/test/java/cn/hutool/core/annotation/scanner/MethodAnnotationScannerTest.java
@@ -0,0 +1,35 @@
+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.Method;
+import java.util.List;
+
+/**
+ * @author huangchengxing
+ */
+public class MethodAnnotationScannerTest {
+
+ @Test
+ public void testMethodAnnotationScanner() {
+ AnnotationScanner scanner = new MethodAnnotationScanner();
+ Method method = ReflectUtil.getMethod(Example.class, "test");
+ Assert.assertNotNull(method);
+ Assert.assertTrue(scanner.support(method));
+ List annotations = scanner.getAnnotations(method);
+ Assert.assertEquals(1, annotations.size());
+ Assert.assertEquals(CollUtil.getFirst(annotations).annotationType(), AnnotationForScannerTest.class);
+ }
+
+ static class Example {
+ @AnnotationForScannerTest
+ public void test() {
+
+ }
+ }
+
+}
diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/scanner/TypeAnnotationScannerTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/scanner/TypeAnnotationScannerTest.java
new file mode 100644
index 000000000..1df0727d1
--- /dev/null
+++ b/hutool-core/src/test/java/cn/hutool/core/annotation/scanner/TypeAnnotationScannerTest.java
@@ -0,0 +1,62 @@
+package cn.hutool.core.annotation.scanner;
+
+import cn.hutool.core.util.ClassUtil;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.lang.annotation.Annotation;
+import java.util.List;
+
+/**
+ * @author huangchengxing
+ * @date 2022/06/10 16:51
+ */
+public class TypeAnnotationScannerTest {
+
+ @Test
+ public void testTypeAnnotationScanner() {
+ AnnotationScanner scanner = new TypeAnnotationScanner();
+ Assert.assertTrue(scanner.support(Example.class));
+ List annotations = scanner.getAnnotations(Example.class);
+ Assert.assertEquals(3, annotations.size());
+ annotations.forEach(a -> Assert.assertEquals(a.annotationType(), AnnotationForScannerTest.class));
+
+ // 不查找父接口
+ scanner = new TypeAnnotationScanner().setIncludeInterfaces(false);
+ Assert.assertTrue(scanner.support(Example.class));
+ annotations = scanner.getAnnotations(Example.class);
+ Assert.assertEquals(2, annotations.size());
+ annotations.forEach(a -> Assert.assertEquals(a.annotationType(), AnnotationForScannerTest.class));
+
+ // 不查找父类
+ scanner = new TypeAnnotationScanner().setIncludeSupperClass(false);
+ Assert.assertTrue(scanner.support(Example.class));
+ annotations = scanner.getAnnotations(Example.class);
+ Assert.assertEquals(1, annotations.size());
+ annotations.forEach(a -> Assert.assertEquals(a.annotationType(), AnnotationForScannerTest.class));
+
+ // 不查找ExampleSupplerClass.class
+ scanner = new TypeAnnotationScanner().addExcludeTypes(ExampleSupplerClass.class);
+ Assert.assertTrue(scanner.support(Example.class));
+ annotations = scanner.getAnnotations(Example.class);
+ Assert.assertEquals(1, annotations.size());
+ annotations.forEach(a -> Assert.assertEquals(a.annotationType(), AnnotationForScannerTest.class));
+
+ // 只查找ExampleSupplerClass.class
+ scanner = new TypeAnnotationScanner().setFilter(t -> ClassUtil.isAssignable(ExampleSupplerClass.class, t));
+ Assert.assertTrue(scanner.support(Example.class));
+ annotations = scanner.getAnnotations(Example.class);
+ Assert.assertEquals(2, annotations.size());
+ annotations.forEach(a -> Assert.assertEquals(a.annotationType(), AnnotationForScannerTest.class));
+ }
+
+ @AnnotationForScannerTest
+ static class ExampleSupplerClass implements ExampleInterface {}
+
+ @AnnotationForScannerTest
+ interface ExampleInterface {}
+
+ @AnnotationForScannerTest
+ static class Example extends ExampleSupplerClass {}
+
+}