diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotatedElementUtil.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotatedElementUtil.java new file mode 100644 index 000000000..2670c99bb --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotatedElementUtil.java @@ -0,0 +1,615 @@ +package cn.hutool.core.annotation; + +import cn.hutool.core.map.WeakConcurrentMap; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjUtil; + +import java.lang.annotation.Annotation; +import java.lang.annotation.Inherited; +import java.lang.reflect.AnnotatedElement; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Stream; + +/** + *

{@link AnnotatedElement}工具类,提供对层级结构中{@link AnnotatedElement}上注解及元注解的访问支持, + * 并提供诸如基于{@link Alias}的属性别名、基于父子注解间的属性值覆盖等特殊的属性映射机制支持。 + * + *

搜索层级结构 + *

参考 Spring 中AnnotatedElementUtils, + * 工具类提供get以及find两种语义的搜索: + *

+ * eg:
+ * 若类A分别有父类和父接口BC, + * 则通过getXXX方法将只能获得A上的注解, + * 而通过getXXX方法将能获得ABC上的注解。 + * + *

搜索元注解 + *

工具类支持搜索注解的元注解。在所有格式为getXXXfindXXX的静态方法中, + * 若不带有directly关键字,则该方法支持搜索元注解,否则皆支持搜索元注解。
+ * eg:
+ * 若类A分别有父类和父接口BC,上面分别有注解X与其元注解Y, + * 则此时基于A有: + *

+ * 注意:在当前实例中将无视{@link Inherited}的效果,即通过directly方法将无法获得父类上带有{@link Inherited}的注解。 + * + *

注解属性映射 + *

工具类支持注解对象属性上的一些属性映射机制,即当注解被扫描时, + * 将根据一些属性映射机制“解析”为其他类型的属性,这里支持的机制包括: + *

+ * + * @author huangchengxing + * @see ResolvedAnnotationMapping + * @see GenericAnnotationMapping + * @see HierarchicalAnnotatedElements + * @see MetaAnnotatedElement + * @since 6.0.0 + */ +public class AnnotatedElementUtil { + + /** + * 支持属性解析的{@link MetaAnnotatedElement}缓存 + */ + private static final Map> RESOLVED_ELEMENT_CACHE = new WeakConcurrentMap<>(); + + /** + * 不支持属性解析的{@link MetaAnnotatedElement}缓存 + */ + private static final Map> ELEMENT_CACHE = new WeakConcurrentMap<>(); + + // region ========== find ========== + + /** + * 在{@code element}所处层级结构的所有{@link AnnotatedElement}上,是否存在该类型的注解或元注解 + * + * @param element {@link AnnotatedElement} + * @param annotationType 注解类型 + * @return 是否 + */ + public static boolean isAnnotated(final AnnotatedElement element, final Class annotationType) { + return toHierarchyMetaElement(element, false) + .isAnnotationPresent(annotationType); + } + + /** + * 从{@code element}所处层级结构的所有{@link AnnotatedElement}上,获取该类型的注解或元注解 + * + * @param element {@link AnnotatedElement} + * @param annotationType 注解类型 + * @param 注解类型 + * @return 注解对象 + */ + public static T findAnnotation(final AnnotatedElement element, final Class annotationType) { + return toHierarchyMetaElement(element, false) + .getAnnotation(annotationType); + } + + /** + * 从{@code element}所处层级结构的所有{@link AnnotatedElement}上,获取所有该类型的注解或元注解 + * + * @param element {@link AnnotatedElement} + * @param annotationType 注解类型 + * @param 注解类型 + * @return 注解对象 + */ + public static T[] findAllAnnotations(final AnnotatedElement element, final Class annotationType) { + return toHierarchyMetaElement(element, false) + .getAnnotationsByType(annotationType); + } + + /** + * 从{@code element}所处层级结构的所有{@link AnnotatedElement}上,获取所有的注解或元注解 + * + * @param element {@link AnnotatedElement} + * @return 注解对象 + */ + public static Annotation[] findAnnotations(final AnnotatedElement element) { + return toHierarchyMetaElement(element, false) + .getAnnotations(); + } + + /** + * 从{@code element}所处层级结构的所有{@link AnnotatedElement}上,获取所有的注解或元注解。
+ * 得到的注解支持基于{@link Alias}的别名、及子注解对元注解中同名同类型属性进行覆写的特殊机制。 + * + * @param element {@link AnnotatedElement} + * @param annotationType 注解类型 + * @param 注解类型 + * @return 注解对象 + */ + public static T findResolvedAnnotation(final AnnotatedElement element, final Class annotationType) { + return toHierarchyMetaElement(element, true) + .getAnnotation(annotationType); + } + + /** + * 从{@code element}所处层级结构的所有{@link AnnotatedElement}上,获取所有的注解或元注解。
+ * 得到的注解支持基于{@link Alias}的别名、及子注解对元注解中同名同类型属性进行覆写的特殊机制。 + * + * @param element {@link AnnotatedElement} + * @return 注解对象 + */ + public static Annotation[] findResolvedAnnotations(final AnnotatedElement element) { + return toHierarchyMetaElement(element, true) + .getAnnotations(); + } + + /** + * 从{@code element}所处层级结构的所有{@link AnnotatedElement}上,获取所有该类型的注解或元注解。
+ * 得到的注解支持基于{@link Alias}的别名、及子注解对元注解中同名同类型属性进行覆写的特殊机制。 + * + * @param element {@link AnnotatedElement} + * @param annotationType 注解类型 + * @param 注解类型 + * @return 注解对象 + */ + public static T[] findAllResolvedAnnotations(final AnnotatedElement element, final Class annotationType) { + return toHierarchyMetaElement(element, true) + .getAnnotationsByType(annotationType); + } + + // endregion + + // region ========== find & direct ========== + + /** + * 从{@code element}所处层级结构的所有{@link AnnotatedElement}上获取该类型的注解 + * + * @param element {@link AnnotatedElement} + * @param annotationType 注解类型 + * @param 注解类型 + * @return 注解对象 + */ + public static T findDirectlyAnnotation(final AnnotatedElement element, final Class annotationType) { + return toHierarchyMetaElement(element, false) + .getDeclaredAnnotation(annotationType); + } + + /** + * 从{@code element}所处层级结构的所有{@link AnnotatedElement}上获取所有该类型的注解 + * + * @param element {@link AnnotatedElement} + * @param annotationType 注解类型 + * @param 注解类型 + * @return 注解对象 + */ + public static T[] findAllDirectlyAnnotations(final AnnotatedElement element, final Class annotationType) { + return toHierarchyMetaElement(element, false) + .getDeclaredAnnotationsByType(annotationType); + } + + /** + * 从{@code element}所处层级结构的所有{@link AnnotatedElement}上获取所有的注解 + * + * @param element {@link AnnotatedElement} + * @return 注解对象 + */ + public static Annotation[] findDirectlyAnnotations(final AnnotatedElement element) { + return toHierarchyMetaElement(element, false) + .getDeclaredAnnotations(); + } + + /** + * 从{@code element}所处层级结构的所有{@link AnnotatedElement}上,获取所有的注解。
+ * 得到的注解支持基于{@link Alias}的别名、及子注解对元注解中同名同类型属性进行覆写的特殊机制。 + * + * @param element {@link AnnotatedElement} + * @param annotationType 注解类型 + * @param 注解类型 + * @return 注解对象 + */ + public static T findDirectlyResolvedAnnotation(final AnnotatedElement element, final Class annotationType) { + return toHierarchyMetaElement(element, true) + .getDeclaredAnnotation(annotationType); + } + + /** + * 从{@code element}所处层级结构的所有{@link AnnotatedElement}上获取所有的注解。
+ * 得到的注解支持基于{@link Alias}的别名、及子注解对元注解中同名同类型属性进行覆写的特殊机制。 + * + * @param element {@link AnnotatedElement} + * @return 注解对象 + */ + public static Annotation[] findDirectlyResolvedAnnotations(final AnnotatedElement element) { + return toHierarchyMetaElement(element, true) + .getDeclaredAnnotations(); + } + + /** + * 从{@code element}所处层级结构的所有{@link AnnotatedElement}上获取所有该类型的注解。
+ * 得到的注解支持基于{@link Alias}的别名、及子注解对元注解中同名同类型属性进行覆写的特殊机制。 + * + * @param element {@link AnnotatedElement} + * @param annotationType 注解类型 + * @param 注解类型 + * @return 注解对象 + */ + public static T[] findAllDirectlyResolvedAnnotations(final AnnotatedElement element, final Class annotationType) { + return toHierarchyMetaElement(element, true) + .getDeclaredAnnotationsByType(annotationType); + } + + // endregion + + // region ========== get ========== + + /** + * 在{@code element}上,是否存在该类型的注解或元注解 + * + * @param element {@link AnnotatedElement} + * @param annotationType 注解类型 + * @return 是否 + */ + public static boolean isAnnotationPresent(final AnnotatedElement element, final Class annotationType) { + return toMetaElement(element, false) + .isAnnotationPresent(annotationType); + } + + /** + * 从{@code element}上,获取该类型的注解或元注解 + * + * @param element {@link AnnotatedElement} + * @param annotationType 注解类型 + * @param 注解类型 + * @return 注解对象 + */ + public static T getAnnotation(final AnnotatedElement element, final Class annotationType) { + return toMetaElement(element, false) + .getAnnotation(annotationType); + } + + /** + * 从{@code element}上,获取所有的注解或元注解 + * + * @param element {@link AnnotatedElement} + * @return 注解对象 + */ + public static Annotation[] getAnnotations(final AnnotatedElement element) { + return toMetaElement(element, false) + .getAnnotations(); + } + + /** + * 从{@code element}上,获取所有的注解或元注解。
+ * 得到的注解支持基于{@link Alias}的别名机制。 + * + * @param element {@link AnnotatedElement} + * @param annotationType 注解类型 + * @param 注解类型 + * @return 注解对象 + */ + public static T getResolvedAnnotation(final AnnotatedElement element, final Class annotationType) { + return toMetaElement(element, true) + .getAnnotation(annotationType); + } + + /** + * 从{@code element}上,获取所有的注解或元注解。
+ * 得到的注解支持基于{@link Alias}的别名机制。 + * + * @param element {@link AnnotatedElement} + * @return 注解对象 + */ + public static Annotation[] getResolvedAnnotations(final AnnotatedElement element) { + return toMetaElement(element, true) + .getAnnotations(); + } + + // endregion + + // region ========== get & direct ========== + + /** + * 从{@code element}上获取该类型的注解 + * + * @param element {@link AnnotatedElement} + * @param annotationType 注解类型 + * @param 注解类型 + * @return 注解对象 + */ + public static T getDirectlyAnnotation(final AnnotatedElement element, final Class annotationType) { + return toMetaElement(element, false) + .getDeclaredAnnotation(annotationType); + } + + /** + * 从{@code element}上获取所有的注解 + * + * @param element {@link AnnotatedElement} + * @return 注解对象 + */ + public static Annotation[] getDirectlyAnnotations(final AnnotatedElement element) { + return toMetaElement(element, false) + .getDeclaredAnnotations(); + } + + /** + * 从{@code element}上,获取所有的注解。
+ * 得到的注解支持基于{@link Alias}的别名机制。 + * + * @param element {@link AnnotatedElement} + * @param annotationType 注解类型 + * @param 注解类型 + * @return 注解对象 + */ + public static T getDirectlyResolvedAnnotation(final AnnotatedElement element, final Class annotationType) { + return toMetaElement(element, true) + .getDeclaredAnnotation(annotationType); + } + + /** + * 从{@code element}上,获取所有的注解。
+ * 得到的注解支持基于{@link Alias}的别名机制。 + * + * @param element {@link AnnotatedElement} + * @return 注解对象 + */ + public static Annotation[] getDirectlyResolvedAnnotations(final AnnotatedElement element) { + return toMetaElement(element, true) + .getDeclaredAnnotations(); + } + + // endregion + + // region ========== to element ========== + + /** + *

扫描{@code element}所处层级结构中的{@link AnnotatedElement}, + * 并将其全部转为{@link MetaAnnotatedElement}后, + * 再把所有对象合并为{@link HierarchicalAnnotatedElements}。
+ * 得到的对象可访问{@code element}所处层级结构中所有{@link AnnotatedElement}上的注解及元注解。 + * + * @param element 元素 + * @param resolved 是否解析注解属性,若为{@code true}则获得的注解将支持属性别名以及属性覆盖机制 + * @return {@link HierarchicalAnnotatedElements}实例 + * @see #getMetaElementCache(AnnotatedElement) + * @see #getResolvedMetaElementCache(AnnotatedElement) + */ + public static AnnotatedElement toHierarchyMetaElement(final AnnotatedElement element, final boolean resolved) { + if (Objects.isNull(element)) { + return emptyElement(); + } + if (resolved) { + return HierarchicalAnnotatedElements.create(element, (es, e) -> getResolvedMetaElementCache(e)); + } + return HierarchicalAnnotatedElements.create(element, (es, e) -> getMetaElementCache(e)); + } + + /** + *

扫描{@code element}所处层级结构中的{@link AnnotatedElement}, + * 再把所有对象合并为{@link HierarchicalAnnotatedElements} + * 得到的对象可访问{@code element}所处层级结构中所有{@link AnnotatedElement}上的注解。 + * + * @param element 元素 + * @return {@link AnnotatedElement}实例 + */ + public static AnnotatedElement toHierarchyElement(final AnnotatedElement element) { + return ObjUtil.defaultIfNull( + element, ele -> HierarchicalAnnotatedElements.create(ele, (es, e) -> e), emptyElement() + ); + } + + /** + * 将{@link AnnotatedElement}转为{@link MetaAnnotatedElement}, + * 得到的对象可访问{@code element}上所有的注解及元注解。 + * + * @param element 元素 + * @param resolved 是否解析注解属性,若为{@code true}则获得的注解将支持属性别名以及属性覆盖机制 + * @return {@link AnnotatedElement}实例 + * @see #getMetaElementCache(AnnotatedElement) + * @see #getResolvedMetaElementCache(AnnotatedElement) + */ + public static AnnotatedElement toMetaElement(final AnnotatedElement element, final boolean resolved) { + return ObjUtil.defaultIfNull( + element, e -> resolved ? getResolvedMetaElementCache(e) : getMetaElementCache(e), emptyElement() + ); + } + + /** + * 将一组注解中的非{@code null}注解对象合并为一个{@link AnnotatedElement} + * + * @param annotations 注解 + * @return {@link AnnotatedElement}实例 + * @see ConstantElement + */ + public static AnnotatedElement asElement(Annotation... annotations) { + annotations = ArrayUtil.filter(annotations, Objects::nonNull); + return ArrayUtil.isEmpty(annotations) ? + emptyElement() : new ConstantElement(annotations); + } + + /** + * 获取一个不包含任何注解的{@link AnnotatedElement} + * + * @return {@link AnnotatedElement}实例 + * @see EmptyElement + */ + public static AnnotatedElement emptyElement() { + return EmptyElement.INSTANCE; + } + + // endregion + + // region ========== private ========== + + /** + * 创建一个支持注解解析的{@link MetaAnnotatedElement} + * + * @param element {@link AnnotatedElement} + * @return {@link MetaAnnotatedElement}实例 + */ + private static MetaAnnotatedElement getResolvedMetaElementCache(final AnnotatedElement element) { + return RESOLVED_ELEMENT_CACHE.computeIfAbsent(element, ele -> MetaAnnotatedElement.create( + element, (source, annotation) -> ResolvedAnnotationMapping.create(source, annotation, true) + )); + } + + /** + * 创建一个支持注解解析的{@link MetaAnnotatedElement} + * + * @param element {@link AnnotatedElement} + * @return {@link MetaAnnotatedElement}实例 + */ + private static MetaAnnotatedElement getMetaElementCache(final AnnotatedElement element) { + return ELEMENT_CACHE.computeIfAbsent(element, ele -> MetaAnnotatedElement.create( + element, (source, annotation) -> GenericAnnotationMapping.create(annotation, Objects.isNull(source)) + )); + } + + // endregion + + /** + * 由一组注解聚合来的{@link AnnotatedElement} + */ + private static class ConstantElement implements AnnotatedElement { + + /** + * 注解对象 + */ + private final Annotation[] annotations; + + /** + * 构造 + * + * @param annotations 注解 + */ + ConstantElement(final Annotation[] annotations) { + this.annotations = Objects.requireNonNull(annotations); + } + + /** + * 获取指定类型的注解对象 + * + * @param annotationClass 注解类型 + * @param 注解类型 + * @return 注解 + */ + @Override + public T getAnnotation(final Class annotationClass) { + return Stream.of(annotations) + .filter(annotation -> Objects.equals(annotation.annotationType(), annotationClass)) + .findFirst() + .map(annotationClass::cast) + .orElse(null); + } + + /** + * 获取指定直接所有的注解对象 + * + * @return 注解 + */ + @Override + public Annotation[] getAnnotations() { + return annotations.clone(); + } + + /** + * 获取指定直接声明的注解对象 + * + * @return 注解 + */ + @Override + public Annotation[] getDeclaredAnnotations() { + return annotations.clone(); + } + } + + /** + * 不包含任何注解的{@link AnnotatedElement} + */ + private static class EmptyElement implements AnnotatedElement { + + /** + * 默认的空实例 + */ + static final EmptyElement INSTANCE = new EmptyElement(); + + /** + * 固定返回{@code null} + * + * @param annotationClass 注解类型 + * @param 注解类型 + * @return {@code null} + */ + @Override + public T getAnnotation(final Class annotationClass) { + return null; + } + + /** + * 固定返回空数组 + * + * @return 空数组 + */ + @Override + public Annotation[] getAnnotations() { + return new Annotation[0]; + } + + /** + * 固定返回空数组 + * + * @return 空数组 + */ + @Override + public Annotation[] getDeclaredAnnotations() { + return new Annotation[0]; + } + } + +} diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/AnnotatedElementUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/AnnotatedElementUtilTest.java new file mode 100644 index 000000000..03ed704a6 --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/AnnotatedElementUtilTest.java @@ -0,0 +1,480 @@ +package cn.hutool.core.annotation; + +import org.junit.Assert; +import org.junit.Test; + +import java.lang.annotation.*; +import java.lang.reflect.AnnotatedElement; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * test for {@link AnnotatedElementUtil} + * + * @author huangchengxing + */ +public class AnnotatedElementUtilTest { + + private final static Annotation3 ANNOTATION3 = Foo.class.getAnnotation(Annotation3.class); // Foo.class's annotations + private final static Annotation2 ANNOTATION2 = Annotation3.class.getAnnotation(Annotation2.class); + private final static Annotation1 ANNOTATION1 = Annotation2.class.getAnnotation(Annotation1.class); + + private final static Annotation4 ANNOTATION4 = Super.class.getAnnotation(Annotation4.class); // Super.class's annotations + + private final static Annotation6 ANNOTATION6 = Interface.class.getAnnotation(Annotation6.class); // Interface.class's annotations + private final static Annotation5 ANNOTATION5 = Annotation6.class.getAnnotation(Annotation5.class); + + private final static Annotation[] DECLARED_ANNOTATIONS = new Annotation[]{ + ANNOTATION3, // Foo.class's annotations + ANNOTATION4, // Super.class's annotations + ANNOTATION6 // Interface.class's annotations + }; + private final static Annotation[] ANNOTATIONS = new Annotation[]{ + ANNOTATION3, ANNOTATION2, ANNOTATION1, // Foo.class's annotations + ANNOTATION4, // Super.class's annotations + ANNOTATION6, ANNOTATION5 // Interface.class's annotations + }; + + @Test + public void testIsAnnotated() { + Assert.assertTrue(AnnotatedElementUtil.isAnnotated(Foo.class, Annotation1.class)); + Assert.assertTrue(AnnotatedElementUtil.isAnnotated(Foo.class, Annotation2.class)); + Assert.assertTrue(AnnotatedElementUtil.isAnnotated(Foo.class, Annotation3.class)); + Assert.assertTrue(AnnotatedElementUtil.isAnnotated(Foo.class, Annotation4.class)); + Assert.assertTrue(AnnotatedElementUtil.isAnnotated(Foo.class, Annotation5.class)); + Assert.assertTrue(AnnotatedElementUtil.isAnnotated(Foo.class, Annotation6.class)); + } + + @Test + public void testFindAnnotation() { + Assert.assertEquals(ANNOTATION1, AnnotatedElementUtil.findAnnotation(Foo.class, Annotation1.class)); + Assert.assertEquals(ANNOTATION2, AnnotatedElementUtil.findAnnotation(Foo.class, Annotation2.class)); + Assert.assertEquals(ANNOTATION3, AnnotatedElementUtil.findAnnotation(Foo.class, Annotation3.class)); + Assert.assertEquals(ANNOTATION4, AnnotatedElementUtil.findAnnotation(Foo.class, Annotation4.class)); + Assert.assertEquals(ANNOTATION5, AnnotatedElementUtil.findAnnotation(Foo.class, Annotation5.class)); + Assert.assertEquals(ANNOTATION6, AnnotatedElementUtil.findAnnotation(Foo.class, Annotation6.class)); + } + + @Test + public void testFindAllAnnotations() { + Assert.assertArrayEquals(new Annotation[]{ANNOTATION1}, AnnotatedElementUtil.findAllAnnotations(Foo.class, Annotation1.class)); + Assert.assertArrayEquals(new Annotation[]{ANNOTATION2}, AnnotatedElementUtil.findAllAnnotations(Foo.class, Annotation2.class)); + Assert.assertArrayEquals(new Annotation[]{ANNOTATION3}, AnnotatedElementUtil.findAllAnnotations(Foo.class, Annotation3.class)); + Assert.assertArrayEquals(new Annotation[]{ANNOTATION4}, AnnotatedElementUtil.findAllAnnotations(Foo.class, Annotation4.class)); + Assert.assertArrayEquals(new Annotation[]{ANNOTATION5}, AnnotatedElementUtil.findAllAnnotations(Foo.class, Annotation5.class)); + Assert.assertArrayEquals(new Annotation[]{ANNOTATION6}, AnnotatedElementUtil.findAllAnnotations(Foo.class, Annotation6.class)); + } + + @Test + public void testFindAnnotations() { + Annotation[] annotations = AnnotatedElementUtil.findAnnotations(Foo.class); + Assert.assertArrayEquals(ANNOTATIONS, annotations); + } + + @Test + public void testFindResolvedAnnotation() { + Annotation3 resolvedAnnotation3 = AnnotatedElementUtil.findResolvedAnnotation(Foo.class, Annotation3.class); + Assert.assertNotNull(resolvedAnnotation3); + Assert.assertEquals(resolvedAnnotation3.alias(), ANNOTATION3.value()); + Assert.assertEquals(resolvedAnnotation3.alias(), resolvedAnnotation3.value()); // value与alias互为别名 + + Annotation2 resolvedAnnotation2 = AnnotatedElementUtil.findResolvedAnnotation(Foo.class, Annotation2.class); + Assert.assertNotNull(resolvedAnnotation2); + Assert.assertEquals(resolvedAnnotation2.num(), ANNOTATION3.num()); // num属性被Annotation3.num覆盖 + + Annotation1 resolvedAnnotation1 = AnnotatedElementUtil.findResolvedAnnotation(Foo.class, Annotation1.class); + Assert.assertNotNull(resolvedAnnotation1); + Assert.assertEquals(ANNOTATION3.value(), resolvedAnnotation1.value()); // value属性被Annotation3.value覆盖 + Assert.assertEquals(resolvedAnnotation1.value(), resolvedAnnotation1.alias()); // value与alias互为别名 + + Assert.assertEquals(ANNOTATION4, AnnotatedElementUtil.findResolvedAnnotation(Foo.class, Annotation4.class)); + Assert.assertEquals(ANNOTATION6, AnnotatedElementUtil.findResolvedAnnotation(Foo.class, Annotation6.class)); + Assert.assertEquals(ANNOTATION5, AnnotatedElementUtil.findResolvedAnnotation(Foo.class, Annotation5.class)); + } + + @Test + public void testFindResolvedAnnotations() { + Annotation[] resolvedAnnotations = AnnotatedElementUtil.findResolvedAnnotations(Foo.class); + Map, Annotation> annotationMap = Stream.of(resolvedAnnotations).collect(Collectors.toMap(Annotation::annotationType, Function.identity())); + + Annotation3 resolvedAnnotation3 = (Annotation3)annotationMap.get(Annotation3.class); + Assert.assertNotNull(resolvedAnnotation3); + Assert.assertEquals(resolvedAnnotation3.alias(), ANNOTATION3.value()); + Assert.assertEquals(resolvedAnnotation3.alias(), resolvedAnnotation3.value()); // value与alias互为别名 + + Annotation2 resolvedAnnotation2 = (Annotation2)annotationMap.get(Annotation2.class); + Assert.assertNotNull(resolvedAnnotation2); + Assert.assertEquals(resolvedAnnotation2.num(), ANNOTATION3.num()); // num属性被Annotation3.num覆盖 + + Annotation1 resolvedAnnotation1 = (Annotation1)annotationMap.get(Annotation1.class); + Assert.assertNotNull(resolvedAnnotation1); + Assert.assertEquals(ANNOTATION3.value(), resolvedAnnotation1.value()); // value属性被Annotation3.value覆盖 + Assert.assertEquals(resolvedAnnotation1.value(), resolvedAnnotation1.alias()); // value与alias互为别名 + + Assert.assertEquals(ANNOTATION4, annotationMap.get(Annotation4.class)); + Assert.assertEquals(ANNOTATION6, annotationMap.get(Annotation6.class)); + Assert.assertEquals(ANNOTATION5, annotationMap.get(Annotation5.class)); + } + + @Test + public void testFindAllResolvedAnnotations() { + Annotation3 resolvedAnnotation3 = AnnotatedElementUtil.findAllResolvedAnnotations(Foo.class, Annotation3.class)[0]; + Assert.assertNotNull(resolvedAnnotation3); + Assert.assertEquals(resolvedAnnotation3.alias(), ANNOTATION3.value()); + Assert.assertEquals(resolvedAnnotation3.alias(), resolvedAnnotation3.value()); // value与alias互为别名 + + Annotation2 resolvedAnnotation2 = AnnotatedElementUtil.findAllResolvedAnnotations(Foo.class, Annotation2.class)[0]; + Assert.assertNotNull(resolvedAnnotation2); + Assert.assertEquals(resolvedAnnotation2.num(), ANNOTATION3.num()); // num属性被Annotation3.num覆盖 + + Annotation1 resolvedAnnotation1 = AnnotatedElementUtil.findAllResolvedAnnotations(Foo.class, Annotation1.class)[0]; + Assert.assertNotNull(resolvedAnnotation1); + Assert.assertEquals(ANNOTATION3.value(), resolvedAnnotation1.value()); // value属性被Annotation3.value覆盖 + Assert.assertEquals(resolvedAnnotation1.value(), resolvedAnnotation1.alias()); // value与alias互为别名 + + Assert.assertEquals(ANNOTATION4, AnnotatedElementUtil.findAllResolvedAnnotations(Foo.class, Annotation4.class)[0]); + Assert.assertEquals(ANNOTATION6, AnnotatedElementUtil.findAllResolvedAnnotations(Foo.class, Annotation6.class)[0]); + Assert.assertEquals(ANNOTATION5, AnnotatedElementUtil.findAllResolvedAnnotations(Foo.class, Annotation5.class)[0]); + } + + @Test + public void testFindDirectlyAnnotation() { + Assert.assertNull(AnnotatedElementUtil.findDirectlyAnnotation(Foo.class, Annotation1.class)); + Assert.assertNull(AnnotatedElementUtil.findDirectlyAnnotation(Foo.class, Annotation2.class)); + Assert.assertEquals(ANNOTATION3, AnnotatedElementUtil.findDirectlyAnnotation(Foo.class, Annotation3.class)); + Assert.assertEquals(ANNOTATION4, AnnotatedElementUtil.findDirectlyAnnotation(Foo.class, Annotation4.class)); + Assert.assertNull(AnnotatedElementUtil.findDirectlyAnnotation(Foo.class, Annotation5.class)); + Assert.assertEquals(ANNOTATION6, AnnotatedElementUtil.findDirectlyAnnotation(Foo.class, Annotation6.class)); + } + + @Test + public void testFindAllDirectlyAnnotations() { + Assert.assertEquals(0, AnnotatedElementUtil.findAllDirectlyAnnotations(Foo.class, Annotation1.class).length); + Assert.assertEquals(0, AnnotatedElementUtil.findAllDirectlyAnnotations(Foo.class, Annotation2.class).length); + Assert.assertArrayEquals(new Annotation[]{ANNOTATION3}, AnnotatedElementUtil.findAllDirectlyAnnotations(Foo.class, Annotation3.class)); + Assert.assertArrayEquals(new Annotation[]{ANNOTATION4}, AnnotatedElementUtil.findAllDirectlyAnnotations(Foo.class, Annotation4.class)); + Assert.assertEquals(0, AnnotatedElementUtil.findAllDirectlyAnnotations(Foo.class, Annotation5.class).length); + Assert.assertArrayEquals(new Annotation[]{ANNOTATION6}, AnnotatedElementUtil.findAllDirectlyAnnotations(Foo.class, Annotation6.class)); + } + + @Test + public void testFindDirectlyAnnotations() { + Assert.assertArrayEquals( + DECLARED_ANNOTATIONS, AnnotatedElementUtil.findDirectlyAnnotations(Foo.class) + ); + } + + @Test + public void testFindDirectlyResolvedAnnotation() { + Assert.assertEquals(ANNOTATION4, AnnotatedElementUtil.findDirectlyResolvedAnnotation(Foo.class, Annotation4.class)); + Assert.assertEquals(ANNOTATION6, AnnotatedElementUtil.findDirectlyResolvedAnnotation(Foo.class, Annotation6.class)); + Annotation3 resolvedAnnotation3 = AnnotatedElementUtil.findDirectlyResolvedAnnotation(Foo.class, Annotation3.class); + Assert.assertNotNull(resolvedAnnotation3); + Assert.assertEquals(resolvedAnnotation3.alias(), ANNOTATION3.value()); + Assert.assertEquals(resolvedAnnotation3.alias(), resolvedAnnotation3.value()); // value与alias互为别名 + + Assert.assertNull(AnnotatedElementUtil.findDirectlyResolvedAnnotation(Foo.class, Annotation1.class)); + Assert.assertNull(AnnotatedElementUtil.findDirectlyResolvedAnnotation(Foo.class, Annotation2.class)); + Assert.assertNull(AnnotatedElementUtil.findDirectlyResolvedAnnotation(Foo.class, Annotation5.class)); + } + + @Test + public void testFindDirectlyResolvedAnnotations() { + Annotation[] resolvedAnnotations = AnnotatedElementUtil.findDirectlyResolvedAnnotations(Foo.class); + Map, Annotation> annotationMap = Stream.of(resolvedAnnotations).collect(Collectors.toMap(Annotation::annotationType, Function.identity())); + + Assert.assertEquals(ANNOTATION4, annotationMap.get(Annotation4.class)); + Assert.assertEquals(ANNOTATION6, annotationMap.get(Annotation6.class)); + Annotation3 resolvedAnnotation3 = (Annotation3)annotationMap.get(Annotation3.class); + Assert.assertNotNull(resolvedAnnotation3); + Assert.assertEquals(resolvedAnnotation3.alias(), ANNOTATION3.value()); + Assert.assertEquals(resolvedAnnotation3.alias(), resolvedAnnotation3.value()); // value与alias互为别名 + + Assert.assertNull(annotationMap.get(Annotation1.class)); + Assert.assertNull(annotationMap.get(Annotation2.class)); + Assert.assertNull(annotationMap.get(Annotation5.class)); + } + + @Test + public void testFindAllDirectlyResolvedAnnotations() { + + Assert.assertEquals(ANNOTATION4, AnnotatedElementUtil.findAllDirectlyResolvedAnnotations(Foo.class, Annotation4.class)[0]); + Assert.assertEquals(ANNOTATION6, AnnotatedElementUtil.findAllDirectlyResolvedAnnotations(Foo.class, Annotation6.class)[0]); + Annotation3 resolvedAnnotation3 = AnnotatedElementUtil.findAllDirectlyResolvedAnnotations(Foo.class, Annotation3.class)[0]; + Assert.assertNotNull(resolvedAnnotation3); + Assert.assertEquals(resolvedAnnotation3.alias(), ANNOTATION3.value()); + Assert.assertEquals(resolvedAnnotation3.alias(), resolvedAnnotation3.value()); // value与alias互为别名 + + Assert.assertEquals(0, AnnotatedElementUtil.findAllDirectlyResolvedAnnotations(Foo.class, Annotation1.class).length); + Assert.assertEquals(0, AnnotatedElementUtil.findAllDirectlyResolvedAnnotations(Foo.class, Annotation2.class).length); + Assert.assertEquals(0, AnnotatedElementUtil.findAllDirectlyResolvedAnnotations(Foo.class, Annotation5.class).length); + } + + @Test + public void testIsAnnotationPresent() { + Assert.assertTrue(AnnotatedElementUtil.isAnnotationPresent(Foo.class, Annotation1.class)); + Assert.assertTrue(AnnotatedElementUtil.isAnnotationPresent(Foo.class, Annotation2.class)); + Assert.assertTrue(AnnotatedElementUtil.isAnnotationPresent(Foo.class, Annotation3.class)); + + Assert.assertFalse(AnnotatedElementUtil.isAnnotationPresent(Foo.class, Annotation4.class)); + Assert.assertFalse(AnnotatedElementUtil.isAnnotationPresent(Foo.class, Annotation5.class)); + Assert.assertFalse(AnnotatedElementUtil.isAnnotationPresent(Foo.class, Annotation6.class)); + } + + @Test + public void testGetAnnotation() { + Assert.assertEquals(ANNOTATION1, AnnotatedElementUtil.getAnnotation(Foo.class, Annotation1.class)); + Assert.assertEquals(ANNOTATION2, AnnotatedElementUtil.getAnnotation(Foo.class, Annotation2.class)); + Assert.assertEquals(ANNOTATION3, AnnotatedElementUtil.getAnnotation(Foo.class, Annotation3.class)); + + Assert.assertNull(AnnotatedElementUtil.getAnnotation(Foo.class, Annotation4.class)); + Assert.assertNull(AnnotatedElementUtil.getAnnotation(Foo.class, Annotation5.class)); + Assert.assertNull(AnnotatedElementUtil.getAnnotation(Foo.class, Annotation6.class)); + } + + @Test + public void testGetAnnotations() { + Annotation[] annotations = AnnotatedElementUtil.getAnnotations(Foo.class); + Assert.assertArrayEquals( + new Annotation[]{ ANNOTATION3, ANNOTATION2, ANNOTATION1 }, + annotations + ); + } + + @Test + public void testGetResolvedAnnotation() { + Annotation3 resolvedAnnotation3 = AnnotatedElementUtil.getResolvedAnnotation(Foo.class, Annotation3.class); + Assert.assertNotNull(resolvedAnnotation3); + Assert.assertEquals(resolvedAnnotation3.alias(), ANNOTATION3.value()); + Assert.assertEquals(resolvedAnnotation3.alias(), resolvedAnnotation3.value()); // value与alias互为别名 + + Annotation2 resolvedAnnotation2 = AnnotatedElementUtil.getResolvedAnnotation(Foo.class, Annotation2.class); + Assert.assertNotNull(resolvedAnnotation2); + Assert.assertEquals(resolvedAnnotation2.num(), ANNOTATION3.num()); // num属性被Annotation3.num覆盖 + + Annotation1 resolvedAnnotation1 = AnnotatedElementUtil.getResolvedAnnotation(Foo.class, Annotation1.class); + Assert.assertNotNull(resolvedAnnotation1); + Assert.assertEquals(ANNOTATION3.value(), resolvedAnnotation1.value()); // value属性被Annotation3.value覆盖 + Assert.assertEquals(resolvedAnnotation1.value(), resolvedAnnotation1.alias()); // value与alias互为别名 + + Assert.assertNull(AnnotatedElementUtil.getResolvedAnnotation(Foo.class, Annotation4.class)); + Assert.assertNull(AnnotatedElementUtil.getResolvedAnnotation(Foo.class, Annotation5.class)); + Assert.assertNull(AnnotatedElementUtil.getResolvedAnnotation(Foo.class, Annotation6.class)); + } + + @Test + public void testGetResolvedAnnotations() { + Map, Annotation> annotationMap = Stream.of(AnnotatedElementUtil.getResolvedAnnotations(Foo.class)) + .collect(Collectors.toMap(Annotation::annotationType, Function.identity())); + + Annotation3 resolvedAnnotation3 = (Annotation3)annotationMap.get(Annotation3.class); + Assert.assertNotNull(resolvedAnnotation3); + Assert.assertEquals(resolvedAnnotation3.alias(), ANNOTATION3.value()); + Assert.assertEquals(resolvedAnnotation3.alias(), resolvedAnnotation3.value()); // value与alias互为别名 + + Annotation2 resolvedAnnotation2 = (Annotation2)annotationMap.get(Annotation2.class); + Assert.assertNotNull(resolvedAnnotation2); + Assert.assertEquals(resolvedAnnotation2.num(), ANNOTATION3.num()); // num属性被Annotation3.num覆盖 + + Annotation1 resolvedAnnotation1 = (Annotation1)annotationMap.get(Annotation1.class); + Assert.assertNotNull(resolvedAnnotation1); + Assert.assertEquals(ANNOTATION3.value(), resolvedAnnotation1.value()); // value属性被Annotation3.value覆盖 + Assert.assertEquals(resolvedAnnotation1.value(), resolvedAnnotation1.alias()); // value与alias互为别名 + + Assert.assertFalse(annotationMap.containsKey(Annotation4.class)); + Assert.assertFalse(annotationMap.containsKey(Annotation5.class)); + Assert.assertFalse(annotationMap.containsKey(Annotation6.class)); + } + + @Test + public void testGetDirectlyAnnotation() { + Assert.assertEquals(ANNOTATION3, AnnotatedElementUtil.getDirectlyAnnotation(Foo.class, Annotation3.class)); + + Assert.assertNull(AnnotatedElementUtil.getDirectlyAnnotation(Foo.class, Annotation2.class)); + Assert.assertNull(AnnotatedElementUtil.getDirectlyAnnotation(Foo.class, Annotation1.class)); + Assert.assertNull(AnnotatedElementUtil.getDirectlyAnnotation(Foo.class, Annotation4.class)); + Assert.assertNull(AnnotatedElementUtil.getDirectlyAnnotation(Foo.class, Annotation5.class)); + Assert.assertNull(AnnotatedElementUtil.getDirectlyAnnotation(Foo.class, Annotation6.class)); + } + + @Test + public void testGetDirectlyAnnotations() { + Annotation[] annotations = AnnotatedElementUtil.getDirectlyAnnotations(Foo.class); + Assert.assertEquals(1, annotations.length); + Assert.assertEquals(ANNOTATION3, annotations[0]); + } + + @Test + public void testGetDirectlyResolvedAnnotation() { + Annotation3 resolvedAnnotation3 = AnnotatedElementUtil.getDirectlyResolvedAnnotation(Foo.class, Annotation3.class); + Assert.assertNotNull(resolvedAnnotation3); + Assert.assertEquals(resolvedAnnotation3.alias(), ANNOTATION3.value()); + Assert.assertEquals(resolvedAnnotation3.alias(), resolvedAnnotation3.value()); // value与alias互为别名 + + Assert.assertNull(AnnotatedElementUtil.getDirectlyResolvedAnnotation(Foo.class, Annotation2.class)); + Assert.assertNull(AnnotatedElementUtil.getDirectlyResolvedAnnotation(Foo.class, Annotation1.class)); + Assert.assertNull(AnnotatedElementUtil.getDirectlyResolvedAnnotation(Foo.class, Annotation4.class)); + Assert.assertNull(AnnotatedElementUtil.getDirectlyResolvedAnnotation(Foo.class, Annotation5.class)); + Assert.assertNull(AnnotatedElementUtil.getDirectlyResolvedAnnotation(Foo.class, Annotation6.class)); + } + + @Test + public void testGetDirectlyResolvedAnnotations() { + Annotation[] annotations = AnnotatedElementUtil.getDirectlyResolvedAnnotations(Foo.class); + Assert.assertEquals(1, annotations.length); + + Annotation3 resolvedAnnotation3 = (Annotation3)annotations[0]; + Assert.assertNotNull(resolvedAnnotation3); + Assert.assertEquals(resolvedAnnotation3.alias(), ANNOTATION3.value()); + Assert.assertEquals(resolvedAnnotation3.alias(), resolvedAnnotation3.value()); // value与alias互为别名 + } + + @Test + public void testToHierarchyMetaElement() { + Assert.assertNotNull(AnnotatedElementUtil.toHierarchyMetaElement(null, false)); + Assert.assertNotNull(AnnotatedElementUtil.toHierarchyMetaElement(null, true)); + AnnotatedElement element = AnnotatedElementUtil.toHierarchyMetaElement(Foo.class, false); + + // 带有元注解 + Assert.assertArrayEquals(ANNOTATIONS, element.getAnnotations()); + + // 不带元注解 + Assert.assertArrayEquals(DECLARED_ANNOTATIONS, element.getDeclaredAnnotations()); + + // 解析注解属性 + AnnotatedElement resolvedElement = AnnotatedElementUtil.toHierarchyMetaElement(Foo.class, true); + Annotation3 resolvedAnnotation3 = resolvedElement.getAnnotation(Annotation3.class); + Assert.assertNotNull(resolvedAnnotation3); + Assert.assertEquals(resolvedAnnotation3.alias(), ANNOTATION3.value()); + Assert.assertEquals(resolvedAnnotation3.alias(), resolvedAnnotation3.value()); // value与alias互为别名 + + Annotation2 resolvedAnnotation2 = resolvedElement.getAnnotation(Annotation2.class); + Assert.assertNotNull(resolvedAnnotation2); + Assert.assertEquals(resolvedAnnotation2.num(), ANNOTATION3.num()); // num属性被Annotation3.num覆盖 + + Annotation1 resolvedAnnotation1 = resolvedElement.getAnnotation(Annotation1.class); + Assert.assertNotNull(resolvedAnnotation1); + Assert.assertEquals(ANNOTATION3.value(), resolvedAnnotation1.value()); // value属性被Annotation3.value覆盖 + Assert.assertEquals(resolvedAnnotation1.value(), resolvedAnnotation1.alias()); // value与alias互为别名 + + Assert.assertEquals(ANNOTATION4, resolvedElement.getAnnotation(Annotation4.class)); + Assert.assertEquals(ANNOTATION6, resolvedElement.getAnnotation(Annotation6.class)); + Assert.assertEquals(ANNOTATION5, resolvedElement.getAnnotation(Annotation5.class)); + } + + @Test + public void testToHierarchyElement() { + Assert.assertNotNull(AnnotatedElementUtil.toHierarchyElement(Foo.class)); + AnnotatedElement element = AnnotatedElementUtil.toHierarchyElement(Foo.class); + Assert.assertArrayEquals(new Annotation[]{ANNOTATION3, ANNOTATION4, ANNOTATION6}, element.getAnnotations()); + } + + @Test + public void testToMetaElement() { + Assert.assertNotNull(AnnotatedElementUtil.toMetaElement(null, false)); + Assert.assertNotNull(AnnotatedElementUtil.toMetaElement(null, true)); + + // 不解析注解属性 + AnnotatedElement element = AnnotatedElementUtil.toMetaElement(Foo.class, false); + Assert.assertSame(element, AnnotatedElementUtil.toMetaElement(Foo.class, false)); // 第二次获取时从缓存中获取 + Assert.assertArrayEquals(new Annotation[]{ANNOTATION3, ANNOTATION2, ANNOTATION1}, element.getAnnotations()); + + // 解析注解属性 + element = AnnotatedElementUtil.toMetaElement(Foo.class, true); + Assert.assertSame(element, AnnotatedElementUtil.toMetaElement(Foo.class, true)); // 第二次获取时从缓存中获取 + Assert.assertEquals(3, element.getAnnotations().length); + + Annotation3 resolvedAnnotation3 = element.getAnnotation(Annotation3.class); + Assert.assertNotNull(resolvedAnnotation3); + Assert.assertEquals(resolvedAnnotation3.alias(), ANNOTATION3.value()); + Assert.assertEquals(resolvedAnnotation3.alias(), resolvedAnnotation3.value()); // value与alias互为别名 + + Annotation2 resolvedAnnotation2 = element.getAnnotation(Annotation2.class); + Assert.assertNotNull(resolvedAnnotation2); + Assert.assertEquals(resolvedAnnotation2.num(), ANNOTATION3.num()); // num属性被Annotation3.num覆盖 + + Annotation1 resolvedAnnotation1 = element.getAnnotation(Annotation1.class); + Assert.assertNotNull(resolvedAnnotation1); + Assert.assertEquals(ANNOTATION3.value(), resolvedAnnotation1.value()); // value属性被Annotation3.value覆盖 + Assert.assertEquals(resolvedAnnotation1.value(), resolvedAnnotation1.alias()); // value与alias互为别名 + } + + @Test + public void testAsElement() { + Annotation[] annotations = new Annotation[]{ANNOTATION1, ANNOTATION2}; + Assert.assertNotNull(AnnotatedElementUtil.asElement()); + + AnnotatedElement element = AnnotatedElementUtil.asElement(ANNOTATION1, null, ANNOTATION2); + Assert.assertArrayEquals(annotations, element.getAnnotations()); + Assert.assertArrayEquals(annotations, element.getDeclaredAnnotations()); + Assert.assertEquals(ANNOTATION1, element.getAnnotation(Annotation1.class)); + Assert.assertNull(element.getAnnotation(Annotation3.class)); + } + + @Test + public void testEmptyElement() { + AnnotatedElement element = AnnotatedElementUtil.emptyElement(); + Assert.assertSame(element, AnnotatedElementUtil.emptyElement()); + Assert.assertNull(element.getAnnotation(Annotation1.class)); + Assert.assertEquals(0, element.getAnnotations().length); + Assert.assertEquals(0, element.getDeclaredAnnotations().length); + } + + // ================= super ================= + + @Target(ElementType.TYPE_USE) + @Retention(RetentionPolicy.RUNTIME) + private @interface Annotation4 {} + + @Annotation4 + private static class Super {}; + + // ================= interface ================= + + @Target(ElementType.TYPE_USE) + @Retention(RetentionPolicy.RUNTIME) + private @interface Annotation5 {} + + @Annotation5 + @Target(ElementType.TYPE_USE) + @Retention(RetentionPolicy.RUNTIME) + private @interface Annotation6 {} + + @Annotation6 + private interface Interface {}; + + // ================= foo ================= + + @Target(ElementType.TYPE_USE) + @Retention(RetentionPolicy.RUNTIME) + private @interface Annotation1 { + @Alias("alias") + String value() default ""; + @Alias("value") + String alias() default ""; + } + + @Annotation1 + @Target(ElementType.TYPE_USE) + @Retention(RetentionPolicy.RUNTIME) + private @interface Annotation2 { + int num() default Integer.MIN_VALUE; + } + + @Annotation2 + @Target(ElementType.TYPE_USE) + @Retention(RetentionPolicy.RUNTIME) + private @interface Annotation3 { + @Alias("alias") + String value() default ""; + @Alias("value") + String alias() default ""; + int num() default Integer.MIN_VALUE; + } + + @Annotation3(value = "foo", num = Integer.MAX_VALUE) + private static class Foo extends Super implements Interface {} + +}