diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AliasAttributePostProcessor.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AliasAttributePostProcessor.java new file mode 100644 index 000000000..f9888c505 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AliasAttributePostProcessor.java @@ -0,0 +1,63 @@ +package cn.hutool.core.annotation; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.lang.Opt; +import cn.hutool.core.map.ForestMap; +import cn.hutool.core.map.LinkedForestMap; +import cn.hutool.core.map.TreeEntry; +import cn.hutool.core.util.ClassUtil; +import cn.hutool.core.util.ObjectUtil; + +import java.util.Map; + +/** + * 用于处理合成注解属性中{@link Alias}的映射关系, + * 该处理器会令{@link Alias#value()}指向的属性值强制覆盖当前属性 + * + * @author huangchengxing + * @see ForceAliasedAnnotationAttribute + */ +public class AliasAttributePostProcessor implements SynthesizedAnnotationPostProcessor { + + @Override + public int order() { + return Integer.MIN_VALUE; + } + + @Override + public void process(SynthesizedAnnotation annotation, SyntheticAnnotation syntheticAnnotation) { + Map attributeMap = annotation.getAttributes(); + + // 记录别名与属性的关系 + ForestMap attributeAliasMappings = new LinkedForestMap<>(false); + attributeMap.forEach((attributeName, attribute) -> { + String alias = Opt.ofNullable(attribute.getAnnotation(Alias.class)) + .map(Alias::value) + .orElse(null); + if (ObjectUtil.isNull(alias)) { + return; + } + AnnotationAttribute aliasAttribute = attributeMap.get(alias); + Assert.notNull(aliasAttribute, "no method for alias: [{}]", alias); + attributeAliasMappings.putLinkedNodes(alias, aliasAttribute, attributeName, attribute); + }); + + // 处理别名 + attributeMap.forEach((attributeName, attribute) -> { + AnnotationAttribute resolvedAttributeMethod = Opt.ofNullable(attributeName) + .map(attributeAliasMappings::getRootNode) + .map(TreeEntry::getValue) + .map(aliasAttribute -> (AnnotationAttribute)new ForceAliasedAnnotationAttribute(attribute, aliasAttribute)) + .orElse(attribute); + Assert.isTrue( + ObjectUtil.isNull(resolvedAttributeMethod) + || ClassUtil.isAssignable(attribute.getAttribute().getReturnType(), resolvedAttributeMethod.getAttribute().getReturnType()), + "return type of the root alias method [{}] is inconsistent with the original [{}]", + resolvedAttributeMethod.getClass(), attribute.getAttribute().getReturnType() + ); + attributeMap.put(attributeName, resolvedAttributeMethod); + }); + annotation.setAttributes(attributeMap); + } + +} diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AliasForLinkAttributePostProcessor.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AliasForLinkAttributePostProcessor.java new file mode 100644 index 000000000..a556e5abf --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AliasForLinkAttributePostProcessor.java @@ -0,0 +1,54 @@ +package cn.hutool.core.annotation; + +import cn.hutool.core.util.ObjectUtil; + +import java.util.HashMap; +import java.util.Map; + +/** + * 处理注解中带有{@link Link}注解,且{@link Link#type()}为{@link RelationType#FORCE_ALIAS_FOR} + * 或{@link RelationType#ALIAS_FOR}的属性。 + * + * @author huangchengxing + * @see ForceAliasedAnnotationAttribute + * @see AliasedAnnotationAttribute + */ +public class AliasForLinkAttributePostProcessor implements SynthesizedAnnotationPostProcessor { + + @Override + public int order() { + return Integer.MIN_VALUE + 2; + } + + @Override + public void process(SynthesizedAnnotation annotation, SyntheticAnnotation syntheticAnnotation) { + Map attributeMap = new HashMap<>(annotation.getAttributes()); + attributeMap.forEach((originalAttributeName, originalAttribute) -> { + // 获取注解 + final Link link = SyntheticAnnotationUtil.getLink( + originalAttribute, RelationType.ALIAS_FOR, RelationType.FORCE_ALIAS_FOR + ); + if (ObjectUtil.isNull(link)) { + return; + } + + // 获取注解属性 + SynthesizedAnnotation aliasAnnotation = SyntheticAnnotationUtil.getLinkedAnnotation(link, syntheticAnnotation, annotation.annotationType()); + if (ObjectUtil.isNull(aliasAnnotation)) { + return; + } + final AnnotationAttribute aliasAttribute = aliasAnnotation.getAttributes().get(link.attribute()); + SyntheticAnnotationUtil.checkLinkedAttributeNotNull(originalAttribute, aliasAttribute, link); + SyntheticAnnotationUtil.checkAttributeType(originalAttribute, aliasAttribute); + + // aliasFor + if (RelationType.ALIAS_FOR.equals(link.type())) { + aliasAnnotation.setAttributes(aliasAttribute.getAttributeName(), new AliasedAnnotationAttribute(aliasAttribute, originalAttribute)); + return; + } + // forceAliasFor + aliasAnnotation.setAttributes(aliasAttribute.getAttributeName(), new ForceAliasedAnnotationAttribute(aliasAttribute, originalAttribute)); + }); + } + +} diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AliasedAnnotationAttribute.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AliasedAnnotationAttribute.java new file mode 100644 index 000000000..44638308a --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AliasedAnnotationAttribute.java @@ -0,0 +1,29 @@ +package cn.hutool.core.annotation; + +import cn.hutool.core.lang.Assert; + +/** + *

表示一个具有别名的属性。 + * 当别名属性值为默认值时,优先返回原属性的值,当别名属性不为默认值时,优先返回别名属性的值 + * + * @author huangchengxing + * @see AliasForLinkAttributePostProcessor + * @see RelationType#ALIAS_BY + * @see RelationType#ALIAS_FOR + */ +public class AliasedAnnotationAttribute extends AnnotationAttributeWrapper implements AnnotationAttribute { + + private final AnnotationAttribute aliasAttribute; + + protected AliasedAnnotationAttribute(AnnotationAttribute origin, AnnotationAttribute aliasAttribute) { + super(origin); + Assert.notNull(aliasAttribute, "aliasAttribute must not null"); + this.aliasAttribute = aliasAttribute; + } + + @Override + public Object getValue() { + return aliasAttribute.isValueEquivalentToDefaultValue() ? super.getValue() : aliasAttribute.getValue(); + } + +} diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationAttribute.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationAttribute.java new file mode 100644 index 000000000..a7b9627b5 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationAttribute.java @@ -0,0 +1,96 @@ +package cn.hutool.core.annotation; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.ReflectUtil; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; + +/** + *

表示注解的某个属性,等同于绑定的调用对象的{@link Method}方法。
+ * 在{@link SyntheticAnnotation}的解析以及取值过程中, + * 可以通过设置{@link SynthesizedAnnotation}的注解属性, + * 从而使得可以从一个注解对象中属性获取另一个注解对象的属性值 + * + *

一般情况下,注解属性的处理会发生在{@link SynthesizedAnnotationPostProcessor}调用时 + * + * @author huangchengxing + * @see SynthesizedAnnotationPostProcessor + * @see CacheableAnnotationAttribute + * @see AnnotationAttributeWrapper + * @see ForceAliasedAnnotationAttribute + * @see AliasedAnnotationAttribute + * @see MirroredAnnotationAttribute + */ +public interface AnnotationAttribute { + + /** + * 获取注解对象 + * + * @return 注解对象 + */ + Annotation getAnnotation(); + + /** + * 获取注解属性对应的方法 + * + * @return 注解属性对应的方法 + */ + Method getAttribute(); + + /** + * 获取属性名称 + * + * @return 属性名称 + */ + default String getAttributeName() { + return getAttribute().getName(); + } + + /** + * 获取注解属性 + * + * @return 注解属性 + */ + default Object getValue() { + return ReflectUtil.invoke(getAnnotation(), getAttribute()); + } + + /** + * 该注解属性的值是否等于默认值 + * + * @return 该注解属性的值是否等于默认值 + */ + default boolean isValueEquivalentToDefaultValue() { + return ObjectUtil.equals(getValue(), getAttribute().getDefaultValue()); + } + + /** + * 获取属性类型 + * + * @return 属性类型 + */ + default Class getAttributeType() { + return getAttribute().getReturnType(); + } + + /** + * 获取属性上的注解 + * + * @param annotationType 注解类型 + * @return 注解对象 + */ + default T getAnnotation(Class annotationType) { + return getAttribute().getAnnotation(annotationType); + } + + /** + * 当前注解属性是否已经被{@link AnnotationAttributeWrapper}包装 + * + * @return boolean + */ + default boolean isWrapped() { + return this instanceof AnnotationAttributeWrapper; + } + +} diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationAttributeWrapper.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationAttributeWrapper.java new file mode 100644 index 000000000..f1ec42cd8 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationAttributeWrapper.java @@ -0,0 +1,57 @@ +package cn.hutool.core.annotation; + +import cn.hutool.core.lang.Assert; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; + +/** + *

表示一个被包装过的{@link AnnotationAttribute}, + * 该实例中的一些方法可能会被代理到其他的注解属性对象中, + * 从而使得通过原始的注解属性的方法获取到另一注解属性的值。 + * + * @author huangchengxing + * @see AnnotationAttribute + * @see ForceAliasedAnnotationAttribute + * @see AliasedAnnotationAttribute + * @see MirroredAnnotationAttribute + */ +public abstract class AnnotationAttributeWrapper implements AnnotationAttribute { + + protected final AnnotationAttribute origin; + + protected AnnotationAttributeWrapper(AnnotationAttribute origin) { + Assert.notNull(origin, "target must not null"); + this.origin = origin; + } + + @Override + public Annotation getAnnotation() { + return origin.getAnnotation(); + } + + @Override + public Method getAttribute() { + return origin.getAttribute(); + } + + @Override + public boolean isValueEquivalentToDefaultValue() { + return origin.isValueEquivalentToDefaultValue(); + } + + @Override + public Class getAttributeType() { + return origin.getAttributeType(); + } + + @Override + public T getAnnotation(Class annotationType) { + return origin.getAnnotation(annotationType); + } + + @Override + public boolean isWrapped() { + return true; + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/CacheableAnnotationAttribute.java b/hutool-core/src/main/java/cn/hutool/core/annotation/CacheableAnnotationAttribute.java new file mode 100644 index 000000000..e4c8bc94c --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/CacheableAnnotationAttribute.java @@ -0,0 +1,63 @@ +package cn.hutool.core.annotation; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.ReflectUtil; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; + +/** + * {@link AnnotationAttribute}的基本实现 + * + * @author huangchengxing + */ +public class CacheableAnnotationAttribute implements AnnotationAttribute { + + private boolean valueInvoked; + private Object value; + + private boolean defaultValueInvoked; + private Object defaultValue; + + private final Annotation annotation; + private final Method attribute; + + public CacheableAnnotationAttribute(Annotation annotation, Method attribute) { + Assert.notNull(annotation, "annotation must not null"); + Assert.notNull(attribute, "attribute must not null"); + this.annotation = annotation; + this.attribute = attribute; + this.valueInvoked = false; + this.defaultValueInvoked = false; + } + + @Override + public Annotation getAnnotation() { + return this.annotation; + } + + @Override + public Method getAttribute() { + return this.attribute; + } + + @Override + public Object getValue() { + if (!valueInvoked) { + valueInvoked = true; + value = ReflectUtil.invoke(annotation, attribute); + } + return value; + } + + @Override + public boolean isValueEquivalentToDefaultValue() { + if (!defaultValueInvoked) { + defaultValue = attribute.getDefaultValue(); + defaultValueInvoked = true; + } + return ObjectUtil.equals(getValue(), defaultValue); + } + +} diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/CacheableSynthesizedAnnotationAttributeProcessor.java b/hutool-core/src/main/java/cn/hutool/core/annotation/CacheableSynthesizedAnnotationAttributeProcessor.java index 8461ac2ac..bfaa3b493 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/CacheableSynthesizedAnnotationAttributeProcessor.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/CacheableSynthesizedAnnotationAttributeProcessor.java @@ -39,7 +39,7 @@ public class CacheableSynthesizedAnnotationAttributeProcessor implements Synthes value = synthesizedAnnotations.stream() .filter(ma -> ma.hasAttribute(attributeName, attributeType)) .min(annotationComparator) - .map(ma -> ma.getAttribute(attributeName)) + .map(ma -> ma.getAttributeValue(attributeName)) .orElse(null); valueCaches.put(attributeName, attributeType, value); return (T)value; diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/ForceAliasedAnnotationAttribute.java b/hutool-core/src/main/java/cn/hutool/core/annotation/ForceAliasedAnnotationAttribute.java new file mode 100644 index 000000000..8349a3bb6 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/ForceAliasedAnnotationAttribute.java @@ -0,0 +1,40 @@ +package cn.hutool.core.annotation; + +import cn.hutool.core.lang.Assert; + +/** + * 表示一个被指定了强制别名的注解属性。 + * 当调用{@link #getValue()}时,总是返回{@link #aliasAttribute}的值 + * + * @author huangchengxing + * @see AliasAttributePostProcessor + * @see AliasForLinkAttributePostProcessor + * @see RelationType#FORCE_ALIAS_BY + * @see RelationType#FORCE_ALIAS_FOR + */ +public class ForceAliasedAnnotationAttribute extends AnnotationAttributeWrapper implements AnnotationAttribute { + + private final AnnotationAttribute aliasAttribute; + + protected ForceAliasedAnnotationAttribute(AnnotationAttribute origin, AnnotationAttribute aliasAttribute) { + super(origin); + Assert.notNull(aliasAttribute, "aliasAttribute must not null"); + this.aliasAttribute = aliasAttribute; + } + + @Override + public Object getValue() { + return aliasAttribute.getValue(); + } + + @Override + public boolean isValueEquivalentToDefaultValue() { + return aliasAttribute.isValueEquivalentToDefaultValue(); + } + + @Override + public Class getAttributeType() { + return aliasAttribute.getAttributeType(); + } + +} diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/Link.java b/hutool-core/src/main/java/cn/hutool/core/annotation/Link.java new file mode 100644 index 000000000..a2e4a9679 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/Link.java @@ -0,0 +1,34 @@ +package cn.hutool.core.annotation; + +import java.lang.annotation.*; + +/** + *

用于在同一注解中,或具有一定关联的不同注解的属性中,表明这些属性之间具有特定的关联关系。 + * 在通过{@link SyntheticAnnotation}获取合成注解后,合成注解获取属性值时会根据该注解进行调整。
+ * 注意:该注解的优先级低于{@link Alias} + * + * @author huangchengxing + * @see SyntheticAnnotation + * @see RelationType + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) +public @interface Link { + + /** + * 产生关联的注解类型,当不指定时,默认指注释的属性所在的类 + */ + Class annotation() default Annotation.class; + + /** + * {@link #annotation()}指定注解中关联的属性 + */ + String attribute() default ""; + + /** + * {@link #attribute()}指定属性与当前注解的属性建的关联关系类型 + */ + RelationType type() default RelationType.MIRROR_FOR; + +} diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/MirrorLinkAttributePostProcessor.java b/hutool-core/src/main/java/cn/hutool/core/annotation/MirrorLinkAttributePostProcessor.java new file mode 100644 index 000000000..352203382 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/MirrorLinkAttributePostProcessor.java @@ -0,0 +1,70 @@ +package cn.hutool.core.annotation; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ObjectUtil; + +import java.util.HashMap; +import java.util.Map; + +/** + * 处理注解中带有{@link Link}注解,且{@link Link#type()}为{@link RelationType#MIRROR_FOR}的属性。 + * + * @author huangchengxing + * @see MirroredAnnotationAttribute + */ +public class MirrorLinkAttributePostProcessor implements SynthesizedAnnotationPostProcessor { + + @Override + public int order() { + return Integer.MIN_VALUE + 1; + } + + @Override + public void process(SynthesizedAnnotation annotation, SyntheticAnnotation syntheticAnnotation) { + Map attributeMap = new HashMap<>(annotation.getAttributes()); + attributeMap.forEach((originalAttributeName, originalAttribute) -> { + // 跳过已经解析的镜像属性 + if (originalAttribute instanceof MirroredAnnotationAttribute) { + return; + } + + // 获取注解 + final Link link = SyntheticAnnotationUtil.getLink(originalAttribute, RelationType.MIRROR_FOR); + if (ObjectUtil.isNull(link)) { + return; + } + + // 获取指定镜像属性所在的注解 + final SynthesizedAnnotation mirrorAnnotation = SyntheticAnnotationUtil.getLinkedAnnotation(link, syntheticAnnotation, annotation.annotationType()); + if (ObjectUtil.isNull(mirrorAnnotation)) { + return; + } + + // 获取镜像属性,并进行校验 + final AnnotationAttribute mirrorAttribute = mirrorAnnotation.getAttributes().get(link.attribute()); + checkMirrorRelation(link, originalAttribute, mirrorAttribute); + + // 包装这一对镜像属性,并替换原注解中的对应属性 + final AnnotationAttribute mirroredOriginalAttribute = new MirroredAnnotationAttribute(originalAttribute, mirrorAttribute); + syntheticAnnotation.getSynthesizedAnnotation(originalAttribute.getAttribute().getDeclaringClass()) + .setAttributes(originalAttributeName, mirroredOriginalAttribute); + final AnnotationAttribute mirroredTargetAttribute = new MirroredAnnotationAttribute(mirrorAttribute, originalAttribute); + mirrorAnnotation.setAttributes(link.attribute(), mirroredTargetAttribute); + }); + } + + private void checkMirrorRelation(Link annotation, AnnotationAttribute original, AnnotationAttribute mirror) { + // 镜像属性必须存在 + SyntheticAnnotationUtil.checkLinkedAttributeNotNull(original, mirror, annotation); + // 镜像属性返回值必须一致 + SyntheticAnnotationUtil.checkAttributeType(original, mirror); + // 镜像属性上必须存在对应的注解 + final Link mirrorAttributeAnnotation = mirror.getAnnotation(Link.class); + Assert.isTrue( + ObjectUtil.isNotNull(mirrorAttributeAnnotation) && RelationType.MIRROR_FOR.equals(mirrorAttributeAnnotation.type()), + "mirror attribute [{}] of original attribute [{}] must marked by @Link, and also @LinkType.type() must is [{}]", + mirror.getAttribute(), original.getAttribute(), RelationType.MIRROR_FOR + ); + } + +} diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/MirroredAnnotationAttribute.java b/hutool-core/src/main/java/cn/hutool/core/annotation/MirroredAnnotationAttribute.java new file mode 100644 index 000000000..2dcd0dd1d --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/MirroredAnnotationAttribute.java @@ -0,0 +1,41 @@ +package cn.hutool.core.annotation; + +import cn.hutool.core.lang.Assert; + +/** + * 表示存在对应镜像属性的注解属性,当获取值时将根据{@link RelationType#MIRROR_FOR}的规则进行处理 + * + * @author huangchengxing + * @see MirrorLinkAttributePostProcessor + * @see RelationType#MIRROR_FOR + */ +public class MirroredAnnotationAttribute extends AnnotationAttributeWrapper implements AnnotationAttribute { + + private final AnnotationAttribute mirrorAttribute; + + public MirroredAnnotationAttribute(AnnotationAttribute origin, AnnotationAttribute mirrorAttribute) { + super(origin); + this.mirrorAttribute = mirrorAttribute; + } + + @Override + public Object getValue() { + boolean originIsDefault = origin.isValueEquivalentToDefaultValue(); + boolean targetIsDefault = mirrorAttribute.isValueEquivalentToDefaultValue(); + Object originValue = origin.getValue(); + Object targetValue = mirrorAttribute.getValue(); + + // 都为默认值,或都为非默认值时,两方法的返回值必须相等 + if (originIsDefault == targetIsDefault) { + Assert.equals( + originValue, targetValue, + "the values of attributes [{}] and [{}] that mirror each other are different: [{}] <==> [{}]", + origin.getAttribute(), mirrorAttribute.getAttribute(), originValue, targetValue + ); + return originValue; + } + + // 两者有一者不为默认值时,优先返回非默认值 + return originIsDefault ? targetValue : originValue; + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/RelationType.java b/hutool-core/src/main/java/cn/hutool/core/annotation/RelationType.java new file mode 100644 index 000000000..ee739ae2d --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/RelationType.java @@ -0,0 +1,50 @@ +package cn.hutool.core.annotation; + +/** + *

注解属性的关系类型
+ * 若将被{@link Link}注解的属性称为“原始属性”,而在{@link Link}注解中指向的注解属性称为“关联属性”, + * 则该枚举用于描述“原始属性”与“关联属性”在{@link SyntheticAnnotation}处理过程中的作用关系。
+ * 根据在{@link Link#type()}中指定的关系类型的不同,通过{@link SyntheticAnnotation}合成的注解的属性值也将有所变化。 + * + *

当一个注解中的所有属性同时具备多种关系时,将依次按下述顺序处理: + *

    + *
  1. 属性上的{@link Alias}注解;
  2. + *
  3. 属性上的{@link Link}注解,且{@link Link#type()}为{@link #MIRROR_FOR};
  4. + *
  5. 属性上的{@link Link}注解,且{@link Link#type()}为{@link #FORCE_ALIAS_FOR};
  6. + *
  7. 属性上的{@link Link}注解,且{@link Link#type()}为{@link #ALIAS_FOR};
  8. + *
+ * + * @author huangchengxing + * @see SyntheticAnnotation + * @see Link + */ +public enum RelationType { + + /** + *

表示注解的属性与指定的属性互为镜像,通过一个属性将能够获得对方的值。
+ * 它们遵循下述规则: + *

    + *
  • 互为镜像的两个属性,必须同时通过指定模式为{@code MIRROR_FOR}的{@link Link}注解指定对方;
  • + *
  • 互为镜像的两个属性,类型必须一致;
  • + *
  • 互为镜像的两个属性在获取值,且两者的值皆不同时,必须且仅允许有一个非默认值,该值被优先返回;
  • + *
  • 互为镜像的两个属性,在值都为默认值或都不为默认值时,两者的值必须相等;
  • + *
+ */ + MIRROR_FOR, + + /** + *

表示“原始属性”将作为“关联属性”的别名。 + *

    + *
  • 当“原始属性”为默认值时,获取“关联属性”将返回“关联属性”本身的值;
  • + *
  • 当“原始属性”不为默认值时,获取“关联属性”将返回“原始属性”的值;
  • + *
+ */ + ALIAS_FOR, + + /** + *

表示“原始属性”将强制作为“关联属性”的别名。效果等同于在“原始属性”上添加{@link Alias}注解, + * 任何情况下,获取“关联属性”的值都将直接返回“原始属性”的值 + */ + FORCE_ALIAS_FOR; + +} diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotation.java b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotation.java index 62a3dc91a..ad016430e 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotation.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotation.java @@ -1,6 +1,9 @@ package cn.hutool.core.annotation; +import cn.hutool.core.collection.CollUtil; + import java.lang.annotation.Annotation; +import java.util.Map; /** * 用于在{@link SyntheticAnnotation}中表示一个处于合成状态的注解对象 @@ -10,6 +13,13 @@ import java.lang.annotation.Annotation; */ public interface SynthesizedAnnotation extends Annotation { + /** + * 获取所属的合成注解 + * + * @return 合成注解 + */ + SyntheticAnnotation getOwner(); + /** * 获取该合成注解对应的根节点 * @@ -58,12 +68,38 @@ public interface SynthesizedAnnotation extends Annotation { */ boolean hasAttribute(String attributeName, Class returnType); + /** + * 获取该注解的全部属性 + * + * @return 注解属性 + */ + Map getAttributes(); + + /** + * 设置该注解的全部属性 + * + * @param attributes 注解属性 + */ + default void setAttributes(Map attributes) { + if (CollUtil.isNotEmpty(attributes)) { + attributes.forEach(this::setAttributes); + } + } + + /** + * 设置属性值 + * + * @param attributeName 属性名称 + * @param attribute 注解属性 + */ + void setAttributes(String attributeName, AnnotationAttribute attribute); + /** * 获取属性值 * * @param attributeName 属性名 * @return 属性值 */ - Object getAttribute(String attributeName); + Object getAttributeValue(String attributeName); } diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationPostProcessor.java b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationPostProcessor.java new file mode 100644 index 000000000..5bf11f1f0 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationPostProcessor.java @@ -0,0 +1,45 @@ +package cn.hutool.core.annotation; + +import cn.hutool.core.comparator.CompareUtil; + +import java.util.Comparator; + +/** + *

被合成注解后置处理器,用于在{@link SyntheticAnnotation}加载完所有待合成注解后, + * 再对加载好的{@link SynthesizedAnnotation}进行后置处理。
+ * 当多个{@link SynthesizedAnnotationPostProcessor}需要一起执行时,将按照{@link #order()}的返回值进行排序, + * 该值更小的处理器将被优先执行。 + * + * @author huangchengxing + */ +public interface SynthesizedAnnotationPostProcessor extends Comparable { + + /** + * 在一组后置处理器中被调用的顺序,越小越靠前 + * + * @return 排序值 + */ + default int order() { + return Integer.MAX_VALUE; + } + + /** + * 比较两个后置处理器的{@link #order()}返回值 + * + * @param o 比较对象 + * @return 大小 + */ + @Override + default int compareTo(SynthesizedAnnotationPostProcessor o) { + return CompareUtil.compare(this, o, Comparator.comparing(SynthesizedAnnotationPostProcessor::order)); + } + + /** + * 给定指定被合成注解与其所属的合成注解实例,经过处理后返回最终 + * + * @param annotation 被合成的注解 + * @param syntheticAnnotation 注解所属的合成注解 + */ + void process(SynthesizedAnnotation annotation, SyntheticAnnotation syntheticAnnotation); + +} diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticAnnotation.java b/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticAnnotation.java index 55c0c44eb..e9a564242 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticAnnotation.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticAnnotation.java @@ -2,6 +2,7 @@ package cn.hutool.core.annotation; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; +import java.util.Collection; /** * 表示基于特定规则聚合的一组注解对象 @@ -9,21 +10,33 @@ import java.lang.reflect.AnnotatedElement; *

合成注解一般被用于处理类层级结果中具有直接或间接关联的注解对象, * 当实例被创建时,会获取到这些注解对象,并使用{@link SynthesizedAnnotationSelector}对类型相同的注解进行过滤, * 并最终得到类型不重复的有效注解对象。这些有效注解将被包装为{@link SynthesizedAnnotation}, - * 然后最终用于“合成”一个{@link SynthesizedAnnotation}。 + * 然后最终用于“合成”一个{@link SyntheticAnnotation}。
+ * {@link SynthesizedAnnotationSelector}是合成注解生命周期中的第一个钩子, + * 自定义选择器以拦截原始注解被扫描的过程。 + * + *

当合成注解完成对待合成注解的扫描,并完成了必要属性的加载后, + * 将会按顺序依次调用{@link SynthesizedAnnotationPostProcessor}, + * 注解后置处理器允许用于对完成注解的待合成注解进行二次调整, + * 该钩子一般用于根据{@link Link}注解对属性进行调整。
+ * {@link SynthesizedAnnotationPostProcessor}是合成注解生命周期中的第二个钩子, + * 自定义后置处理器以拦截原始在转为待合成注解后的初始化过程。 + * + *

合成注解允许通过{@link #syntheticAnnotation(Class)}合成一个指定的注解对象, + * 该方法返回的注解对象可能是原始的注解对象,也有可能通过动态代理的方式生成, + * 该对象实例的属性不一定来自对象本身,而是来自于经过{@link SynthesizedAnnotationAttributeProcessor} + * 处理后的、用于合成当前实例的全部关联注解的相关属性。
+ * {@link SynthesizedAnnotationAttributeProcessor}是合成注解生命周期中的第三个钩子, + * 自定义属性处理器以拦截合成注解的取值过程。 * *

合成注解可以作为一个特殊的{@link Annotation}或者{@link AnnotatedElement}, * 当调用{@link Annotation}的方法时,应当返回当前实例本身的有效信息, * 而当调用{@link AnnotatedElement}的方法时,应当返回用于合成该对象的相关注解的信息。 * - *

合成注解允许通过{@link #syntheticAnnotation(Class)}合成一个指定的注解对象, - * 该方法返回的注解对象可能是原始的注解对象,也有可能通过动态代理的方式生成, - * 该对象实例的属性不一定来自对象本身,而是来自于经过{@link SynthesizedAnnotationAttributeProcessor} - * 处理后的、用于合成当前实例的全部关联注解的相关属性。 - * * @author huangchengxing * @see SynthesizedAnnotation * @see SynthesizedAnnotationSelector * @see SynthesizedAnnotationAttributeProcessor + * @see SynthesizedAnnotationPostProcessor * @see SyntheticMetaAnnotation */ public interface SyntheticAnnotation extends Annotation, AnnotatedElement { @@ -42,6 +55,13 @@ public interface SyntheticAnnotation extends Annotation, AnnotatedElement { */ SynthesizedAnnotationAttributeProcessor getAttributeProcessor(); + /** + * 获取合成注解属性后置处理器 + * + * @return 合成注解属性后置处理器 + */ + Collection getSynthesizedAnnotationAttributePostProcessors(); + /** * 获取已合成的注解 * diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticAnnotationProxy.java b/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticAnnotationProxy.java index 5cf2d1723..c8b5513e3 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticAnnotationProxy.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticAnnotationProxy.java @@ -1,5 +1,6 @@ package cn.hutool.core.annotation; +import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Opt; import cn.hutool.core.util.ClassUtil; import cn.hutool.core.util.ObjectUtil; @@ -24,12 +25,11 @@ import java.util.stream.Stream; */ class SyntheticAnnotationProxy implements InvocationHandler { - private final SyntheticAnnotation syntheticAnnotation; private final SynthesizedAnnotation annotation; private final Map> methods; - SyntheticAnnotationProxy(SyntheticAnnotation syntheticAnnotation, SynthesizedAnnotation annotation) { - this.syntheticAnnotation = syntheticAnnotation; + SyntheticAnnotationProxy(SynthesizedAnnotation annotation) { + Assert.notNull(annotation, "annotation must not null"); this.annotation = annotation; this.methods = new HashMap<>(9); loadMethods(); @@ -50,7 +50,10 @@ class SyntheticAnnotationProxy implements InvocationHandler { static T create( Class annotationType, SyntheticAnnotation syntheticAnnotation) { final SynthesizedAnnotation annotation = syntheticAnnotation.getSynthesizedAnnotation(annotationType); - final SyntheticAnnotationProxy proxyHandler = new SyntheticAnnotationProxy(syntheticAnnotation, annotation); + if (ObjectUtil.isNull(annotation)) { + return null; + } + final SyntheticAnnotationProxy proxyHandler = new SyntheticAnnotationProxy(annotation); if (ObjectUtil.isNull(annotation)) { return null; } @@ -84,7 +87,12 @@ class SyntheticAnnotationProxy implements InvocationHandler { methods.put("getVerticalDistance", (method, args) -> annotation.getVerticalDistance()); methods.put("getHorizontalDistance", (method, args) -> annotation.getHorizontalDistance()); methods.put("hasAttribute", (method, args) -> annotation.hasAttribute((String)args[0], (Class)args[1])); - methods.put("getAttribute", (method, args) -> annotation.getAttribute((String)args[0])); + methods.put("getAttributes", (method, args) -> annotation.getAttributes()); + methods.put("setAttributes", (method, args) -> { + throw new UnsupportedOperationException("proxied annotation can not reset attributes"); + }); + methods.put("getAttributeValue", (method, args) -> annotation.getAttributeValue((String)args[0])); + methods.put("getOwner", (method, args) -> annotation.getOwner()); methods.put("annotationType", (method, args) -> annotation.annotationType()); for (final Method declaredMethod : annotation.getAnnotation().annotationType().getDeclaredMethods()) { methods.put(declaredMethod.getName(), (method, args) -> proxyAttributeValue(method)); @@ -94,17 +102,17 @@ class SyntheticAnnotationProxy implements InvocationHandler { private String proxyToString() { final String attributes = Stream.of(annotation.annotationType().getDeclaredMethods()) .filter(AnnotationUtil::isAttributeMethod) - .map(method -> StrUtil.format("{}={}", method.getName(), syntheticAnnotation.getAttribute(method.getName(), method.getReturnType()))) + .map(method -> StrUtil.format("{}={}", method.getName(), annotation.getOwner().getAttribute(method.getName(), method.getReturnType()))) .collect(Collectors.joining(", ")); return StrUtil.format("@{}({})", annotation.annotationType().getName(), attributes); } private int proxyHashCode() { - return Objects.hash(syntheticAnnotation, annotation); + return Objects.hash(annotation.getOwner(), annotation); } private Object proxyGetSyntheticAnnotation() { - return syntheticAnnotation; + return annotation.getOwner(); } private Object proxyGetSynthesizedAnnotation() { @@ -112,7 +120,7 @@ class SyntheticAnnotationProxy implements InvocationHandler { } private Object proxyAttributeValue(Method attributeMethod) { - return syntheticAnnotation.getAttribute(attributeMethod.getName(), attributeMethod.getReturnType()); + return annotation.getOwner().getAttribute(attributeMethod.getName(), attributeMethod.getReturnType()); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticAnnotationUtil.java b/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticAnnotationUtil.java new file mode 100644 index 000000000..1cec36aaa --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticAnnotationUtil.java @@ -0,0 +1,71 @@ +package cn.hutool.core.annotation; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.lang.Opt; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjectUtil; + +import java.lang.annotation.Annotation; + +/** + * {@link SyntheticAnnotation}相关工具,用于内部使用 + * + * @author huangchengxing + */ +class SyntheticAnnotationUtil { + + /** + * 从注解属性上获取指定类型的{@link Link}注解 + * + * @param attribute 注解属性 + * @param relationTypes 类型 + * @return 注解 + */ + static Link getLink(AnnotationAttribute attribute, RelationType... relationTypes) { + return Opt.ofNullable(attribute) + .map(t -> t.getAnnotation(Link.class)) + .filter(a -> ArrayUtil.contains(relationTypes, a.type())) + .get(); + } + + /** + * 从合成注解中获取{@link Link#type()}指定的注解对象 + * + * @param annotation {@link Link}注解 + * @param syntheticAnnotation 合成注解 + */ + static SynthesizedAnnotation getLinkedAnnotation( + Link annotation, SyntheticAnnotation syntheticAnnotation, Class defaultType) { + final Class targetAnnotationType = ObjectUtil.equals(annotation.annotation(), Annotation.class) ? + defaultType : annotation.annotation(); + return syntheticAnnotation.getSynthesizedAnnotation(targetAnnotationType); + } + + /** + * 校验两个注解属性的返回值类型是否一致 + * + * @param original 原属性 + * @param alias 别名属性 + */ + static void checkAttributeType(AnnotationAttribute original, AnnotationAttribute alias) { + Assert.equals( + original.getAttributeType(), alias.getAttributeType(), + "return type of the linked attribute [{}] is inconsistent with the original [{}]", + original.getAttribute(), alias.getAttribute() + ); + } + + /** + * 检查{@link Link}指向的注解属性是否存在 + * + * @param original {@link Link}注解的属性 + * @param linkedAttribute {@link Link}指向的注解属性 + * @param annotation {@link Link}注解 + */ + static void checkLinkedAttributeNotNull(AnnotationAttribute original, AnnotationAttribute linkedAttribute, Link annotation) { + Assert.notNull(linkedAttribute, "cannot find linked attribute [{}] of original [{}] in [{}]", + original.getAttribute(), annotation.attribute(), annotation.annotation() + ); + } + +} diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticMetaAnnotation.java b/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticMetaAnnotation.java index c2a7a97d8..3299421ea 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticMetaAnnotation.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticMetaAnnotation.java @@ -5,14 +5,13 @@ import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Opt; import cn.hutool.core.util.ClassUtil; import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.ReflectUtil; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; -import java.util.Comparator; -import java.util.LinkedHashMap; -import java.util.Map; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * 表示一个根注解与根注解上的多层元注解合成的注解 @@ -65,6 +64,11 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation { */ private final SynthesizedAnnotationAttributeProcessor attributeProcessor; + /** + * 合成注解属性处理器 + */ + private final List postProcessors; + /** * 基于指定根注解,为其层级结构中的全部注解构造一个合成注解。 * 当层级结构中出现了相同的注解对象时,将优先选择以距离根注解最近,且优先被扫描的注解对象, @@ -78,6 +82,11 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation { new CacheableSynthesizedAnnotationAttributeProcessor( Comparator.comparing(SynthesizedAnnotation::getVerticalDistance) .thenComparing(SynthesizedAnnotation::getHorizontalDistance) + ), + Arrays.asList( + new AliasAttributePostProcessor(), + new MirrorLinkAttributePostProcessor(), + new AliasForLinkAttributePostProcessor() ) ); } @@ -92,16 +101,26 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation { public SyntheticMetaAnnotation( Annotation annotation, SynthesizedAnnotationSelector annotationSelector, - SynthesizedAnnotationAttributeProcessor attributeProcessor) { + SynthesizedAnnotationAttributeProcessor attributeProcessor, + Collection annotationPostProcessors) { Assert.notNull(annotation, "annotation must not null"); Assert.notNull(annotationSelector, "annotationSelector must not null"); Assert.notNull(attributeProcessor, "attributeProcessor must not null"); + Assert.notNull(annotationPostProcessors, "attributePostProcessors must not null"); + // 初始化属性 this.source = annotation; this.annotationSelector = annotationSelector; this.attributeProcessor = attributeProcessor; + this.postProcessors = new ArrayList<>(annotationPostProcessors); + this.postProcessors.sort(Comparator.comparing(SynthesizedAnnotationPostProcessor::order)); this.metaAnnotationMap = new LinkedHashMap<>(); + + // 初始化元注解信息,并进行后置处理 loadMetaAnnotations(); + annotationPostProcessors.forEach(processor -> + metaAnnotationMap.values().forEach(synthesized -> processor.process(synthesized, this)) + ); } /** @@ -142,6 +161,16 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation { return this.attributeProcessor; } + /** + * 获取合成注解属性后置处理器 + * + * @return 合成注解属性后置处理器 + */ + @Override + public Collection getSynthesizedAnnotationAttributePostProcessors() { + return this.postProcessors; + } + /** * 获取已合成的注解 * @@ -241,11 +270,11 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation { private void loadMetaAnnotations() { Assert.isFalse(SyntheticAnnotationProxy.isProxyAnnotation(source.getClass()), "source [{}] has been synthesized"); // 扫描元注解 - metaAnnotationMap.put(source.annotationType(), new MetaAnnotation(source, source, 0, 0)); + metaAnnotationMap.put(source.annotationType(), new MetaAnnotation(this, source, source, 0, 0)); new MetaAnnotationScanner().scan( (index, annotation) -> { SynthesizedAnnotation oldAnnotation = metaAnnotationMap.get(annotation.annotationType()); - SynthesizedAnnotation newAnnotation = new MetaAnnotation(source, annotation, index, metaAnnotationMap.size()); + SynthesizedAnnotation newAnnotation = new MetaAnnotation(this, source, annotation, index, metaAnnotationMap.size()); if (ObjectUtil.isNull(oldAnnotation)) { metaAnnotationMap.put(annotation.annotationType(), newAnnotation); } else { @@ -263,18 +292,32 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation { */ public static class MetaAnnotation implements Annotation, SynthesizedAnnotation { + private final SyntheticAnnotation owner; private final Annotation root; private final Annotation annotation; - private final Map attributeMethodCaches; + private final Map attributeMethodCaches; private final int verticalDistance; private final int horizontalDistance; - public MetaAnnotation(Annotation root, Annotation annotation, int verticalDistance, int horizontalDistance) { + public MetaAnnotation(SyntheticAnnotation owner, Annotation root, Annotation annotation, int verticalDistance, int horizontalDistance) { + this.owner = owner; this.root = root; this.annotation = annotation; this.verticalDistance = verticalDistance; this.horizontalDistance = horizontalDistance; - this.attributeMethodCaches = AnnotationUtil.getAttributeMethods(annotation.annotationType()); + this.attributeMethodCaches = Stream.of(annotation.annotationType().getDeclaredMethods()) + .filter(AnnotationUtil::isAttributeMethod) + .collect(Collectors.toMap(Method::getName, method -> new CacheableAnnotationAttribute(annotation, method))); + } + + /** + * 获取所属的合成注解 + * + * @return 合成注解 + */ + @Override + public SyntheticAnnotation getOwner() { + return owner; } /** @@ -347,21 +390,42 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation { @Override public boolean hasAttribute(String attributeName, Class returnType) { return Opt.ofNullable(attributeMethodCaches.get(attributeName)) - .filter(method -> ClassUtil.isAssignable(returnType, method.getReturnType())) + .filter(method -> ClassUtil.isAssignable(returnType, method.getAttributeType())) .isPresent(); } /** - * 获取元注解的属性值 + * 获取该注解的全部属性 * - * @param attributeName 属性名 - * @return 元注解的属性值 + * @return 注解属性 */ @Override - public Object getAttribute(String attributeName) { + public Map getAttributes() { + return this.attributeMethodCaches; + } + + /** + * 设置属性值 + * + * @param attributeName 属性名称 + * @param attribute 注解属性 + */ + @Override + public void setAttributes(String attributeName, AnnotationAttribute attribute) { + attributeMethodCaches.put(attributeName, attribute); + } + + /** + * 获取属性值 + * + * @param attributeName 属性名 + * @return 属性值 + */ + @Override + public Object getAttributeValue(String attributeName) { return Opt.ofNullable(attributeMethodCaches.get(attributeName)) - .map(method -> ReflectUtil.invoke(annotation, method)) - .orElse(null); + .map(AnnotationAttribute::getValue) + .get(); } } diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/SyntheticMetaAnnotationTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/SyntheticMetaAnnotationTest.java index 009f1e205..31403ed79 100644 --- a/hutool-core/src/test/java/cn/hutool/core/annotation/SyntheticMetaAnnotationTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/SyntheticMetaAnnotationTest.java @@ -53,6 +53,61 @@ public class SyntheticMetaAnnotationTest { Assert.assertThrows(IllegalArgumentException.class, () -> new SyntheticMetaAnnotation(grandParentAnnotation)); } + @Test + public void mirrorAttributeTest() { + AnnotationForMirrorTest annotation = ClassForMirrorTest.class.getAnnotation(AnnotationForMirrorTest.class); + SyntheticAnnotation synthetic = new SyntheticMetaAnnotation(annotation); + AnnotationForMirrorTest syntheticAnnotation = synthetic.syntheticAnnotation(AnnotationForMirrorTest.class); + Assert.assertEquals("Foo", syntheticAnnotation.name()); + Assert.assertEquals("Foo", syntheticAnnotation.value()); + + annotation = ClassForMirrorTest2.class.getAnnotation(AnnotationForMirrorTest.class); + synthetic = new SyntheticMetaAnnotation(annotation); + syntheticAnnotation = synthetic.syntheticAnnotation(AnnotationForMirrorTest.class); + Assert.assertEquals("Foo", syntheticAnnotation.name()); + Assert.assertEquals("Foo", syntheticAnnotation.value()); + + annotation = ClassForMirrorTest3.class.getAnnotation(AnnotationForMirrorTest.class); + synthetic = new SyntheticMetaAnnotation(annotation); + syntheticAnnotation = synthetic.syntheticAnnotation(AnnotationForMirrorTest.class); + AnnotationForMirrorTest finalSyntheticAnnotation = syntheticAnnotation; + Assert.assertThrows(IllegalArgumentException.class, () -> finalSyntheticAnnotation.name()); + } + + @Test + public void aliasForTest() { + AnnotationForAliasForTest annotation = ClassForAliasForTest.class.getAnnotation(AnnotationForAliasForTest.class); + SyntheticAnnotation synthetic = new SyntheticMetaAnnotation(annotation); + MetaAnnotationForAliasForTest metaAnnotation = synthetic.syntheticAnnotation(MetaAnnotationForAliasForTest.class); + Assert.assertEquals("Meta", metaAnnotation.name()); + AnnotationForAliasForTest childAnnotation = synthetic.syntheticAnnotation(AnnotationForAliasForTest.class); + Assert.assertEquals("", childAnnotation.value()); + + annotation = ClassForAliasForTest2.class.getAnnotation(AnnotationForAliasForTest.class); + synthetic = new SyntheticMetaAnnotation(annotation); + metaAnnotation = synthetic.syntheticAnnotation(MetaAnnotationForAliasForTest.class); + Assert.assertEquals("Foo", metaAnnotation.name()); + childAnnotation = synthetic.syntheticAnnotation(AnnotationForAliasForTest.class); + Assert.assertEquals("Foo", childAnnotation.value()); + } + + @Test + public void forceAliasForTest() { + AnnotationForceForAliasForTest annotation = ClassForForceAliasForTest.class.getAnnotation(AnnotationForceForAliasForTest.class); + SyntheticAnnotation synthetic = new SyntheticMetaAnnotation(annotation); + MetaAnnotationForForceAliasForTest metaAnnotation = synthetic.syntheticAnnotation(MetaAnnotationForForceAliasForTest.class); + Assert.assertEquals("", metaAnnotation.name()); + AnnotationForceForAliasForTest childAnnotation = synthetic.syntheticAnnotation(AnnotationForceForAliasForTest.class); + Assert.assertEquals("", childAnnotation.value()); + + annotation = ClassForForceAliasForTest2.class.getAnnotation(AnnotationForceForAliasForTest.class); + synthetic = new SyntheticMetaAnnotation(annotation); + metaAnnotation = synthetic.syntheticAnnotation(MetaAnnotationForForceAliasForTest.class); + Assert.assertEquals("Foo", metaAnnotation.name()); + childAnnotation = synthetic.syntheticAnnotation(AnnotationForceForAliasForTest.class); + Assert.assertEquals("Foo", childAnnotation.value()); + } + // 注解结构如下: // AnnotatedClass -> @ChildAnnotation -> @ParentAnnotation -> @GrandParentAnnotation // -> @GrandParentAnnotation @@ -85,4 +140,61 @@ public class SyntheticMetaAnnotationTest { Class grandParentType() default Void.class; } + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.METHOD, ElementType.TYPE }) + @interface AnnotationForMirrorTest { + @Link(attribute = "name") + String value() default ""; + @Link(attribute = "value") + String name() default ""; + } + @AnnotationForMirrorTest("Foo") + static class ClassForMirrorTest {} + @AnnotationForMirrorTest(name = "Foo") + static class ClassForMirrorTest2 {} + @AnnotationForMirrorTest(value = "Aoo", name = "Foo") + static class ClassForMirrorTest3 {} + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.METHOD, ElementType.TYPE }) + @interface MetaAnnotationForAliasForTest { + String name() default ""; + } + @MetaAnnotationForAliasForTest(name = "Meta") + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.METHOD, ElementType.TYPE }) + @interface AnnotationForAliasForTest { + @Link( + annotation = MetaAnnotationForAliasForTest.class, + attribute = "name", + type = RelationType.ALIAS_FOR + ) + String value() default ""; + } + @AnnotationForAliasForTest + static class ClassForAliasForTest {} + @AnnotationForAliasForTest("Foo") + static class ClassForAliasForTest2 {} + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.METHOD, ElementType.TYPE }) + @interface MetaAnnotationForForceAliasForTest { + String name() default ""; + } + @MetaAnnotationForForceAliasForTest(name = "Meta") + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.METHOD, ElementType.TYPE }) + @interface AnnotationForceForAliasForTest { + @Link( + annotation = MetaAnnotationForForceAliasForTest.class, + attribute = "name", + type = RelationType.FORCE_ALIAS_FOR + ) + String value() default ""; + } + @AnnotationForceForAliasForTest + static class ClassForForceAliasForTest {} + @AnnotationForceForAliasForTest("Foo") + static class ClassForForceAliasForTest2 {} + }