From a1a199513f22e88a51500463960c0dac9aa45b3e Mon Sep 17 00:00:00 2001 From: huangchengxing <841396397@qq.com> Date: Tue, 13 Sep 2022 13:56:41 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=B1=9E=E6=80=A7=E6=98=A0=E5=B0=84=E7=9A=84=E5=A2=9E=E5=BC=BA?= =?UTF-8?q?=E6=B3=A8=E8=A7=A3=E5=8C=85=E8=A3=85=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/hutool/core/annotation/Alias.java | 12 +- .../core/annotation/AnnotationMapping.java | 89 +++ .../annotation/AnnotationMappingProxy.java | 177 +++++ .../core/annotation/AnnotationUtil.java | 58 +- .../annotation/GenericAnnotationMapping.java | 156 ++++ .../annotation/ResolvedAnnotationMapping.java | 682 ++++++++++++++++++ .../GenericAnnotationMappingTest.java | 120 +++ .../ResolvedAnnotationMappingTest.java | 300 ++++++++ 8 files changed, 1581 insertions(+), 13 deletions(-) create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationMapping.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationMappingProxy.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/GenericAnnotationMapping.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/ResolvedAnnotationMapping.java create mode 100644 hutool-core/src/test/java/cn/hutool/core/annotation/GenericAnnotationMappingTest.java create mode 100644 hutool-core/src/test/java/cn/hutool/core/annotation/ResolvedAnnotationMappingTest.java diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/Alias.java b/hutool-core/src/main/java/cn/hutool/core/annotation/Alias.java index 3c1274e5f..142a4fc41 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/Alias.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/Alias.java @@ -1,13 +1,13 @@ package cn.hutool.core.annotation; -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; +import java.lang.annotation.*; /** - * 别名注解,使用此注解的字段、方法、参数等会有一个别名,用于Bean拷贝、Bean转Map等 + *
别名注解,使用此注解的字段、方法、参数等会有一个别名,用于Bean拷贝、Bean转Map等。 + * + *
当在注解中使用时,可为令多个属性互相关联,当对其中任意属性赋值时,
+ * 会将属性值一并同步到所有关联的属性中。 注解映射,用于包装并增强一个普通注解对象,
+ * 包装后的可以通过{@code getResolvedXXX}获得注解对象或属性值,
+ * 可以支持属性别名与属性覆写的属性解析机制。
+ *
+ * 父子注解
+ * 当实例创建时,可通过{@link #source}指定当前注解的子注解,多个实例通过该引用,
+ * 可以构成一条表示父子/元注解关系的单向链表。 属性别名
+ * 注解内的属性可以通过{@link Alias}互相关联,当解析时,
+ * 对绑定中的任意一个属性的赋值,会被同步给其他直接或者间接关联的属性。 属性覆写
+ * 当实例中{@link #source}不为{@code null},即当前注解存在至少一个或者多个子注解时,
+ * 若在子注解中的同名、同类型的属性,则获取值时将优先获取子注解的值,若该属性存在别名,则别名属性也如此。 注解元素映射,用于包装一个{@link AnnotatedElement},然后将被包装的元素上,
+ * 直接声明的注解以及这些注解的元组全部解析为{@link ResolvedAnnotationMapping}。
+ * 从而用于支持对元注解的访问操作。 默认情况下,总是不扫描{@link java.lang}包下的注解,
+ * 并且在当前实例中,{@link Inherited}注解将不生效,
+ * 即通过directly方法将无法获得父类上带有{@link Inherited}的注解。
+ *
+ * 当通过静态工厂方法创建时,该实例与关联的{@link ResolvedAnnotationMapping}都会针对{@link ResolvedAnnotationMapping}进行缓存,
+ * 从而避免频繁的反射与代理造成不必要的性能损耗。
+ *
+ * @author huangchengxing
+ * @see ResolvedAnnotationMapping
+ * @since 6.0.0
+ */
+public class MetaAnnotatedElement 表示一组处于在层级结构中具有关联关系的{@link AnnotatedElement},创建实例时,
+ * 将扫描指定{@link AnnotatedElement}的层级结构中的所有{@link AnnotatedElement},
+ * 并将其包装为{@link MetaAnnotatedElement}。 注解搜索范围
+ * 在当前实例中,针对带有和不带declared关键字的方法定义如下:
+ * 扫描顺序
+ * 当{@link AnnotatedElement}具有层级结构式,会按照广度优先扫描其本身(元素是{@link Class})、
+ * 或其声明类(元素是{@link Method})的层级结构。 {@link AnnotatedElement}工具类,提供对层级结构中{@link AnnotatedElement}上注解及元注解的访问支持,
+ * 并提供诸如基于{@link Alias}的属性别名、基于父子注解间的属性值覆盖等特殊的属性映射机制支持。
+ *
+ * 搜索层级结构
+ * 参考 Spring 中 搜索元注解
+ * 工具类支持搜索注解的元注解。在所有格式为getXXX或findXXX的静态方法中,
+ * 若不带有directly关键字,则该方法支持搜索元注解,否则皆支持搜索元注解。 注解属性映射
+ * 工具类支持注解对象属性上的一些属性映射机制,即当注解被扫描时,
+ * 将根据一些属性映射机制“解析”为其他类型的属性,这里支持的机制包括:
+ * 扫描{@code element}所处层级结构中的{@link AnnotatedElement},
+ * 并将其全部转为{@link MetaAnnotatedElement}后,
+ * 再把所有对象合并为{@link HierarchicalAnnotatedElements}。 扫描{@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
+ * 该功能参考{@link AnnotatedElementUtil}。
*
* @author Looly
* @since 5.1.1
diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationMapping.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationMapping.java
new file mode 100644
index 000000000..c177629f6
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationMapping.java
@@ -0,0 +1,89 @@
+package cn.hutool.core.annotation;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+
+/**
+ * 用于增强注解的包装器
+ *
+ * @param
+ *
+ * 当{@link #isResolved()}为{@code false}时,则该方法应当被包装的原始注解对象,
+ * 即返回值应当与{@link #getAnnotation()}相同。
+ *
+ * @return 所需的注解,若{@link #isResolved()}为{@code false}则返回的是原始的注解对象
+ */
+ T getResolvedAnnotation();
+
+ /**
+ * 获取注解类型
+ *
+ * @return 注解类型
+ */
+ @Override
+ default Class extends Annotation> annotationType() {
+ return getAnnotation().annotationType();
+ }
+
+ /**
+ * 当前注解是否存在被解析的属性,当该值为{@code false}时,
+ * 通过{@code getResolvedAttributeValue}获得的值皆为注解的原始属性值,
+ * 通过{@link #getResolvedAnnotation()}获得注解对象为原始的注解对象。
+ *
+ * @return 是否
+ */
+ boolean isResolved();
+
+ /**
+ * 获取注解原始属性
+ *
+ * @return 注解属性
+ */
+ Method[] getAttributes();
+
+ /**
+ * 获取属性值
+ *
+ * @param attributeName 属性名称
+ * @param attributeType 属性类型
+ * @param
@@ -295,4 +293,50 @@ public class AnnotationUtil {
final T annotation = getAnnotation(annotationEle, annotationType);
return (T) Proxy.newProxyInstance(annotationType.getClassLoader(), new Class[]{annotationType}, new AnnotationProxy<>(annotation));
}
+
+ /**
+ * 获取注解属性
+ *
+ * @param annotationType 注解类型
+ * @return 注解属性
+ * @see 6.0.0
+ */
+ public static Method[] getAnnotationAttributes(final Class extends Annotation> annotationType) {
+ // TODO 改为通过带缓存的反射工具类完成
+ Objects.requireNonNull(annotationType);
+ return Stream.of(annotationType.getDeclaredMethods())
+ .filter(AnnotationUtil::isAnnotationAttribute)
+ .toArray(Method[]::new);
+ }
+
+ /**
+ * 该方法是否是注解属性,需要满足下述条件:
+ *
+ *
+ *
+ * @param attribute 方法对象
+ * @return 是否
+ * @see 6.0.0
+ */
+ public static boolean isAnnotationAttribute(final Method attribute) {
+ return !MethodUtil.isEqualsMethod(attribute)
+ && !MethodUtil.isHashCodeMethod(attribute)
+ && !MethodUtil.isToStringMethod(attribute)
+ && ArrayUtil.isEmpty(attribute.getParameterTypes())
+ && ObjUtil.notEquals(attribute.getReturnType(), Void.class)
+ && !Modifier.isStatic(attribute.getModifiers())
+ && Modifier.isPublic(attribute.getModifiers())
+ && !attribute.isBridge()
+ && !attribute.isSynthetic();
+ }
+
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/GenericAnnotationMapping.java b/hutool-core/src/main/java/cn/hutool/core/annotation/GenericAnnotationMapping.java
new file mode 100644
index 000000000..1d35b024c
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/annotation/GenericAnnotationMapping.java
@@ -0,0 +1,156 @@
+package cn.hutool.core.annotation;
+
+import cn.hutool.core.reflect.ClassUtil;
+import cn.hutool.core.reflect.MethodUtil;
+import cn.hutool.core.text.CharSequenceUtil;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.Objects;
+import java.util.stream.Stream;
+
+/**
+ * {@link AnnotationMapping}的基本实现,仅仅是简单包装了注解对象
+ *
+ * @author huangchengxing
+ * @since 6.0.0
+ */
+public class GenericAnnotationMapping implements AnnotationMapping
+ * 当{@link #source}为{@code null}时,认为当前注解即为根注解。
+ *
+ *
+ * eg: 若注解存在{@code a <=> b <=> c}的属性别名关系,则对a赋值,此时b、c也会被一并赋值。
+ *
+ *
+ * 属性覆写遵循如下机制:
+ *
+ *
+ *
+ * @author huangchengxing
+ * @see MetaAnnotatedElement
+ * @since 6.0.0
+ */
+public class ResolvedAnnotationMapping implements AnnotationMapping
+ * eg: 若注解存在{@code a <=> b <=> c}的属性别名关系,则覆写a,,属性b、c也会被覆写;
+ *
+ * eg:若从根注解a到元注解b有依赖关系{@code a => b => c},
+ * 此时若c中存在属性可同时被a、b覆写,则优先选择a;
+ *
+ * eg:若从根注解a到元注解b有依赖关系{@code a => b => c},
+ * 此时若b中存在属性被a覆写,而b中被a覆写的属性又覆写c中属性,
+ * 则等同于c中被覆写的属性直接被a覆写。
+ *
+ *
+ */
+ private final int[] resolvedAttributes;
+
+ /**
+ * 解析后的属性对应的数据源
+ * 当属性被覆写时,该属性对应下标位置会指向覆写该属性的注解对象
+ */
+ private final ResolvedAnnotationMapping[] resolvedAttributeSources;
+
+ /**
+ * 子注解的映射对象,当该项为{@code null}时,则认为当前注解为根注解
+ */
+ private final ResolvedAnnotationMapping source;
+
+ /**
+ * 注解属性
+ */
+ private final Annotation annotation;
+
+ /**
+ * 代理对象缓存
+ */
+ private volatile Annotation proxied;
+
+ /**
+ * 该注解的属性是否发生了解析
+ */
+ private final boolean resolved;
+
+ /**
+ * 构建一个注解映射对象
+ *
+ * @param annotation 注解对象
+ * @param resolveAnnotationAttribute 是否解析注解属性,为{@code true}时获得的注解皆支持属性覆盖与属性别名机制
+ * @return 注解映射对象
+ */
+ public static ResolvedAnnotationMapping create(final Annotation annotation, final boolean resolveAnnotationAttribute) {
+ return create(null, annotation, resolveAnnotationAttribute);
+ }
+
+ /**
+ * 构建一个注解映射对象,子注解及子注解的子注解们的属性会覆写注解对象的中的同名同名同类型属性,
+ * 当一个属性被多个子注解覆写时,优先选择离根注解最接近的注解中的属性用于覆写,
+ *
+ * @param source 子注解
+ * @param annotation 注解对象
+ * @param resolveAnnotationAttribute 是否解析注解属性,为{@code true}时获得的注解皆支持属性覆盖与属性别名机制
+ * @return 注解映射对象
+ */
+ public static ResolvedAnnotationMapping create(
+ final ResolvedAnnotationMapping source, final Annotation annotation, final boolean resolveAnnotationAttribute) {
+ return new ResolvedAnnotationMapping(source, annotation, resolveAnnotationAttribute);
+ }
+
+ /**
+ * 构建一个注解映射对象
+ *
+ * @param source 当前注解的子注解
+ * @param annotation 注解对象
+ * @param resolveAttribute 是否需要解析属性
+ * @throws NullPointerException {@code source}为{@code null}时抛出
+ * @throws IllegalArgumentException
+ *
+ *
+ */
+ ResolvedAnnotationMapping(final ResolvedAnnotationMapping source, final Annotation annotation, boolean resolveAttribute) {
+ Objects.requireNonNull(annotation);
+ Assert.isFalse(AnnotationMappingProxy.isProxied(annotation), "annotation has been proxied");
+ Assert.isFalse(annotation instanceof ResolvedAnnotationMapping, "annotation has been wrapped");
+ Assert.isFalse(
+ Objects.nonNull(source) && Objects.equals(source.annotation, annotation),
+ "source annotation can not same with target [{}]", annotation
+ );
+ this.annotation = annotation;
+ this.attributes = AnnotationUtil.getAnnotationAttributes(annotation.annotationType());
+ this.source = source;
+
+ // 别名属性
+ this.aliasSets = new AliasSet[this.attributes.length];
+
+ // 解析后的属性与数据源
+ this.resolvedAttributeSources = new ResolvedAnnotationMapping[this.attributes.length];
+ this.resolvedAttributes = new int[this.attributes.length];
+ Arrays.fill(this.resolvedAttributes, NOT_FOUND_INDEX);
+
+ // 若有必要,解析属性
+ // TODO 可能的改进:flag改为枚举,使得可以自行选择:1.只支持属性别名,2.只支持属性覆盖,3.两个都支持,4.两个都不支持
+ this.resolved = resolveAttribute && resolveAttributes();
+ }
+
+ /**
+ * 解析属性
+ */
+ private boolean resolveAttributes() {
+ // 解析同一注解中的别名
+ resolveAliasAttributes();
+ // 使用子注解覆写当前注解中的属性
+ resolveOverwriteAttributes();
+ // 注解的属性是否发生过解析
+ return IntStream.of(resolvedAttributes)
+ .anyMatch(idx -> NOT_FOUND_INDEX != idx);
+ }
+
+ // ================== 通用 ==================
+
+ /**
+ * 当前注解是否为根注解
+ *
+ * @return 是否
+ */
+ @Override
+ public boolean isRoot() {
+ return Objects.isNull(source);
+ }
+
+ /**
+ * 获取根注解
+ *
+ * @return 根注解的映射对象
+ */
+ public ResolvedAnnotationMapping getRoot() {
+ ResolvedAnnotationMapping mapping = this;
+ while (Objects.nonNull(mapping.source)) {
+ mapping = mapping.source;
+ }
+ return mapping;
+ }
+
+ /**
+ * 获取注解属性
+ *
+ * @return 注解属性
+ */
+ @Override
+ public Method[] getAttributes() {
+ return attributes;
+ }
+
+ /**
+ * 获取注解对象
+ *
+ * @return 注解对象
+ */
+ @Override
+ public Annotation getAnnotation() {
+ return annotation;
+ }
+
+ /**
+ * 当前注解是否存在被解析的属性,当该值为{@code false}时,
+ * 通过{@code getResolvedAttributeValue}获得的值皆为注解的原始属性值,
+ * 通过{@link #getResolvedAnnotation()}获得注解对象为原始的注解对象。
+ *
+ * @return 是否
+ */
+ @Override
+ public boolean isResolved() {
+ return resolved;
+ }
+
+ /**
+ * 根据当前映射对象,通过动态代理生成一个类型与被包装注解对象一致的合成注解,该注解相对原生注解:
+ *
+ *
+ * 当{@link #isResolved()}为{@code false}时,则该方法返回被包装的原始注解对象。
+ *
+ * @return 所需的注解,若{@link ResolvedAnnotationMapping#isResolved()}为{@code false}则返回的是原始的注解对象
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public Annotation getResolvedAnnotation() {
+ if (!isResolved()) {
+ return annotation;
+ }
+ // 双重检查保证线程安全的创建代理缓存
+ if (Objects.isNull(proxied)) {
+ synchronized (this) {
+ if (Objects.isNull(proxied)) {
+ proxied = AnnotationMappingProxy.create(annotationType(), this);
+ }
+ }
+ }
+ return proxied;
+ }
+
+ // ================== 属性搜索 ==================
+
+ /**
+ * 注解是否存在指定属性
+ *
+ * @param attributeName 属性名称
+ * @param attributeType 属性类型
+ * @return 是否
+ */
+ public boolean hasAttribute(final String attributeName, final Class> attributeType) {
+ return getAttributeIndex(attributeName, attributeType) != NOT_FOUND_INDEX;
+ }
+
+ /**
+ * 该属性下标是否在注解中存在对应属性
+ *
+ * @param index 属性下标
+ * @return 是否
+ */
+ public boolean hasAttribute(final int index) {
+ return index != NOT_FOUND_INDEX
+ && Objects.nonNull(ArrayUtil.get(attributes, index));
+ }
+
+ /**
+ * 获取注解属性的下标
+ *
+ * @param attributeName 属性名称
+ * @param attributeType 属性类型
+ * @return 属性下标
+ */
+ public int getAttributeIndex(final String attributeName, final Class> attributeType) {
+ for (int i = 0; i < attributes.length; i++) {
+ final Method attribute = attributes[i];
+ if (CharSequenceUtil.equals(attribute.getName(), attributeName)
+ && ClassUtil.isAssignable(attributeType, attribute.getReturnType())) {
+ return i;
+ }
+ }
+ return NOT_FOUND_INDEX;
+ }
+
+ /**
+ * 根据下标获取注解属性
+ *
+ * @param index 属性下标
+ * @return 属性对象
+ */
+ public Method getAttribute(final int index) {
+ return ArrayUtil.get(attributes, index);
+ }
+
+ // ================== 属性取值 ==================
+
+ /**
+ * 获取属性值
+ *
+ * @param attributeName 属性名称
+ * @param attributeType 属性类型
+ * @param
+ *
+ */
+ private int resolve() {
+ int resolvedIndex = NOT_FOUND_INDEX;
+ boolean hasNotDef = false;
+ Object lastValue = null;
+ for (final int index : indexes) {
+ final Method attribute = attributes[index];
+
+ // 获取属性的值,并确认是否为默认值
+ final Object def = attribute.getDefaultValue();
+ final Object undef = MethodUtil.invoke(annotation, attribute);
+ final boolean isDefault = Objects.equals(def, undef);
+
+ // 若是首个属性
+ if (resolvedIndex == NOT_FOUND_INDEX) {
+ resolvedIndex = index;
+ lastValue = isDefault ? def : undef;
+ hasNotDef = !isDefault;
+ continue;
+ }
+
+ // 不是首个属性,且已存在非默认值
+ if (hasNotDef) {
+ // 如果当前也是非默认值,则要求两值必须相等
+ if (!isDefault) {
+ Assert.isTrue(
+ Objects.equals(lastValue, undef),
+ "aliased attribute [{}] and [{}] must have same not default value, but is different: [{}] <==> [{}]",
+ attributes[resolvedIndex], attribute, lastValue, undef
+ );
+ }
+ // 否则直接跳过,依然以上一非默认值为准
+ continue;
+ }
+
+ // 不是首个属性,但是还没有非默认值,而当前值恰好是非默认值,直接更新当前有效值与对应索引
+ if (!isDefault) {
+ hasNotDef = true;
+ lastValue = undef;
+ resolvedIndex = index;
+ continue;
+ }
+
+ // 不是首个属性,还没有非默认值,如果当前也是默认值,则要求两值必须相等
+ Assert.isTrue(
+ Objects.equals(lastValue, def),
+ "aliased attribute [{}] and [{}] must have same default value, but is different: [{}] <==> [{}]",
+ attributes[resolvedIndex], attribute, lastValue, def
+ );
+ }
+ Assert.isFalse(resolvedIndex == NOT_FOUND_INDEX, "can not resolve aliased attributes from [{}]", annotation);
+ return resolvedIndex;
+ }
+
+ /**
+ * 遍历下标
+ */
+ void forEach(IntConsumer consumer) {
+ for (int index : indexes) {
+ consumer.accept(index);
+ }
+ }
+
+ }
+}
diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/GenericAnnotationMappingTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/GenericAnnotationMappingTest.java
new file mode 100644
index 000000000..011cf1419
--- /dev/null
+++ b/hutool-core/src/test/java/cn/hutool/core/annotation/GenericAnnotationMappingTest.java
@@ -0,0 +1,120 @@
+package cn.hutool.core.annotation;
+
+import lombok.SneakyThrows;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.Method;
+
+/**
+ * test for {@link GenericAnnotationMapping}
+ *
+ * @author huangchengxing
+ */
+public class GenericAnnotationMappingTest {
+
+ @Test
+ public void testEquals() {
+ Annotation1 annotation = Foo.class.getAnnotation(Annotation1.class);
+ GenericAnnotationMapping mapping = GenericAnnotationMapping.create(annotation, false);
+ Assert.assertEquals(mapping, mapping);
+ Assert.assertNotEquals(mapping, null);
+ Assert.assertEquals(mapping, GenericAnnotationMapping.create(annotation, false));
+ Assert.assertNotEquals(mapping, GenericAnnotationMapping.create(annotation, true));
+ }
+
+ @Test
+ public void testHashCode() {
+ Annotation1 annotation = Foo.class.getAnnotation(Annotation1.class);
+ int hashCode = GenericAnnotationMapping.create(annotation, false).hashCode();
+ Assert.assertEquals(hashCode, GenericAnnotationMapping.create(annotation, false).hashCode());
+ Assert.assertNotEquals(hashCode, GenericAnnotationMapping.create(annotation, true).hashCode());
+ }
+
+
+ @Test
+ public void testCreate() {
+ Annotation1 annotation = Foo.class.getAnnotation(Annotation1.class);
+ GenericAnnotationMapping mapping = GenericAnnotationMapping.create(annotation, false);
+ Assert.assertNotNull(mapping);
+ }
+
+ @Test
+ public void testIsRoot() {
+ Annotation1 annotation = Foo.class.getAnnotation(Annotation1.class);
+ GenericAnnotationMapping mapping = GenericAnnotationMapping.create(annotation, true);
+ Assert.assertTrue(mapping.isRoot());
+
+ mapping = GenericAnnotationMapping.create(annotation, false);
+ Assert.assertFalse(mapping.isRoot());
+ }
+
+ @Test
+ public void testGetAnnotation() {
+ Annotation1 annotation = Foo.class.getAnnotation(Annotation1.class);
+ GenericAnnotationMapping mapping = GenericAnnotationMapping.create(annotation, false);
+ Assert.assertSame(annotation, mapping.getAnnotation());
+ }
+
+ @SneakyThrows
+ @Test
+ public void testGetAttributes() {
+ Annotation1 annotation = Foo.class.getAnnotation(Annotation1.class);
+ GenericAnnotationMapping mapping = GenericAnnotationMapping.create(annotation, false);
+ for (int i = 0; i < mapping.getAttributes().length; i++) {
+ Method method = mapping.getAttributes()[i];
+ Assert.assertEquals(Annotation1.class.getDeclaredMethod(method.getName()), method);
+ }
+ }
+
+ @Test
+ public void testAnnotationType() {
+ Annotation1 annotation = Foo.class.getAnnotation(Annotation1.class);
+ GenericAnnotationMapping mapping = GenericAnnotationMapping.create(annotation, false);
+ Assert.assertEquals(annotation.annotationType(), mapping.annotationType());
+ }
+
+ @Test
+ public void testIsResolved() {
+ Annotation1 annotation = Foo.class.getAnnotation(Annotation1.class);
+ GenericAnnotationMapping mapping = GenericAnnotationMapping.create(annotation, false);
+ Assert.assertFalse(mapping.isResolved());
+ }
+
+ @Test
+ public void testGetAttributeValue() {
+ Annotation1 annotation = Foo.class.getAnnotation(Annotation1.class);
+ GenericAnnotationMapping mapping = GenericAnnotationMapping.create(annotation, false);
+ Assert.assertEquals(annotation.value(), mapping.getAttributeValue("value", String.class));
+ Assert.assertNull(mapping.getAttributeValue("value", Integer.class));
+ }
+
+ @Test
+ public void testGetResolvedAnnotation() {
+ Annotation1 annotation = Foo.class.getAnnotation(Annotation1.class);
+ GenericAnnotationMapping mapping = GenericAnnotationMapping.create(annotation, false);
+ Assert.assertSame(annotation, mapping.getResolvedAnnotation());
+ }
+
+ @Test
+ public void testGetResolvedAttributeValue() {
+ Annotation1 annotation = Foo.class.getAnnotation(Annotation1.class);
+ GenericAnnotationMapping mapping = GenericAnnotationMapping.create(annotation, false);
+ Assert.assertEquals(annotation.value(), mapping.getResolvedAttributeValue("value", String.class));
+ Assert.assertNull(mapping.getResolvedAttributeValue("value", Integer.class));
+ }
+
+ @Target(ElementType.TYPE_USE)
+ @Retention(RetentionPolicy.RUNTIME)
+ private @interface Annotation1 {
+ String value() default "";
+ }
+
+ @Annotation1("foo")
+ private static class Foo {};
+
+}
diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/ResolvedAnnotationMappingTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/ResolvedAnnotationMappingTest.java
new file mode 100644
index 000000000..adb56f25e
--- /dev/null
+++ b/hutool-core/src/test/java/cn/hutool/core/annotation/ResolvedAnnotationMappingTest.java
@@ -0,0 +1,300 @@
+package cn.hutool.core.annotation;
+
+import lombok.SneakyThrows;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.Method;
+
+/**
+ * test for {@link ResolvedAnnotationMapping}
+ *
+ * @author huangchengxing
+ */
+public class ResolvedAnnotationMappingTest {
+
+ @Test
+ public void testEquals() {
+ Annotation1 annotation = Foo.class.getAnnotation(Annotation1.class);
+ ResolvedAnnotationMapping mapping = ResolvedAnnotationMapping.create(annotation, false);
+ Assert.assertEquals(mapping, mapping);
+ Assert.assertNotEquals(null, mapping);
+ Assert.assertEquals(mapping, ResolvedAnnotationMapping.create(annotation, false));
+ Assert.assertNotEquals(mapping, ResolvedAnnotationMapping.create(annotation, true));
+
+ // Annotation3没有需要解析的属性,因此即使在构造函数指定false也一样
+ Annotation3 annotation3 = Foo.class.getAnnotation(Annotation3.class);
+ Assert.assertEquals(
+ ResolvedAnnotationMapping.create(annotation3, false),
+ ResolvedAnnotationMapping.create(annotation3, true)
+ );
+ }
+
+ @Test
+ public void testHashCode() {
+ Annotation1 annotation = Foo.class.getAnnotation(Annotation1.class);
+ int hashCode = ResolvedAnnotationMapping.create(annotation, false).hashCode();
+ Assert.assertEquals(hashCode, ResolvedAnnotationMapping.create(annotation, false).hashCode());
+ Assert.assertNotEquals(hashCode, ResolvedAnnotationMapping.create(annotation, true).hashCode());
+
+ // Annotation3没有需要解析的属性,因此即使在构造函数指定false也一样
+ Annotation3 annotation3 = Foo.class.getAnnotation(Annotation3.class);
+ Assert.assertEquals(
+ ResolvedAnnotationMapping.create(annotation3, false).hashCode(),
+ ResolvedAnnotationMapping.create(annotation3, true).hashCode()
+ );
+ }
+
+
+ @Test
+ public void testCreate() {
+ Annotation3 annotation3 = Foo.class.getAnnotation(Annotation3.class);
+ ResolvedAnnotationMapping mapping3 = ResolvedAnnotationMapping.create(annotation3, false);
+ Assert.assertNotNull(mapping3);
+
+ Annotation2 annotation2 = Foo.class.getAnnotation(Annotation2.class);
+ ResolvedAnnotationMapping mapping2 = ResolvedAnnotationMapping.create(mapping3, annotation2, false);
+ Assert.assertNotNull(mapping2);
+
+ Annotation1 annotation1 = Foo.class.getAnnotation(Annotation1.class);
+ ResolvedAnnotationMapping mapping1 = ResolvedAnnotationMapping.create(mapping2, annotation1, false);
+ Assert.assertNotNull(mapping1);
+ }
+
+ @Test
+ public void testIsRoot() {
+ Annotation3 annotation3 = Foo.class.getAnnotation(Annotation3.class);
+ ResolvedAnnotationMapping mapping3 = ResolvedAnnotationMapping.create(annotation3, false);
+ Assert.assertTrue(mapping3.isRoot());
+
+ Annotation2 annotation2 = Foo.class.getAnnotation(Annotation2.class);
+ ResolvedAnnotationMapping mapping2 = ResolvedAnnotationMapping.create(mapping3, annotation2, false);
+ Assert.assertFalse(mapping2.isRoot());
+ }
+
+ @Test
+ public void testGetRoot() {
+ Annotation3 annotation3 = Foo.class.getAnnotation(Annotation3.class);
+ ResolvedAnnotationMapping mapping3 = ResolvedAnnotationMapping.create(annotation3, false);
+ Assert.assertSame(mapping3, mapping3.getRoot());
+
+ Annotation2 annotation2 = Foo.class.getAnnotation(Annotation2.class);
+ ResolvedAnnotationMapping mapping2 = ResolvedAnnotationMapping.create(mapping3, annotation2, false);
+ Assert.assertSame(mapping3, mapping2.getRoot());
+
+ Annotation1 annotation1 = Foo.class.getAnnotation(Annotation1.class);
+ ResolvedAnnotationMapping mapping1 = ResolvedAnnotationMapping.create(mapping2, annotation1, false);
+ Assert.assertSame(mapping3, mapping1.getRoot());
+ }
+
+ @Test
+ public void testGetAnnotation() {
+ Annotation1 annotation = Foo.class.getAnnotation(Annotation1.class);
+ ResolvedAnnotationMapping mapping = ResolvedAnnotationMapping.create(annotation, false);
+ Assert.assertSame(annotation, mapping.getAnnotation());
+ }
+
+ @SneakyThrows
+ @Test
+ public void testGetAttributes() {
+ Annotation1 annotation = Foo.class.getAnnotation(Annotation1.class);
+ ResolvedAnnotationMapping mapping = ResolvedAnnotationMapping.create(annotation, false);
+ for (int i = 0; i < mapping.getAttributes().length; i++) {
+ Method method = mapping.getAttributes()[i];
+ Assert.assertEquals(Annotation1.class.getDeclaredMethod(method.getName()), method);
+ }
+ }
+
+ @Test
+ public void testHasAttribute() {
+ Annotation1 annotation = Foo.class.getAnnotation(Annotation1.class);
+ ResolvedAnnotationMapping mapping = ResolvedAnnotationMapping.create(annotation, false);
+
+ Assert.assertTrue(mapping.hasAttribute("value", String.class));
+ Assert.assertFalse(mapping.hasAttribute("value", Integer.class));
+
+ int index = mapping.getAttributeIndex("value", String.class);
+ Assert.assertTrue(mapping.hasAttribute(index));
+ Assert.assertFalse(mapping.hasAttribute(Integer.MIN_VALUE));
+ }
+
+ @Test
+ public void testAnnotationType() {
+ Annotation1 annotation = Foo.class.getAnnotation(Annotation1.class);
+ ResolvedAnnotationMapping mapping = ResolvedAnnotationMapping.create(annotation, false);
+ Assert.assertEquals(annotation.annotationType(), mapping.annotationType());
+ }
+
+ @Test
+ public void testIsResolved() {
+ Annotation1 annotation1 = Foo.class.getAnnotation(Annotation1.class);
+
+ ResolvedAnnotationMapping mapping1 = ResolvedAnnotationMapping.create(annotation1, true);
+ Assert.assertTrue(mapping1.isResolved());
+ Assert.assertFalse(ResolvedAnnotationMapping.create(annotation1, false).isResolved());
+
+ Annotation2 annotation2 = Foo.class.getAnnotation(Annotation2.class);
+ ResolvedAnnotationMapping mapping2 = ResolvedAnnotationMapping.create(annotation2, true);
+ Assert.assertFalse(mapping2.isResolved());
+
+ mapping2 = ResolvedAnnotationMapping.create(mapping1, annotation2, true);
+ Assert.assertTrue(mapping2.isResolved());
+ }
+
+ @Test
+ public void testGetAttributeIndex() {
+ Annotation1 annotation = Foo.class.getAnnotation(Annotation1.class);
+ ResolvedAnnotationMapping mapping = ResolvedAnnotationMapping.create(annotation, false);
+ for (int i = 0; i < mapping.getAttributes().length; i++) {
+ Method method = mapping.getAttributes()[i];
+ Assert.assertEquals(i, mapping.getAttributeIndex(method.getName(), method.getReturnType()));
+ }
+ Assert.assertEquals(ResolvedAnnotationMapping.NOT_FOUND_INDEX, mapping.getAttributeIndex("value", Void.class));
+ Assert.assertEquals(ResolvedAnnotationMapping.NOT_FOUND_INDEX, mapping.getAttributeIndex("nonexistent", Void.class));
+ }
+
+ @Test
+ public void testGetAttributeValue() {
+ Annotation1 annotation = Foo.class.getAnnotation(Annotation1.class);
+ ResolvedAnnotationMapping mapping = ResolvedAnnotationMapping.create(annotation, false);
+
+ Assert.assertNull(mapping.getAttribute(Integer.MAX_VALUE));
+
+ int valueIdx = mapping.getAttributeIndex("value", String.class);
+ Assert.assertEquals(annotation.value(), mapping.getAttributeValue(valueIdx));
+ Assert.assertEquals(annotation.value(), mapping.getAttributeValue("value", String.class));
+
+ int name1Idx = mapping.getAttributeIndex("value1", String.class);
+ Assert.assertEquals(annotation.value1(), mapping.getAttributeValue(name1Idx));
+ Assert.assertEquals(annotation.value1(), mapping.getAttributeValue("value1", String.class));
+
+ int name2Idx = mapping.getAttributeIndex("value2", String.class);
+ Assert.assertEquals(annotation.value2(), mapping.getAttributeValue(name2Idx));
+ Assert.assertEquals(annotation.value2(), mapping.getAttributeValue("value2", String.class));
+ }
+
+ @Test
+ public void testGetResolvedAnnotation() {
+ Annotation1 annotation = Foo.class.getAnnotation(Annotation1.class);
+ ResolvedAnnotationMapping mapping = ResolvedAnnotationMapping.create(annotation, true);
+ Annotation1 synthesis = (Annotation1)mapping.getResolvedAnnotation();
+
+ Assert.assertEquals(annotation.annotationType(), synthesis.annotationType());
+ Assert.assertEquals(annotation.value(), synthesis.value());
+ Assert.assertEquals(annotation.value(), synthesis.value1());
+ Assert.assertEquals(annotation.value(), synthesis.value2());
+
+ Assert.assertTrue(AnnotationMappingProxy.isProxied(synthesis));
+ Assert.assertSame(mapping, ((AnnotationMappingProxy.Proxied)synthesis).getMapping());
+
+ Assert.assertNotEquals(synthesis, annotation);
+ Assert.assertNotEquals(synthesis.hashCode(), annotation.hashCode());
+ Assert.assertNotEquals(synthesis.toString(), annotation.toString());
+
+ Annotation3 annotation3 = Foo.class.getAnnotation(Annotation3.class);
+ Assert.assertSame(annotation3, ResolvedAnnotationMapping.create(annotation3, true).getResolvedAnnotation());
+ }
+
+ // ======================= resolved attribute value =======================
+
+ @Test
+ public void testGetResolvedAttributeValueWhenAliased() {
+ Annotation1 annotation = Foo.class.getAnnotation(Annotation1.class);
+ ResolvedAnnotationMapping mapping = ResolvedAnnotationMapping.create(annotation, true);
+ Assert.assertNull(mapping.getResolvedAttributeValue(Integer.MIN_VALUE));
+
+ // value = value1 = value2
+ Assert.assertEquals(annotation.value(), mapping.getResolvedAttributeValue("value", String.class));
+ Assert.assertEquals(annotation.value(), mapping.getResolvedAttributeValue("value1", String.class));
+ Assert.assertEquals(annotation.value(), mapping.getResolvedAttributeValue("value2", String.class));
+
+ // alias == alias1 == alias2
+ Assert.assertEquals(annotation.alias(), mapping.getResolvedAttributeValue("alias", String.class));
+ Assert.assertEquals(annotation.alias(), mapping.getResolvedAttributeValue("alias1", String.class));
+ Assert.assertEquals(annotation.alias(), mapping.getResolvedAttributeValue("alias2", String.class));
+
+ // defVal1 == defVal2
+ Assert.assertEquals(
+ mapping.getResolvedAttributeValue("defVal", String.class),
+ mapping.getResolvedAttributeValue("defVal2", String.class)
+ );
+
+ // unDefVal1 == unDefVal2
+ Assert.assertEquals(
+ mapping.getResolvedAttributeValue("unDefVal", String.class),
+ mapping.getResolvedAttributeValue("unDefVal2", String.class)
+ );
+ }
+
+ @Test
+ public void testGetResolvedAttributeWhenOverwritten() {
+ Annotation3 annotation3 = Foo.class.getAnnotation(Annotation3.class);
+ ResolvedAnnotationMapping mapping3 = ResolvedAnnotationMapping.create(annotation3, true);
+ Assert.assertEquals(annotation3.value(), mapping3.getResolvedAttributeValue("value", String.class));
+ Assert.assertEquals((Integer)annotation3.alias(), mapping3.getResolvedAttributeValue("alias", Integer.class));
+
+ // annotation2中与annotation3同名同类型的属性value、alias被覆写
+ Annotation2 annotation2 = Foo.class.getAnnotation(Annotation2.class);
+ ResolvedAnnotationMapping mapping2 = ResolvedAnnotationMapping.create(mapping3, annotation2, true);
+ Assert.assertEquals(annotation3.value(), mapping2.getResolvedAttributeValue("value", String.class));
+ Assert.assertEquals((Integer)annotation3.alias(), mapping2.getResolvedAttributeValue("alias", Integer.class));
+
+ // annotation1中与annotation3同名同类型的属性value被覆写,由于value存在别名value1,value2因此也一并被覆写
+ Annotation1 annotation1 = Foo.class.getAnnotation(Annotation1.class);
+ ResolvedAnnotationMapping mapping1 = ResolvedAnnotationMapping.create(mapping2, annotation1, true);
+ Assert.assertEquals(annotation3.value(), mapping1.getResolvedAttributeValue("value", String.class));
+ Assert.assertEquals(annotation3.value(), mapping1.getResolvedAttributeValue("value1", String.class));
+ Assert.assertEquals(annotation3.value(), mapping1.getResolvedAttributeValue("value2", String.class));
+ // 而alias由于类型不同不会被覆写
+ Assert.assertEquals(annotation1.alias(), mapping1.getResolvedAttributeValue("alias", String.class));
+ }
+
+ @Target(ElementType.TYPE_USE)
+ @Retention(RetentionPolicy.RUNTIME)
+ private @interface Annotation1 {
+ @Alias("value1")
+ String value() default "";
+ String value1() default "";
+ @Alias("value")
+ String value2() default "";
+
+ @Alias("alias2")
+ String alias() default "";
+ @Alias("alias2")
+ String alias1() default "";
+ @Alias("alias1")
+ String alias2() default "";
+
+ @Alias("defVal2")
+ String defVal() default "";
+ String defVal2() default "";
+
+ @Alias("unDefVal2")
+ String unDefVal() default "";
+ String unDefVal2() default "";
+ }
+
+ @Target(ElementType.TYPE_USE)
+ @Retention(RetentionPolicy.RUNTIME)
+ private @interface Annotation2 {
+ String value() default "";
+ int alias() default 123;
+ }
+
+ @Target(ElementType.TYPE_USE)
+ @Retention(RetentionPolicy.RUNTIME)
+ private @interface Annotation3 {
+ String value() default "";
+ int alias() default 123;
+ }
+
+ @Annotation3(value = "Annotation3", alias = 312)
+ @Annotation2(value = "Annotation2")
+ @Annotation1(value = "Annotation1", alias = "goo", unDefVal = "foo", unDefVal2 = "foo")
+ private static class Foo {};
+
+}
From 77e065f302480d56fcc63f0917c411d8d2a4a4ad Mon Sep 17 00:00:00 2001
From: huangchengxing <841396397@qq.com>
Date: Tue, 13 Sep 2022 13:57:46 +0800
Subject: [PATCH 2/4] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=94=AF=E6=8C=81?=
=?UTF-8?q?=E5=A4=84=E7=90=86=E5=85=83=E6=B3=A8=E8=A7=A3=E7=9A=84=E5=A2=9E?=
=?UTF-8?q?=E5=BC=BAAnnotatedElement=E5=8C=85=E8=A3=85=E5=99=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../core/annotation/MetaAnnotatedElement.java | 322 ++++++++++++++++++
.../annotation/MetaAnnotatedElementTest.java | 208 +++++++++++
2 files changed, 530 insertions(+)
create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/MetaAnnotatedElement.java
create mode 100644 hutool-core/src/test/java/cn/hutool/core/annotation/MetaAnnotatedElementTest.java
diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/MetaAnnotatedElement.java b/hutool-core/src/main/java/cn/hutool/core/annotation/MetaAnnotatedElement.java
new file mode 100644
index 000000000..26e0567cc
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/annotation/MetaAnnotatedElement.java
@@ -0,0 +1,322 @@
+package cn.hutool.core.annotation;
+
+import cn.hutool.core.stream.EasyStream;
+import cn.hutool.core.text.CharSequenceUtil;
+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.*;
+import java.util.function.BiFunction;
+import java.util.stream.Stream;
+
+/**
+ *
+ *
+ *
+ * 默认情况下,已经处理过、或在{@link java.lang}包下的注解不会被处理
+ *
+ * @param mappings 当前已处理的注解
+ * @param annotation 注解对象
+ * @return 是否
+ */
+ protected boolean isNeedMapping(final Map
+ * eg:
+ * 若存在元素A有对应父类与父接口B,C,
+ * 则根据A生成的{@link HierarchicalAnnotatedElements}实例将同时包含A,B,C,
+ * 该实例同时支持对这三个实例上直接声明的注解,以及这些注解的元注解进行访问。
+ *
+ *
+ *
+ *
+ *
+ *
+ * final
修饰的方法时,
+ * 则额外获取包括其声明类的所有父类和所有父接口中,与该方法具有相同方法签名的方法上的注解和元注解;
+ *
+ * 在该过程中,总是先扫描父类,再扫描父接口,
+ * 若存在多个父接口,则其扫描顺序遵循从{@link Class#getInterfaces()}获得该接口的顺序。
+ *
+ * @author huangchengxing
+ * @since 6.0.0
+ */
+public class HierarchicalAnnotatedElements implements AnnotatedElement, Iterable
+ * 该集合中的元素按照其与被包装的{@link AnnotatedElement}的距离和被按广度优先扫描的顺序排序
+ */
+ private volatile Set
+ *
+ */
+ private boolean isNeedMapping(Class> type, SetAnnotatedElementUtils
,
+ * 工具类提供get以及find两种语义的搜索:
+ *
+ *
+ * eg:
+ *
+ * 若类A分别有父类和父接口B、C,
+ * 则通过getXXX方法将只能获得A上的注解,
+ * 而通过getXXX方法将能获得A、B、C上的注解。
+ *
+ *
+ * eg:
+ * 若类A分别有父类和父接口B、C,上面分别有注解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
+ * eg:
+ *
+ *
+ * // set aliased attributes
+ * {@literal @}interface FooAnnotation {
+ * {@literal @}Alias("alias")
+ * default String value() default "";
+ * {@literal @}Alias("value")
+ * default String alias() default "";
+ * }
+ * {@literal @}FooAnnotation("foo")
+ * class Foo { }
+ *
+ * // get resolved annotation
+ * FooAnnotation annotation = getResolvedAnnotation(Foo.class, FooAnnotation.class);
+ * annotation.value(); // = "foo"
+ * annotation.alias(); // = "foo"
+ * }
+ * eg:
+ *
+ *
+ * {@literal @}interface Meta {
+ * default String value() default "";
+ * }
+ * {@literal @}Meta("meta")
+ * {@literal @}interface Root {
+ * default String value() default ""; // overwrite for @Meta.value
+ * }
+ * {@literal @}Root("foo")
+ * class Foo { }
+ *
+ * // get resolved annotation
+ * Meta meta = getResolvedAnnotation(Foo.class, Meta.class);
+ * meta.value(); // = "foo"
+ * Root root = getResolvedAnnotation(Foo.class, Root.class);
+ * root.value(); // = "foo"
+ *
+ * 得到的注解支持基于{@link Alias}的别名、及子注解对元注解中同名同类型属性进行覆写的特殊机制。
+ *
+ * @param element {@link AnnotatedElement}
+ * @param annotationType 注解类型
+ * @param
+ * 得到的注解支持基于{@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
+ * 得到的注解支持基于{@link Alias}的别名、及子注解对元注解中同名同类型属性进行覆写的特殊机制。
+ *
+ * @param element {@link AnnotatedElement}
+ * @param annotationType 注解类型
+ * @param
+ * 得到的注解支持基于{@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
+ * 得到的注解支持基于{@link Alias}的别名机制。
+ *
+ * @param element {@link AnnotatedElement}
+ * @param annotationType 注解类型
+ * @param
+ * 得到的注解支持基于{@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
+ * 得到的注解支持基于{@link Alias}的别名机制。
+ *
+ * @param element {@link AnnotatedElement}
+ * @param annotationType 注解类型
+ * @param
+ * 得到的注解支持基于{@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}上的注解及元注解。
+ *
+ * @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));
+ }
+
+ /**
+ *