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 { } + +}