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 index 2407de316..34ee2974b 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotatedElementUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotatedElementUtil.java @@ -93,11 +93,41 @@ import java.util.stream.Stream; * * * + *
可重复注解支持 + *
工具类中格式为findAllXXX或getAllXXX格式的方法, + * 支持获得{@link AnnotatedElement}上的可重复注解。 + * 此处的可重复注解定义包括两方面: + *
缓存 + *
为了避免注解以及{@link AnnotatedElement}层级结构解析过程中的大量反射调用,
+ * 工具类为{@link AnnotatedElement}及其元注解信息进行了缓存。 扫描{@code element}所处层级结构中的{@link AnnotatedElement},
+ * 并将其全部转为{@link RepeatableMetaAnnotatedElement}后,
+ * 再把所有对象合并为{@link HierarchicalAnnotatedElements}。 扫描{@code element}所处层级结构中的{@link AnnotatedElement},
* 再把所有对象合并为{@link HierarchicalAnnotatedElements}
@@ -460,6 +580,49 @@ public class AnnotatedElementUtil {
);
}
+ /**
+ * 将{@link AnnotatedElement}转为{@link RepeatableMetaAnnotatedElement},
+ * 得到的对象可访问{@link AnnotatedElement}上的直接声明的注解,这些注解包含的可重复注解,以及上述注解的所有元注解。
+ *
+ * @param element 元素
+ * @param resolved 是否解析注解属性,若为{@code true}则获得的注解将支持属性别名以及属性覆盖机制
+ * @return {@link AnnotatedElement}实例
+ * @see #getMetaElementCache(AnnotatedElement)
+ * @see #getResolvedMetaElementCache(AnnotatedElement)
+ */
+ public static AnnotatedElement toRepeatableMetaElement(final AnnotatedElement element, final boolean resolved) {
+ return ObjUtil.defaultIfNull(
+ element, e -> resolved ? getResolvedRepeatableMetaElementCache(e) : getRepeatableMetaElementCache(e), emptyElement()
+ );
+ }
+
+ /**
+ * 将{@link AnnotatedElement}转为{@link RepeatableMetaAnnotatedElement},
+ * 得到的对象可访问{@link AnnotatedElement}上的直接声明的注解,
+ * 通过{@code collector}从这些注解获得的可重复注解,以及上述注解的所有元注解。 注解元素映射,用于包装一个{@link AnnotatedElement},然后将被包装的元素上,
@@ -21,8 +20,11 @@ import java.util.stream.Stream;
* 并且在当前实例中,{@link Inherited}注解将不生效,
* 即通过directly方法将无法获得父类上带有{@link Inherited}的注解。
*
- * 当通过静态工厂方法创建时,该实例与关联的{@link ResolvedAnnotationMapping}都会针对{@link ResolvedAnnotationMapping}进行缓存,
- * 从而避免频繁的反射与代理造成不必要的性能损耗。
+ * 在一个{@link MetaAnnotatedElement}中,
+ * {@link AnnotatedElement}上同类型的注解或元注解只会被保留一个,
+ * 即当出现两个根注解都具有相同元注解时,仅有第一个根注解上的元注解会被保留,
+ * 因此当通过{@link #getAnnotationsByType(Class)}
+ * 或{@link #getDeclaredAnnotationsByType(Class)}方法用于只能获得一个注解对象。
*
* @author huangchengxing
* @see ResolvedAnnotationMapping
@@ -214,6 +216,34 @@ public class MetaAnnotatedElement 当注解中有且仅有一个名为{@code value}的属性时,
+ * 若该属性类型为注解数组,且该数组对应的注解类型被{@link Repeatable}注解,
+ * 则收集器将返回该属性中包括的可重复注解。 当注解中存在有属性为注解数组,且该数组对应的注解类型被{@link Repeatable}注解时,
+ * 认为该属性包含可重复注解。 若一个注解是可重复注解的容器注解,则尝试通过其属性获得获得包含的注解对象。
+ * 若包含的注解对象也是可重复注解的容器注解,则继续解析直到获得所有非容器注解为止。 若一个注解是可重复注解的容器注解,则尝试通过其属性获得获得包含的注解对象。
+ * 若包含的注解对象也是可重复注解的容器注解,则继续解析直到获得所有非容器注解为止。 若一个注解是可重复注解的容器注解,则尝试通过其属性获得获得包含的指定类型注解对象。 若一个注解是可重复注解的容器注解,则尝试通过其属性获得获得包含的注解对象。
+ * 若包含的注解对象也是可重复注解的容器注解,则继续解析直到获得所有非容器注解为止。
+ *
+ * @param annotation 容器注解
+ * @return 容器注解中的可重复注解,若{@code annotation}不为容器注解,则数组中仅有其本身一个对象
+ */
+ @Override
+ public final List 若一个注解是可重复注解的容器注解,则尝试通过其属性获得获得包含的注解对象。
+ * 若包含的注解对象也是可重复注解的容器注解,则继续解析直到获得所有非容器注解为止。 支持可重复注解的增强{@link AnnotatedElement},
+ * 功能与{@link MetaAnnotatedElement}类似,但是存在下述差异:
+ * Foo.class上注解情况如下:
+ * Super.class上注解情况如下:
+ * Interface.class上注解情况如下:
+ *
+ * 缓存功能默认基于{@link WeakConcurrentMap}实现,会在gc时自动回收部分缓存数据。
+ * 但是若有必要,也可以调用{@link #clearCaches()}方法主动清空缓存。
+ *
* @author huangchengxing
* @see ResolvedAnnotationMapping
* @see GenericAnnotationMapping
* @see HierarchicalAnnotatedElements
+ * @see RepeatableMetaAnnotatedElement
* @see MetaAnnotatedElement
+ * @see RepeatableAnnotationCollector
* @since 6.0.0
*/
public class AnnotatedElementUtil {
@@ -112,6 +142,16 @@ public class AnnotatedElementUtil {
*/
private static final Map
+ * 从{@code element}所处层级结构的所有{@link AnnotatedElement}上直接声明的注解、
+ * 这些注解包含的可重复注解,以及上述所有注解的元注解中获取指定类型注解。
* 得到的注解支持基于{@link Alias}的别名、及子注解对元注解中同名同类型属性进行覆写的特殊机制。
*
* @param element {@link AnnotatedElement}
@@ -199,7 +241,7 @@ public class AnnotatedElementUtil {
* @return 注解对象
*/
public static
+ * 从{@code element}所处层级结构的所有{@link AnnotatedElement}上直接声明的注解、
+ * 这些注解包含的可重复注解,以及上述所有注解的元注解中获取指定类型注解。
* 得到的注解支持基于{@link Alias}的别名、及子注解对元注解中同名同类型属性进行覆写的特殊机制。
*
* @param element {@link AnnotatedElement}
@@ -280,7 +324,7 @@ public class AnnotatedElementUtil {
* @return 注解对象
*/
public static
* 得到的注解支持基于{@link Alias}的别名机制。
@@ -350,6 +407,20 @@ public class AnnotatedElementUtil {
.getAnnotations();
}
+ /**
+ * 从{@code element}上直接声明的注解、这些注解包含的可重复注解,以及上述所有注解的元注解中获取指定类型注解
+ * 得到的注解支持基于{@link Alias}的别名机制。
+ *
+ * @param element {@link AnnotatedElement}
+ * @param annotationType 注解类型
+ * @param
+ * 得到的对象可访问{@code element}所处层级结构中所有{@link AnnotatedElement}上的直接声明的注解,
+ * 这些注解包含的可重复注解,以及上述注解的所有元注解。
+ *
+ * @param element 元素
+ * @param resolved 是否解析注解属性,若为{@code true}则获得的注解将支持属性别名以及属性覆盖机制
+ * @return {@link HierarchicalAnnotatedElements}实例
+ * @see #getRepeatableMetaElementCache(AnnotatedElement)
+ * @see #getResolvedRepeatableMetaElementCache(AnnotatedElement)
+ */
+ public static AnnotatedElement toHierarchyRepeatableMetaElement(final AnnotatedElement element, final boolean resolved) {
+ if (Objects.isNull(element)) {
+ return emptyElement();
+ }
+ if (resolved) {
+ return HierarchicalAnnotatedElements.create(element, (es, e) -> getResolvedRepeatableMetaElementCache(e));
+ }
+ return HierarchicalAnnotatedElements.create(element, (es, e) -> getRepeatableMetaElementCache(e));
+ }
+
/**
*
+ * 注意:方法将不会通过缓存结果,因此每次调用都需要重新通过反射并获得相关注解。
+ *
+ * @param collector 可重复注解收集器,为{@code null}时等同于{@link RepeatableAnnotationCollector#none()}
+ * @param element 元素
+ * @param resolved 是否解析注解属性,若为{@code true}则获得的注解将支持属性别名以及属性覆盖机制
+ * @return {@link AnnotatedElement}实例
+ */
+ public static AnnotatedElement toRepeatableMetaElement(
+ final AnnotatedElement element, RepeatableAnnotationCollector collector, final boolean resolved) {
+ if (Objects.isNull(element)) {
+ return emptyElement();
+ }
+ collector = ObjUtil.defaultIfNull(collector, RepeatableAnnotationCollector.none());
+ if (resolved) {
+ return RepeatableMetaAnnotatedElement.create(
+ collector, element, (source, annotation) -> ResolvedAnnotationMapping.create((ResolvedAnnotationMapping)source, annotation, true)
+ );
+ }
+ return RepeatableMetaAnnotatedElement.create(
+ collector, element, (source, annotation) -> GenericAnnotationMapping.create(annotation, Objects.isNull(source))
+ );
+ }
+
/**
* 将一组注解中的非{@code null}注解对象合并为一个{@link AnnotatedElement}
*
@@ -493,26 +656,70 @@ public class AnnotatedElementUtil {
* @param element {@link AnnotatedElement}
* @return {@link MetaAnnotatedElement}实例
*/
- private static MetaAnnotatedElement
+ *
+ *
+ * @see AnnotationUtil#clearCaches()
+ * @see RepeatableAnnotationCollector#clearSingletonCaches()
+ */
+ public static void clearCaches() {
+ ELEMENT_CACHE.clear();
+ RESOLVED_ELEMENT_CACHE.clear();
+ REPEATABLE_ELEMENT_CACHE.clear();
+ RESOLVED_REPEATABLE_ELEMENT_CACHE.clear();
+ RepeatableAnnotationCollector.clearSingletonCaches();
+ AnnotationUtil.clearCaches();
+ }
+
/**
* 由一组注解聚合来的{@link AnnotatedElement}
*/
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 124ac5eac..6691928d6 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,8 @@
package cn.hutool.core.annotation;
import cn.hutool.core.exceptions.UtilException;
+import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.map.WeakConcurrentMap;
import cn.hutool.core.reflect.FieldUtil;
import cn.hutool.core.reflect.MethodUtil;
import cn.hutool.core.util.ArrayUtil;
@@ -13,7 +15,6 @@ import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
-import java.util.Objects;
import java.util.function.Predicate;
import java.util.stream.Stream;
@@ -26,6 +27,22 @@ import java.util.stream.Stream;
*/
public class AnnotationUtil {
+ /**
+ * 直接声明的注解缓存
+ */
+ private static final Map
+ * eg:
+ *
+ * 解析任意{@code Annotation}注解对象,则可以获得{@code value}属性中的{@code Item}注解对象
+ *
+ * @return {@link RepeatableAnnotationCollector}实例
+ * @see Standard
+ */
+ static RepeatableAnnotationCollector standard() {
+ return Standard.INSTANCE;
+ }
+
+ /**
+ * 当解析注解属性时,将根据给定的判断条件,确定该属性中是否含有可重复注解。
+ * // 容器注解
+ * {@literal @}interface Annotation {
+ * Item[] value() default {};
+ * }
+ * // 可重复注解
+ * {@literal @}Repeatable(Annotation.class)
+ * {@literal @}interface Item {}
+ *
+ * 收集器将返回所有匹配的属性中的可重复注解。
+ *
+ * @param predicate 是否为容纳可重复注解的属性的判断条件
+ * @return {@link RepeatableAnnotationCollector}实例
+ */
+ static RepeatableAnnotationCollector condition(final BiPredicate
+ * 收集器将返回所有符合上述条件的属性中的可重复注解。
+ * eg:
+ *
+ * 解析任意{@code Annotation}注解对象,
+ * 则可以获得{@code items1}属性中的{@code Item1}注解对象,
+ * 以及{@code items2}属性中的{@code Item2}注解对象,
+ *
+ * @return {@link RepeatableAnnotationCollector}实例
+ */
+ static RepeatableAnnotationCollector full() {
+ return Full.INSTANCE;
+ }
+
+ /**
+ * 清空单例缓存
+ */
+ static void clearSingletonCaches() {
+ Standard.INSTANCE.repeatableMethodCache.clear();
+ Full.INSTANCE.repeatableMethodCache.clear();
+ }
+
+ /**
+ *
+ * {@literal @}interface Annotation {
+ * Item1[] items1() default {};
+ * Item2[] items2() default {};
+ * }
+ *
+ * eg:
+ * 若存在嵌套关系{@code a -> b -> c},
+ * 则解析注解a,则将得到全部c注解。
+ * 如果注解不包含可重复注解,则返回a本身。
+ *
+ * @param annotation 容器注解
+ * @return 容器注解中的可重复注解,若{@code annotation}不为容器注解,则数组中仅有其本身一个对象
+ */
+ List
+ * eg:
+ * 若存在嵌套关系{@code a -> b -> c},则解析注解a,
+ * 将获得全部a、b、c注解。
+ * 如果注解不包含可重复注解,则返回a本身。
+ *
+ * @param annotation 容器注解
+ * @return 容器注解中的可重复注解,若{@code annotation}不为容器注解,则数组中仅有其本身一个对象
+ */
+ List
+ * eg:
+ * 若存在嵌套关系{@code a -> b -> c},则:
+ *
+ *
+ *
+ * @param annotation 容器注解
+ * @param annotationType 注解类型
+ * @param
+ * 当{@code accumulate}为{@code true}时,返回结果为全量的注解。
+ * eg:
+ * 若存在嵌套关系{@code a -> b -> c},则解析注解a,
+ * 将获得全部a、b、c注解。
+ * 如果注解不包含可重复注解,则返回其本身。
+ *
+ * @param annotation 容器注解
+ * @return 容器注解中的可重复注解,若{@code annotation}不为容器注解,则数组中仅有其本身一个对象
+ */
+ @Override
+ public List
+ * 收集器将返回所有符合上述条件的属性中的可重复注解。
+ */
+ class Full extends AbstractCollector {
+
+ /**
+ * 默认实例
+ */
+ private static final Full INSTANCE = new Full();
+
+ /**
+ * 空方法缓存
+ */
+ private static final Object NONE = new Object();
+
+ /**
+ * 可重复注解对应的方法缓存
+ */
+ private final Map
+ *
+ * 由于上述机制,当通过实例的{@link #getAnnotation(Class)}或{@link #getDeclaredAnnotation(Class)}
+ * 方法获得指定类型注解时,若该类型注解存在多个,仅能尽可能获得最先被扫描到的那一个。
+ *
+ * @author huangchengxing
+ * @since 6.0.0
+ * @see RepeatableAnnotationCollector
+ */
+public class RepeatableMetaAnnotatedElement
+ * eg:
+ * A上存在注解X,该注解是一个容器注解,内部可重复注解Y,
+ * 包含解析后,得到注解X与可重复注解Y,
+ * 同理,若存在X、Y、X的嵌套关系,则解析后获得全部三者;
+ *
+ * 默认情况下,已经处理过、或在{@link java.lang}包下的注解不会被处理
+ */
+ private boolean isNeedMapping(final Map
* 接口获取方法和默认方法,获取的方法包括:
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
index 03ed704a6..7445efde8 100644
--- a/hutool-core/src/test/java/cn/hutool/core/annotation/AnnotatedElementUtilTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/annotation/AnnotatedElementUtilTest.java
@@ -37,6 +37,31 @@ public class AnnotatedElementUtilTest {
ANNOTATION6, ANNOTATION5 // Interface.class's annotations
};
+ @Test
+ public void testClearCaches() {
+ AnnotatedElement type = Foo.class;
+
+ AnnotatedElement element = AnnotatedElementUtil.getResolvedMetaElementCache(type);
+ Assert.assertSame(element, AnnotatedElementUtil.getResolvedMetaElementCache(type));
+ AnnotatedElementUtil.clearCaches();
+ Assert.assertNotSame(element, AnnotatedElementUtil.getResolvedMetaElementCache(type));
+
+ element = AnnotatedElementUtil.getMetaElementCache(type);
+ Assert.assertSame(element, AnnotatedElementUtil.getMetaElementCache(type));
+ AnnotatedElementUtil.clearCaches();
+ Assert.assertNotSame(element, AnnotatedElementUtil.getMetaElementCache(type));
+
+ element = AnnotatedElementUtil.getResolvedRepeatableMetaElementCache(type);
+ Assert.assertSame(element, AnnotatedElementUtil.getResolvedRepeatableMetaElementCache(type));
+ AnnotatedElementUtil.clearCaches();
+ Assert.assertNotSame(element, AnnotatedElementUtil.getResolvedRepeatableMetaElementCache(type));
+
+ element = AnnotatedElementUtil.getRepeatableMetaElementCache(type);
+ Assert.assertSame(element, AnnotatedElementUtil.getRepeatableMetaElementCache(type));
+ AnnotatedElementUtil.clearCaches();
+ Assert.assertNotSame(element, AnnotatedElementUtil.getRepeatableMetaElementCache(type));
+ }
+
@Test
public void testIsAnnotated() {
Assert.assertTrue(AnnotatedElementUtil.isAnnotated(Foo.class, Annotation1.class));
@@ -243,6 +268,25 @@ public class AnnotatedElementUtilTest {
);
}
+ @Test
+ public void testGetAllAnnotations() {
+ Annotation3[] resolvedAnnotation3s = AnnotatedElementUtil.getAllAnnotations(Foo.class, Annotation3.class);
+ Assert.assertEquals(1, resolvedAnnotation3s.length);
+ Assert.assertEquals(ANNOTATION3, resolvedAnnotation3s[0]); // value与alias互为别名
+
+ Annotation2[] resolvedAnnotation2s = AnnotatedElementUtil.getAllAnnotations(Foo.class, Annotation2.class);
+ Assert.assertEquals(1, resolvedAnnotation2s.length);
+ Assert.assertEquals(ANNOTATION2, resolvedAnnotation2s[0]); // value与alias互为别名
+
+ Annotation1[] resolvedAnnotation1s = AnnotatedElementUtil.getAllAnnotations(Foo.class, Annotation1.class);
+ Assert.assertEquals(1, resolvedAnnotation1s.length);
+ Assert.assertEquals(ANNOTATION1, resolvedAnnotation1s[0]);
+
+ Assert.assertEquals(0, AnnotatedElementUtil.getAllAnnotations(Foo.class, Annotation4.class).length);
+ Assert.assertEquals(0, AnnotatedElementUtil.getAllAnnotations(Foo.class, Annotation5.class).length);
+ Assert.assertEquals(0, AnnotatedElementUtil.getAllAnnotations(Foo.class, Annotation6.class).length);
+ }
+
@Test
public void testGetResolvedAnnotation() {
Annotation3 resolvedAnnotation3 = AnnotatedElementUtil.getResolvedAnnotation(Foo.class, Annotation3.class);
@@ -264,6 +308,33 @@ public class AnnotatedElementUtilTest {
Assert.assertNull(AnnotatedElementUtil.getResolvedAnnotation(Foo.class, Annotation6.class));
}
+ @Test
+ public void testGetAllResolvedAnnotations() {
+ Annotation3[] resolvedAnnotation3s = AnnotatedElementUtil.getAllResolvedAnnotations(Foo.class, Annotation3.class);
+ Assert.assertEquals(1, resolvedAnnotation3s.length);
+ Annotation3 resolvedAnnotation3 = resolvedAnnotation3s[0];
+ Assert.assertNotNull(resolvedAnnotation3);
+ Assert.assertEquals(resolvedAnnotation3.alias(), ANNOTATION3.value());
+ Assert.assertEquals(resolvedAnnotation3.alias(), resolvedAnnotation3.value()); // value与alias互为别名
+
+ Annotation2[] resolvedAnnotation2s = AnnotatedElementUtil.getAllResolvedAnnotations(Foo.class, Annotation2.class);
+ Assert.assertEquals(1, resolvedAnnotation2s.length);
+ Annotation2 resolvedAnnotation2 = resolvedAnnotation2s[0];
+ Assert.assertNotNull(resolvedAnnotation2);
+ Assert.assertEquals(resolvedAnnotation2.num(), ANNOTATION3.num()); // num属性被Annotation3.num覆盖
+
+ Annotation1[] resolvedAnnotation1s = AnnotatedElementUtil.getAllResolvedAnnotations(Foo.class, Annotation1.class);
+ Assert.assertEquals(1, resolvedAnnotation1s.length);
+ Annotation1 resolvedAnnotation1 = resolvedAnnotation1s[0];
+ Assert.assertNotNull(resolvedAnnotation1);
+ Assert.assertEquals(ANNOTATION3.value(), resolvedAnnotation1.value()); // value属性被Annotation3.value覆盖
+ Assert.assertEquals(resolvedAnnotation1.value(), resolvedAnnotation1.alias()); // value与alias互为别名
+
+ Assert.assertEquals(0, AnnotatedElementUtil.getAllResolvedAnnotations(Foo.class, Annotation4.class).length);
+ Assert.assertEquals(0, AnnotatedElementUtil.getAllResolvedAnnotations(Foo.class, Annotation5.class).length);
+ Assert.assertEquals(0, AnnotatedElementUtil.getAllResolvedAnnotations(Foo.class, Annotation6.class).length);
+ }
+
@Test
public void testGetResolvedAnnotations() {
Map{@code
+ * annotation3 => annotation2 => annotation1
+ * }
+ *
+ * {@code
+ * annotation4
+ * }
+ *
+ * {@code
+ * annotation6 => annotation5 => annotation6【循环引用】
+ * }
+ */
@Annotation3(value = "foo", num = Integer.MAX_VALUE)
private static class Foo extends Super implements Interface {}
diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/AnnotationUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/AnnotationUtilTest.java
index 5d3612bc8..6053d8851 100755
--- a/hutool-core/src/test/java/cn/hutool/core/annotation/AnnotationUtilTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/annotation/AnnotationUtilTest.java
@@ -1,10 +1,12 @@
package cn.hutool.core.annotation;
import cn.hutool.core.util.ObjUtil;
+import lombok.SneakyThrows;
import org.junit.Assert;
import org.junit.Test;
import java.lang.annotation.*;
+import java.lang.reflect.Method;
import java.util.Map;
/**
@@ -12,6 +14,16 @@ import java.util.Map;
*/
public class AnnotationUtilTest {
+ @Test
+ public void testGetDeclaredAnnotations() {
+ Annotation[] annotations = AnnotationUtil.getDeclaredAnnotations(ClassForTest.class);
+ Assert.assertArrayEquals(annotations, ClassForTest.class.getDeclaredAnnotations());
+ Assert.assertSame(annotations, AnnotationUtil.getDeclaredAnnotations(ClassForTest.class));
+
+ AnnotationUtil.clearCaches();
+ Assert.assertNotSame(annotations, AnnotationUtil.getDeclaredAnnotations(ClassForTest.class));
+ }
+
@Test
public void testToCombination() {
final CombinationAnnotationElement element = AnnotationUtil.toCombination(ClassForTest.class);
@@ -115,6 +127,21 @@ public class AnnotationUtilTest {
Assert.assertEquals(MetaAnnotationForTest.class, annotation.annotationType());
}
+ @Test
+ public void testGetAnnotationAttributes() {
+ Method[] methods = AnnotationUtil.getAnnotationAttributes(AnnotationForTest.class);
+ Assert.assertArrayEquals(methods, AnnotationUtil.getAnnotationAttributes(AnnotationForTest.class));
+ Assert.assertEquals(1, methods.length);
+ Assert.assertArrayEquals(AnnotationForTest.class.getDeclaredMethods(), methods);
+ }
+
+ @SneakyThrows
+ @Test
+ public void testIsAnnotationAttribute() {
+ Assert.assertFalse(AnnotationUtil.isAnnotationAttribute(AnnotationForTest.class.getMethod("equals", Object.class)));
+ Assert.assertTrue(AnnotationUtil.isAnnotationAttribute(AnnotationForTest.class.getMethod("value")));
+ }
+
@Target(ElementType.TYPE_USE)
@Retention(RetentionPolicy.RUNTIME)
private @interface MetaAnnotationForTest{
diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/MetaAnnotatedElementTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/MetaAnnotatedElementTest.java
index fd8a2f3cc..f4cdfdc1d 100644
--- a/hutool-core/src/test/java/cn/hutool/core/annotation/MetaAnnotatedElementTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/annotation/MetaAnnotatedElementTest.java
@@ -111,6 +111,7 @@ public class MetaAnnotatedElementTest {
new Annotation[]{ annotation4 },
element.getAnnotationsByType(Annotation4.class)
);
+ Assert.assertEquals(0, element.getAnnotationsByType(None.class).length);
}
@Test
@@ -121,6 +122,7 @@ public class MetaAnnotatedElementTest {
new Annotation[]{ annotation4 },
element.getDeclaredAnnotationsByType(Annotation4.class)
);
+ Assert.assertEquals(0, element.getDeclaredAnnotationsByType(None.class).length);
}
@Test
@@ -168,6 +170,12 @@ public class MetaAnnotatedElementTest {
Assert.assertSame(source, element.getElement());
}
+ @Annotation4 // 循环引用
+ @Target(ElementType.TYPE_USE)
+ @Retention(RetentionPolicy.RUNTIME)
+ private @interface None { }
+
+ @Annotation4 // 循环引用
@Target(ElementType.TYPE_USE)
@Retention(RetentionPolicy.RUNTIME)
private @interface Annotation1 {
diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/RepeatableAnnotationCollectorTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/RepeatableAnnotationCollectorTest.java
new file mode 100644
index 000000000..ac9386a57
--- /dev/null
+++ b/hutool-core/src/test/java/cn/hutool/core/annotation/RepeatableAnnotationCollectorTest.java
@@ -0,0 +1,220 @@
+package cn.hutool.core.annotation;
+
+import cn.hutool.core.text.CharSequenceUtil;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.lang.annotation.*;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.BiPredicate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * test for {@link RepeatableAnnotationCollector}
+ *
+ * @author huangchengxing
+ */
+public class RepeatableAnnotationCollectorTest {
+
+ private static final Annotation1 ANNOTATION1 = Foo.class.getAnnotation(Annotation1.class);
+ private static final List