From e4a7576d7fce92e1362e0a93e690909a9f1f38cd Mon Sep 17 00:00:00 2001 From: huangchengxing <841396397@qq.com> Date: Wed, 13 Jul 2022 20:38:18 +0800 Subject: [PATCH 01/15] =?UTF-8?q?1.=E6=B7=BB=E5=8A=A0@Link=E6=B3=A8?= =?UTF-8?q?=E8=A7=A3=E4=BB=A5=E5=8F=8A=E5=AF=B9=E5=BA=94=E7=9A=84=E5=85=B3?= =?UTF-8?q?=E7=B3=BB=E6=9E=9A=E4=B8=BE=202.=E6=B7=BB=E5=8A=A0=E6=B3=A8?= =?UTF-8?q?=E8=A7=A3=E5=B1=9E=E6=80=A7=E5=AF=B9=E8=B1=A1=E4=B8=8E=E5=90=88?= =?UTF-8?q?=E6=88=90=E6=B3=A8=E8=A7=A3=E5=90=8E=E7=BD=AE=E5=A4=84=E7=90=86?= =?UTF-8?q?=E5=99=A8=EF=BC=8C=E7=94=A8=E4=BA=8E=E6=94=AF=E6=8C=81=E5=9F=BA?= =?UTF-8?q?=E4=BA=8E@Alias=E4=B8=8E@Link=E7=9A=84=E6=9E=9A=E4=B8=BE?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=E6=98=A0=E5=B0=84=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AliasAttributePostProcessor.java | 63 ++++++++++ .../AliasForLinkAttributePostProcessor.java | 54 +++++++++ .../AliasedAnnotationAttribute.java | 29 +++++ .../core/annotation/AnnotationAttribute.java | 96 +++++++++++++++ .../AnnotationAttributeWrapper.java | 57 +++++++++ .../CacheableAnnotationAttribute.java | 63 ++++++++++ ...nthesizedAnnotationAttributeProcessor.java | 2 +- .../ForceAliasedAnnotationAttribute.java | 40 +++++++ .../java/cn/hutool/core/annotation/Link.java | 34 ++++++ .../MirrorLinkAttributePostProcessor.java | 70 +++++++++++ .../MirroredAnnotationAttribute.java | 41 +++++++ .../hutool/core/annotation/RelationType.java | 50 ++++++++ .../annotation/SynthesizedAnnotation.java | 38 +++++- .../SynthesizedAnnotationPostProcessor.java | 45 +++++++ .../core/annotation/SyntheticAnnotation.java | 32 ++++- .../annotation/SyntheticAnnotationProxy.java | 26 ++-- .../annotation/SyntheticAnnotationUtil.java | 71 +++++++++++ .../annotation/SyntheticMetaAnnotation.java | 98 ++++++++++++--- .../SyntheticMetaAnnotationTest.java | 112 ++++++++++++++++++ 19 files changed, 987 insertions(+), 34 deletions(-) create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/AliasAttributePostProcessor.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/AliasForLinkAttributePostProcessor.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/AliasedAnnotationAttribute.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationAttribute.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationAttributeWrapper.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/CacheableAnnotationAttribute.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/ForceAliasedAnnotationAttribute.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/Link.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/MirrorLinkAttributePostProcessor.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/MirroredAnnotationAttribute.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/RelationType.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationPostProcessor.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticAnnotationUtil.java 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 {} + } From 491c53e7dd3c07f3945c140c32d1282139c96c55 Mon Sep 17 00:00:00 2001 From: huangchengxing <841396397@qq.com> Date: Wed, 13 Jul 2022 22:31:48 +0800 Subject: [PATCH 02/15] =?UTF-8?q?=E6=B7=BB=E5=8A=A0@MirrorFor=E5=92=8C@Ali?= =?UTF-8?q?asFor=E6=B3=A8=E8=A7=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AbstractAnnotationAttributeWrapper.java | 71 +++++++++++ .../AliasAttributePostProcessor.java | 18 +-- .../cn/hutool/core/annotation/AliasFor.java | 35 ++++++ .../AliasForLinkAttributePostProcessor.java | 38 +++++- .../AliasedAnnotationAttribute.java | 30 +++-- .../core/annotation/AnnotationAttribute.java | 14 ++- .../AnnotationAttributeWrapper.java | 116 ++++++++++++++---- .../core/annotation/AnnotationUtil.java | 27 +++- ...nthesizedAnnotationAttributeProcessor.java | 2 +- .../ForceAliasedAnnotationAttribute.java | 37 +++--- .../java/cn/hutool/core/annotation/Link.java | 2 +- .../cn/hutool/core/annotation/MirrorFor.java | 38 ++++++ .../MirrorLinkAttributePostProcessor.java | 8 +- .../MirroredAnnotationAttribute.java | 29 +++-- .../annotation/SynthesizedAnnotation.java | 13 +- .../annotation/SyntheticAnnotationProxy.java | 2 +- .../annotation/SyntheticAnnotationUtil.java | 2 +- .../annotation/SyntheticMetaAnnotation.java | 18 ++- .../SyntheticMetaAnnotationTest.java | 96 ++++++++++++++- 19 files changed, 505 insertions(+), 91 deletions(-) create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/AbstractAnnotationAttributeWrapper.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/AliasFor.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/MirrorFor.java diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractAnnotationAttributeWrapper.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractAnnotationAttributeWrapper.java new file mode 100644 index 000000000..9d4c170cd --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractAnnotationAttributeWrapper.java @@ -0,0 +1,71 @@ +package cn.hutool.core.annotation; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ObjectUtil; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * {@link AnnotationAttributeWrapper}的基本实现 + * + * @author huangchengxing + * @see ForceAliasedAnnotationAttribute + * @see AliasedAnnotationAttribute + * @see MirroredAnnotationAttribute + */ +public abstract class AbstractAnnotationAttributeWrapper implements AnnotationAttributeWrapper { + + protected final AnnotationAttribute original; + protected final AnnotationAttribute linked; + + protected AbstractAnnotationAttributeWrapper(AnnotationAttribute original, AnnotationAttribute linked) { + Assert.notNull(original, "target must not null"); + Assert.notNull(linked, "linked must not null"); + this.original = original; + this.linked = linked; + } + + @Override + public AnnotationAttribute getOriginal() { + return original; + } + + @Override + public AnnotationAttribute getLinked() { + return linked; + } + + @Override + public AnnotationAttribute getNonWrappedOriginal() { + AnnotationAttribute curr = null; + AnnotationAttribute next = original; + while (next != null) { + curr = next; + next = next.isWrapped() ? ((AnnotationAttributeWrapper)curr).getOriginal() : null; + } + return curr; + } + + @Override + public Collection getAllLinkedNonWrappedAttributes() { + List leafAttributes = new ArrayList<>(); + collectLeafAttribute(this, leafAttributes); + return leafAttributes; + } + + private void collectLeafAttribute(AnnotationAttribute curr, List leafAttributes) { + if (ObjectUtil.isNull(curr)) { + return; + } + if (!curr.isWrapped()) { + leafAttributes.add(curr); + return; + } + AnnotationAttributeWrapper wrappedAttribute = (AnnotationAttributeWrapper)curr; + collectLeafAttribute(wrappedAttribute.getOriginal(), leafAttributes); + collectLeafAttribute(wrappedAttribute.getLinked(), leafAttributes); + } + +} 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 index f9888c505..8eba6bb49 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/AliasAttributePostProcessor.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AliasAttributePostProcessor.java @@ -26,36 +26,36 @@ public class AliasAttributePostProcessor implements SynthesizedAnnotationPostPro @Override public void process(SynthesizedAnnotation annotation, SyntheticAnnotation syntheticAnnotation) { - Map attributeMap = annotation.getAttributes(); + final Map attributeMap = annotation.getAttributes(); // 记录别名与属性的关系 - ForestMap attributeAliasMappings = new LinkedForestMap<>(false); + final ForestMap attributeAliasMappings = new LinkedForestMap<>(false); attributeMap.forEach((attributeName, attribute) -> { - String alias = Opt.ofNullable(attribute.getAnnotation(Alias.class)) + final String alias = Opt.ofNullable(attribute.getAnnotation(Alias.class)) .map(Alias::value) .orElse(null); if (ObjectUtil.isNull(alias)) { return; } - AnnotationAttribute aliasAttribute = attributeMap.get(alias); + final 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) + final AnnotationAttribute resolvedAttribute = 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()), + ObjectUtil.isNull(resolvedAttribute) + || ClassUtil.isAssignable(attribute.getAttributeType(), resolvedAttribute.getAttributeType()), "return type of the root alias method [{}] is inconsistent with the original [{}]", - resolvedAttributeMethod.getClass(), attribute.getAttribute().getReturnType() + resolvedAttribute.getClass(), attribute.getAttributeType() ); - attributeMap.put(attributeName, resolvedAttributeMethod); + attributeMap.put(attributeName, resolvedAttribute); }); annotation.setAttributes(attributeMap); } diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AliasFor.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AliasFor.java new file mode 100644 index 000000000..188b1e509 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AliasFor.java @@ -0,0 +1,35 @@ +package cn.hutool.core.annotation; + +import java.lang.annotation.*; + +/** + *

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

    + *
  • 当“原始属性”为默认值时,获取“关联属性”将返回“关联属性”本身的值;
  • + *
  • 当“原始属性”不为默认值时,获取“关联属性”将返回“原始属性”的值;
  • + *
+ * 注意,该注解与{@link Link}或{@link MirrorFor}一起使用时,将只有被声明在最上面的注解会生效 + * + * @author huangchengxing + * @see Link + * @see RelationType#ALIAS_FOR + */ +@Link(type = RelationType.ALIAS_FOR) +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) +public @interface AliasFor { + + /** + * 产生关联的注解类型,当不指定时,默认指注释的属性所在的类 + */ + @Link(annotation = Link.class, attribute = "annotation", type = RelationType.FORCE_ALIAS_FOR) + Class annotation() default Annotation.class; + + /** + * {@link #annotation()}指定注解中关联的属性 + */ + @Link(annotation = Link.class, attribute = "attribute", type = RelationType.FORCE_ALIAS_FOR) + String attribute() default ""; + +} 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 index a556e5abf..d775d6ab8 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/AliasForLinkAttributePostProcessor.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AliasForLinkAttributePostProcessor.java @@ -1,9 +1,11 @@ package cn.hutool.core.annotation; +import cn.hutool.core.lang.Opt; import cn.hutool.core.util.ObjectUtil; import java.util.HashMap; import java.util.Map; +import java.util.function.BinaryOperator; /** * 处理注解中带有{@link Link}注解,且{@link Link#type()}为{@link RelationType#FORCE_ALIAS_FOR} @@ -22,7 +24,7 @@ public class AliasForLinkAttributePostProcessor implements SynthesizedAnnotation @Override public void process(SynthesizedAnnotation annotation, SyntheticAnnotation syntheticAnnotation) { - Map attributeMap = new HashMap<>(annotation.getAttributes()); + final Map attributeMap = new HashMap<>(annotation.getAttributes()); attributeMap.forEach((originalAttributeName, originalAttribute) -> { // 获取注解 final Link link = SyntheticAnnotationUtil.getLink( @@ -33,7 +35,7 @@ public class AliasForLinkAttributePostProcessor implements SynthesizedAnnotation } // 获取注解属性 - SynthesizedAnnotation aliasAnnotation = SyntheticAnnotationUtil.getLinkedAnnotation(link, syntheticAnnotation, annotation.annotationType()); + final SynthesizedAnnotation aliasAnnotation = SyntheticAnnotationUtil.getLinkedAnnotation(link, syntheticAnnotation, annotation.annotationType()); if (ObjectUtil.isNull(aliasAnnotation)) { return; } @@ -43,12 +45,40 @@ public class AliasForLinkAttributePostProcessor implements SynthesizedAnnotation // aliasFor if (RelationType.ALIAS_FOR.equals(link.type())) { - aliasAnnotation.setAttributes(aliasAttribute.getAttributeName(), new AliasedAnnotationAttribute(aliasAttribute, originalAttribute)); + wrappingLinkedAttribute(syntheticAnnotation, originalAttribute, aliasAttribute, AliasedAnnotationAttribute::new); return; } // forceAliasFor - aliasAnnotation.setAttributes(aliasAttribute.getAttributeName(), new ForceAliasedAnnotationAttribute(aliasAttribute, originalAttribute)); + wrappingLinkedAttribute(syntheticAnnotation, originalAttribute, aliasAttribute, ForceAliasedAnnotationAttribute::new); }); } + /** + * 对指定注解属性进行包装,若该属性已被包装过,则递归以其为根节点的树结构,对树上全部的叶子节点进行包装 + */ + private void wrappingLinkedAttribute( + SyntheticAnnotation syntheticAnnotation, AnnotationAttribute originalAttribute, AnnotationAttribute aliasAttribute, BinaryOperator wrapping) { + // 不是包装属性 + if (!aliasAttribute.isWrapped()) { + processAttribute(syntheticAnnotation, originalAttribute, aliasAttribute, wrapping); + return; + } + // 是包装属性 + final AbstractAnnotationAttributeWrapper wrapper = (AbstractAnnotationAttributeWrapper)aliasAttribute; + wrapper.getAllLinkedNonWrappedAttributes().forEach( + t -> processAttribute(syntheticAnnotation, originalAttribute, t, wrapping) + ); + } + + /** + * 获取指定注解属性,然后将其再进行一层包装 + */ + private void processAttribute( + SyntheticAnnotation syntheticAnnotation, AnnotationAttribute originalAttribute, + AnnotationAttribute target, BinaryOperator wrapping) { + Opt.ofNullable(target.getAnnotationType()) + .map(syntheticAnnotation::getSynthesizedAnnotation) + .ifPresent(t -> t.replaceAttribute(target.getAttributeName(), old -> wrapping.apply(old, 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 index 44638308a..36d8dda81 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/AliasedAnnotationAttribute.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AliasedAnnotationAttribute.java @@ -1,29 +1,37 @@ package cn.hutool.core.annotation; -import cn.hutool.core.lang.Assert; - /** *

表示一个具有别名的属性。 * 当别名属性值为默认值时,优先返回原属性的值,当别名属性不为默认值时,优先返回别名属性的值 * * @author huangchengxing * @see AliasForLinkAttributePostProcessor - * @see RelationType#ALIAS_BY + * @see RelationType#FORCE_ALIAS_FOR * @see RelationType#ALIAS_FOR */ -public class AliasedAnnotationAttribute extends AnnotationAttributeWrapper implements AnnotationAttribute { +public class AliasedAnnotationAttribute extends AbstractAnnotationAttributeWrapper { - private final AnnotationAttribute aliasAttribute; - - protected AliasedAnnotationAttribute(AnnotationAttribute origin, AnnotationAttribute aliasAttribute) { - super(origin); - Assert.notNull(aliasAttribute, "aliasAttribute must not null"); - this.aliasAttribute = aliasAttribute; + protected AliasedAnnotationAttribute(AnnotationAttribute origin, AnnotationAttribute linked) { + super(origin, linked); } + /** + * 若{@link #linked}为默认值,则返回{@link #original}的值,否则返回{@link #linked}的值 + * + * @return 属性值 + */ @Override public Object getValue() { - return aliasAttribute.isValueEquivalentToDefaultValue() ? super.getValue() : aliasAttribute.getValue(); + return linked.isValueEquivalentToDefaultValue() ? super.getValue() : linked.getValue(); } + /** + * 当{@link #original}与{@link #linked}都为默认值时返回{@code true} + * + * @return 是否 + */ + @Override + public boolean isValueEquivalentToDefaultValue() { + return linked.isValueEquivalentToDefaultValue() && original.isValueEquivalentToDefaultValue(); + } } 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 index a7b9627b5..aface5b92 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationAttribute.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationAttribute.java @@ -16,8 +16,9 @@ import java.lang.reflect.Method; * * @author huangchengxing * @see SynthesizedAnnotationPostProcessor - * @see CacheableAnnotationAttribute * @see AnnotationAttributeWrapper + * @see CacheableAnnotationAttribute + * @see AbstractAnnotationAttributeWrapper * @see ForceAliasedAnnotationAttribute * @see AliasedAnnotationAttribute * @see MirroredAnnotationAttribute @@ -38,6 +39,15 @@ public interface AnnotationAttribute { */ Method getAttribute(); + /** + * 获取声明属性的注解类 + * + * @return 声明注解的注解类 + */ + default Class getAnnotationType() { + return getAttribute().getDeclaringClass(); + } + /** * 获取属性名称 * @@ -90,7 +100,7 @@ public interface AnnotationAttribute { * @return boolean */ default boolean isWrapped() { - return this instanceof AnnotationAttributeWrapper; + return false; } } 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 index f1ec42cd8..54caa5d98 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationAttributeWrapper.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationAttributeWrapper.java @@ -1,14 +1,25 @@ package cn.hutool.core.annotation; -import cn.hutool.core.lang.Assert; - import java.lang.annotation.Annotation; import java.lang.reflect.Method; +import java.util.Collection; /** *

表示一个被包装过的{@link AnnotationAttribute}, - * 该实例中的一些方法可能会被代理到其他的注解属性对象中, - * 从而使得通过原始的注解属性的方法获取到另一注解属性的值。 + * 该实例中的一些方法可能会被代理到另一个注解属性对象中, + * 从而使得通过原始的注解属性的方法获取到另一注解属性的值。
+ * 除了{@link #getValue()}以外,其他方法的返回值应当尽可能与{@link #getOriginal()} + * 返回的{@link AnnotationAttribute}对象的方法返回值一致。 + * + *

当包装类被包装了多层后,则规则生效优先级按包装的先后顺序倒序排序, + * 比如a、b互为镜像,此时a、b两属性应当都被{@link MirroredAnnotationAttribute}包装, + * 若再指定c为a的别名字段,则c、a、b都要在原基础上再次包装一层{@link AliasedAnnotationAttribute}。
+ * 此时a、b同时被包装了两层,则执行时,优先执行{@link AliasedAnnotationAttribute}的逻辑, + * 当该规则不生效时,比如c只有默认值,此时上一次的{@link MirroredAnnotationAttribute}的逻辑才会生效。 + * + *

被包装的{@link AnnotationAttribute}实际结构为一颗二叉树, + * 当包装类再次被包装时,实际上等于又添加了一个新的根节点, + * 此时需要同时更新树的全部关联叶子节点。 * * @author huangchengxing * @see AnnotationAttribute @@ -16,42 +27,101 @@ import java.lang.reflect.Method; * @see AliasedAnnotationAttribute * @see MirroredAnnotationAttribute */ -public abstract class AnnotationAttributeWrapper implements AnnotationAttribute { +public interface AnnotationAttributeWrapper extends AnnotationAttribute { - protected final AnnotationAttribute origin; + // =========================== 新增方法 =========================== - protected AnnotationAttributeWrapper(AnnotationAttribute origin) { - Assert.notNull(origin, "target must not null"); - this.origin = origin; + /** + * 获取被包装的{@link AnnotationAttribute}对象,该对象也可能是{@link AnnotationAttribute} + * + * @return 被包装的{@link AnnotationAttribute}对象 + */ + AnnotationAttribute getOriginal(); + + /** + * 获取最初的被包装的{@link AnnotationAttribute} + * + * @return 最初的被包装的{@link AnnotationAttribute} + */ + AnnotationAttribute getNonWrappedOriginal(); + + /** + * 获取包装{@link #getOriginal()}的{@link AnnotationAttribute}对象,该对象也可能是{@link AnnotationAttribute} + * + * @return 包装对象 + */ + AnnotationAttribute getLinked(); + + /** + * 遍历以当前实例为根节点的树结构,获取所有未被包装的属性 + * + * @return 叶子节点 + */ + Collection getAllLinkedNonWrappedAttributes(); + + // =========================== 代理实现 =========================== + + /** + * 获取注解对象 + * + * @return 注解对象 + */ + @Override + default Annotation getAnnotation() { + return getOriginal().getAnnotation(); } + /** + * 获取注解属性对应的方法 + * + * @return 注解属性对应的方法 + */ @Override - public Annotation getAnnotation() { - return origin.getAnnotation(); + default Method getAttribute() { + return getOriginal().getAttribute(); } + /** + * 该注解属性的值是否等于默认值
+ * 默认仅当{@link #getOriginal()}与{@link #getLinked()}返回的注解属性 + * 都为默认值时,才返回{@code true} + * + * @return 该注解属性的值是否等于默认值 + */ @Override - public Method getAttribute() { - return origin.getAttribute(); + default boolean isValueEquivalentToDefaultValue() { + return getOriginal().isValueEquivalentToDefaultValue() && getLinked().isValueEquivalentToDefaultValue(); } + /** + * 获取属性类型 + * + * @return 属性类型 + */ @Override - public boolean isValueEquivalentToDefaultValue() { - return origin.isValueEquivalentToDefaultValue(); + default Class getAttributeType() { + return getOriginal().getAttributeType(); } + /** + * 获取属性上的注解 + * + * @param annotationType 注解类型 + * @return 注解对象 + */ @Override - public Class getAttributeType() { - return origin.getAttributeType(); + default T getAnnotation(Class annotationType) { + return getOriginal().getAnnotation(annotationType); } + /** + * 当前注解属性是否已经被{@link AnnotationAttributeWrapper}包装 + * + * @return boolean + */ @Override - public T getAnnotation(Class annotationType) { - return origin.getAnnotation(annotationType); - } - - @Override - public boolean isWrapped() { + default boolean isWrapped() { return true; } + } 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 869412563..08c40fcb0 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 @@ -359,7 +359,8 @@ public class AnnotationUtil { * @see SyntheticAnnotation */ public static T getSynthesisAnnotation(Annotation annotation, Class annotationType) { - return SyntheticAnnotation.of(annotation).getAnnotation(annotationType); + // TODO 缓存合成注解信息,避免重复解析 + return SyntheticAnnotation.of(annotation).syntheticAnnotation(annotationType); } /** @@ -385,6 +386,30 @@ public class AnnotationUtil { .collect(Collectors.toList()); } + /** + * 获取元素上距离指定元素最接近的合成注解 + *

    + *
  • 若元素是类,则递归解析全部父类和全部父接口上的注解;
  • + *
  • 若元素是方法、属性或注解,则只解析其直接声明的注解;
  • + *
+ * + * @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission + * @param annotationType 注解类 + * @param 注解类型 + * @return 合成注解 + * @see SyntheticAnnotation + */ + public static T getSyntheticAnnotation(AnnotatedElement annotatedEle, Class annotationType) { + AnnotationScanner[] scanners = new AnnotationScanner[]{ + new MetaAnnotationScanner(), new TypeAnnotationScanner(), new MethodAnnotationScanner(), new FieldAnnotationScanner() + }; + return AnnotationScanner.scanByAnySupported(annotatedEle, scanners).stream() + .map(annotation -> getSynthesisAnnotation(annotation, annotationType)) + .filter(Objects::nonNull) + .findFirst() + .orElse(null); + } + /** * 扫描注解类,以及注解类的{@link Class}层级结构中的注解,将返回除了{@link #META_ANNOTATIONS}中指定的JDK默认注解外, * 按元注解对象与{@code annotationType}的距离和{@link Class#getAnnotations()}顺序排序的注解对象集合 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 bfaa3b493..4fd2ffd1d 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 @@ -8,7 +8,7 @@ import java.util.Collection; import java.util.Comparator; /** - * 带缓存功能的{@link SynthesizedAnnotationAttributeProcessor}实现, + *

带缓存功能的{@link SynthesizedAnnotationAttributeProcessor}实现, * 构建时需要传入比较器,获取属性值时将根据比较器对合成注解进行排序, * 然后选择具有所需属性的,排序最靠前的注解用于获取属性值 * 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 index 8349a3bb6..72318ffba 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/ForceAliasedAnnotationAttribute.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/ForceAliasedAnnotationAttribute.java @@ -1,40 +1,49 @@ package cn.hutool.core.annotation; -import cn.hutool.core.lang.Assert; - /** * 表示一个被指定了强制别名的注解属性。 - * 当调用{@link #getValue()}时,总是返回{@link #aliasAttribute}的值 + * 当调用{@link #getValue()}时,总是返回{@link #linked}的值 * * @author huangchengxing * @see AliasAttributePostProcessor * @see AliasForLinkAttributePostProcessor - * @see RelationType#FORCE_ALIAS_BY + * @see RelationType#ALIAS_FOR * @see RelationType#FORCE_ALIAS_FOR */ -public class ForceAliasedAnnotationAttribute extends AnnotationAttributeWrapper implements AnnotationAttribute { +public class ForceAliasedAnnotationAttribute extends AbstractAnnotationAttributeWrapper { - private final AnnotationAttribute aliasAttribute; - - protected ForceAliasedAnnotationAttribute(AnnotationAttribute origin, AnnotationAttribute aliasAttribute) { - super(origin); - Assert.notNull(aliasAttribute, "aliasAttribute must not null"); - this.aliasAttribute = aliasAttribute; + protected ForceAliasedAnnotationAttribute(AnnotationAttribute origin, AnnotationAttribute linked) { + super(origin, linked); } + /** + * 总是返回{@link #linked}的{@link AnnotationAttribute#getValue()}的返回值 + * + * @return {@link #linked}的{@link AnnotationAttribute#getValue()}的返回值 + */ @Override public Object getValue() { - return aliasAttribute.getValue(); + return linked.getValue(); } + /** + * 总是返回{@link #linked}的{@link AnnotationAttribute#isValueEquivalentToDefaultValue()}的返回值 + * + * @return {@link #linked}的{@link AnnotationAttribute#isValueEquivalentToDefaultValue()}的返回值 + */ @Override public boolean isValueEquivalentToDefaultValue() { - return aliasAttribute.isValueEquivalentToDefaultValue(); + return linked.isValueEquivalentToDefaultValue(); } + /** + * 总是返回{@link #linked}的{@link AnnotationAttribute#getAttributeType()}的返回值 + * + * @return {@link #linked}的{@link AnnotationAttribute#getAttributeType()}的返回值 + */ @Override public Class getAttributeType() { - return aliasAttribute.getAttributeType(); + return linked.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 index a2e4a9679..d2f8ecacd 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/Link.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/Link.java @@ -5,7 +5,7 @@ import java.lang.annotation.*; /** *

用于在同一注解中,或具有一定关联的不同注解的属性中,表明这些属性之间具有特定的关联关系。 * 在通过{@link SyntheticAnnotation}获取合成注解后,合成注解获取属性值时会根据该注解进行调整。
- * 注意:该注解的优先级低于{@link Alias} + * 注意:该注解的优先级低于{@link Alias};且与{@link MirrorFor}或{@link AliasFor}一起使用时,将只有被声明在最上面的注解会生效 * * @author huangchengxing * @see SyntheticAnnotation diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/MirrorFor.java b/hutool-core/src/main/java/cn/hutool/core/annotation/MirrorFor.java new file mode 100644 index 000000000..99ffdab4b --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/MirrorFor.java @@ -0,0 +1,38 @@ +package cn.hutool.core.annotation; + +import java.lang.annotation.*; + +/** + *

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

    + *
  • 互为镜像的两个属性,必须同时通过指定模式为{@code MIRROR_FOR}的{@link Link}注解指定对方;
  • + *
  • 互为镜像的两个属性,类型必须一致;
  • + *
  • 互为镜像的两个属性在获取值,且两者的值皆不同时,必须且仅允许有一个非默认值,该值被优先返回;
  • + *
  • 互为镜像的两个属性,在值都为默认值或都不为默认值时,两者的值必须相等;
  • + *
+ * 注意,该注解与{@link Link}或{@link AliasFor}一起使用时,将只有被声明在最上面的注解会生效 + * + * @author huangchengxing + * @see Link + * @see RelationType#MIRROR_FOR + */ +@Link(type = RelationType.MIRROR_FOR) +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) +public @interface MirrorFor { + + /** + * 产生关联的注解类型,当不指定时,默认指注释的属性所在的类 + */ + @Link(annotation = Link.class, attribute = "annotation", type = RelationType.FORCE_ALIAS_FOR) + Class annotation() default Annotation.class; + + /** + * {@link #annotation()}指定注解中关联的属性 + */ + @Link(annotation = Link.class, attribute = "attribute", type = RelationType.FORCE_ALIAS_FOR) + String attribute() default ""; + +} 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 index 352203382..7c370ff5a 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/MirrorLinkAttributePostProcessor.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/MirrorLinkAttributePostProcessor.java @@ -46,10 +46,10 @@ public class MirrorLinkAttributePostProcessor implements SynthesizedAnnotationPo // 包装这一对镜像属性,并替换原注解中的对应属性 final AnnotationAttribute mirroredOriginalAttribute = new MirroredAnnotationAttribute(originalAttribute, mirrorAttribute); - syntheticAnnotation.getSynthesizedAnnotation(originalAttribute.getAttribute().getDeclaringClass()) - .setAttributes(originalAttributeName, mirroredOriginalAttribute); + syntheticAnnotation.getSynthesizedAnnotation(originalAttribute.getAnnotationType()) + .setAttribute(originalAttributeName, mirroredOriginalAttribute); final AnnotationAttribute mirroredTargetAttribute = new MirroredAnnotationAttribute(mirrorAttribute, originalAttribute); - mirrorAnnotation.setAttributes(link.attribute(), mirroredTargetAttribute); + mirrorAnnotation.setAttribute(link.attribute(), mirroredTargetAttribute); }); } @@ -59,7 +59,7 @@ public class MirrorLinkAttributePostProcessor implements SynthesizedAnnotationPo // 镜像属性返回值必须一致 SyntheticAnnotationUtil.checkAttributeType(original, mirror); // 镜像属性上必须存在对应的注解 - final Link mirrorAttributeAnnotation = mirror.getAnnotation(Link.class); + final Link mirrorAttributeAnnotation = SyntheticAnnotationUtil.getLink(mirror, RelationType.MIRROR_FOR); 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 [{}]", 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 index 2dcd0dd1d..20d2305ce 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/MirroredAnnotationAttribute.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/MirroredAnnotationAttribute.java @@ -9,28 +9,25 @@ import cn.hutool.core.lang.Assert; * @see MirrorLinkAttributePostProcessor * @see RelationType#MIRROR_FOR */ -public class MirroredAnnotationAttribute extends AnnotationAttributeWrapper implements AnnotationAttribute { +public class MirroredAnnotationAttribute extends AbstractAnnotationAttributeWrapper { - private final AnnotationAttribute mirrorAttribute; - - public MirroredAnnotationAttribute(AnnotationAttribute origin, AnnotationAttribute mirrorAttribute) { - super(origin); - this.mirrorAttribute = mirrorAttribute; + public MirroredAnnotationAttribute(AnnotationAttribute origin, AnnotationAttribute linked) { + super(origin, linked); } @Override public Object getValue() { - boolean originIsDefault = origin.isValueEquivalentToDefaultValue(); - boolean targetIsDefault = mirrorAttribute.isValueEquivalentToDefaultValue(); - Object originValue = origin.getValue(); - Object targetValue = mirrorAttribute.getValue(); + final boolean originIsDefault = original.isValueEquivalentToDefaultValue(); + final boolean targetIsDefault = linked.isValueEquivalentToDefaultValue(); + final Object originValue = original.getValue(); + final Object targetValue = linked.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 + original.getAttribute(), linked.getAttribute(), originValue, targetValue ); return originValue; } @@ -38,4 +35,14 @@ public class MirroredAnnotationAttribute extends AnnotationAttributeWrapper impl // 两者有一者不为默认值时,优先返回非默认值 return originIsDefault ? targetValue : originValue; } + + /** + * 当{@link #original}与{@link #linked}都为默认值时返回{@code true} + * + * @return 是否 + */ + @Override + public boolean isValueEquivalentToDefaultValue() { + return original.isValueEquivalentToDefaultValue() && linked.isValueEquivalentToDefaultValue(); + } } 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 ad016430e..5ff9e9bb3 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 @@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil; import java.lang.annotation.Annotation; import java.util.Map; +import java.util.function.UnaryOperator; /** * 用于在{@link SyntheticAnnotation}中表示一个处于合成状态的注解对象 @@ -82,7 +83,7 @@ public interface SynthesizedAnnotation extends Annotation { */ default void setAttributes(Map attributes) { if (CollUtil.isNotEmpty(attributes)) { - attributes.forEach(this::setAttributes); + attributes.forEach(this::setAttribute); } } @@ -92,7 +93,15 @@ public interface SynthesizedAnnotation extends Annotation { * @param attributeName 属性名称 * @param attribute 注解属性 */ - void setAttributes(String attributeName, AnnotationAttribute attribute); + void setAttribute(String attributeName, AnnotationAttribute attribute); + + /** + * 替换属性值 + * + * @param attributeName 属性名 + * @param operator 替换操作 + */ + void replaceAttribute(String attributeName, UnaryOperator operator); /** * 获取属性值 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 c8b5513e3..e71208c8a 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 @@ -88,7 +88,7 @@ class SyntheticAnnotationProxy implements InvocationHandler { methods.put("getHorizontalDistance", (method, args) -> annotation.getHorizontalDistance()); methods.put("hasAttribute", (method, args) -> annotation.hasAttribute((String)args[0], (Class)args[1])); methods.put("getAttributes", (method, args) -> annotation.getAttributes()); - methods.put("setAttributes", (method, args) -> { + methods.put("setAttribute", (method, args) -> { throw new UnsupportedOperationException("proxied annotation can not reset attributes"); }); methods.put("getAttributeValue", (method, args) -> annotation.getAttributeValue((String)args[0])); 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 index 1cec36aaa..a007adc75 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticAnnotationUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticAnnotationUtil.java @@ -23,7 +23,7 @@ class SyntheticAnnotationUtil { */ static Link getLink(AnnotationAttribute attribute, RelationType... relationTypes) { return Opt.ofNullable(attribute) - .map(t -> t.getAnnotation(Link.class)) + .map(t -> AnnotationUtil.getSyntheticAnnotation(attribute.getAttribute(), Link.class)) .filter(a -> ArrayUtil.contains(relationTypes, a.type())) .get(); } 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 3299421ea..11b04a714 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 @@ -10,6 +10,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; import java.util.*; +import java.util.function.UnaryOperator; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -117,6 +118,7 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation { this.metaAnnotationMap = new LinkedHashMap<>(); // 初始化元注解信息,并进行后置处理 + // TODO 缓存元注解信息,避免重复解析 loadMetaAnnotations(); annotationPostProcessors.forEach(processor -> metaAnnotationMap.values().forEach(synthesized -> processor.process(synthesized, this)) @@ -411,10 +413,24 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation { * @param attribute 注解属性 */ @Override - public void setAttributes(String attributeName, AnnotationAttribute attribute) { + public void setAttribute(String attributeName, AnnotationAttribute attribute) { attributeMethodCaches.put(attributeName, attribute); } + /** + * 替换属性值 + * + * @param attributeName 属性名 + * @param operator 替换操作 + */ + @Override + public void replaceAttribute(String attributeName, UnaryOperator operator) { + AnnotationAttribute old = attributeMethodCaches.get(attributeName); + if (ObjectUtil.isNotNull(old)) { + attributeMethodCaches.put(attributeName, operator.apply(old)); + } + } + /** * 获取属性值 * 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 31403ed79..1deacec39 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 @@ -1,5 +1,6 @@ package cn.hutool.core.annotation; +import cn.hutool.core.util.ReflectUtil; import org.junit.Assert; import org.junit.Test; @@ -7,6 +8,7 @@ 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; /** * 合成注解{@link SyntheticMetaAnnotation}的测试用例 @@ -53,6 +55,15 @@ public class SyntheticMetaAnnotationTest { Assert.assertThrows(IllegalArgumentException.class, () -> new SyntheticMetaAnnotation(grandParentAnnotation)); } + @Test + public void linkTest() { + Method method = ReflectUtil.getMethod(AnnotationForLinkTest.class, "value"); + SyntheticAnnotation syntheticAnnotation = new SyntheticMetaAnnotation(method.getAnnotation(AliasFor.class)); + Link link = syntheticAnnotation.syntheticAnnotation(Link.class); + Assert.assertEquals(AnnotationForLinkTest.class, link.annotation()); + Assert.assertEquals("name", link.attribute()); + } + @Test public void mirrorAttributeTest() { AnnotationForMirrorTest annotation = ClassForMirrorTest.class.getAnnotation(AnnotationForMirrorTest.class); @@ -108,6 +119,31 @@ public class SyntheticMetaAnnotationTest { Assert.assertEquals("Foo", childAnnotation.value()); } + @Test + public void aliasForAndMirrorTest() { + AnnotationForMirrorThenAliasForTest annotation = ClassForAliasForAndMirrorTest.class.getAnnotation(AnnotationForMirrorThenAliasForTest.class); + SyntheticAnnotation synthetic = new SyntheticMetaAnnotation(annotation); + MetaAnnotationForMirrorThenAliasForTest metaAnnotation = synthetic.syntheticAnnotation(MetaAnnotationForMirrorThenAliasForTest.class); + Assert.assertEquals("test", metaAnnotation.name()); + Assert.assertEquals("test", metaAnnotation.value()); + AnnotationForMirrorThenAliasForTest childAnnotation = synthetic.syntheticAnnotation(AnnotationForMirrorThenAliasForTest.class); + Assert.assertEquals("test", childAnnotation.childValue()); + } + + @Test + public void multiAliasForTest() { + AnnotationForMultiAliasForTest annotation = ClassForMultiAliasForTest.class.getAnnotation(AnnotationForMultiAliasForTest.class); + SyntheticAnnotation synthetic = new SyntheticMetaAnnotation(annotation); + + MetaAnnotationForMultiAliasForTest1 metaAnnotation1 = synthetic.syntheticAnnotation(MetaAnnotationForMultiAliasForTest1.class); + Assert.assertEquals("test", metaAnnotation1.name()); + Assert.assertEquals("test", metaAnnotation1.value1()); + MetaAnnotationForMultiAliasForTest2 metaAnnotation2 = synthetic.syntheticAnnotation(MetaAnnotationForMultiAliasForTest2.class); + Assert.assertEquals("test", metaAnnotation2.value2()); + AnnotationForMultiAliasForTest childAnnotation = synthetic.syntheticAnnotation(AnnotationForMultiAliasForTest.class); + Assert.assertEquals("test", childAnnotation.value3()); + } + // 注解结构如下: // AnnotatedClass -> @ChildAnnotation -> @ParentAnnotation -> @GrandParentAnnotation // -> @GrandParentAnnotation @@ -143,9 +179,11 @@ public class SyntheticMetaAnnotationTest { @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD, ElementType.TYPE }) @interface AnnotationForMirrorTest { - @Link(attribute = "name") + //@Link(attribute = "name") + @MirrorFor(attribute = "name") String value() default ""; - @Link(attribute = "value") + //@Link(attribute = "value") + @MirrorFor(attribute = "value") String name() default ""; } @AnnotationForMirrorTest("Foo") @@ -164,10 +202,9 @@ public class SyntheticMetaAnnotationTest { @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD, ElementType.TYPE }) @interface AnnotationForAliasForTest { - @Link( + @AliasFor( annotation = MetaAnnotationForAliasForTest.class, - attribute = "name", - type = RelationType.ALIAS_FOR + attribute = "name" ) String value() default ""; } @@ -197,4 +234,53 @@ public class SyntheticMetaAnnotationTest { @AnnotationForceForAliasForTest("Foo") static class ClassForForceAliasForTest2 {} + @interface AnnotationForLinkTest { + @AliasFor(attribute = "name", annotation = AnnotationForLinkTest.class) + String value() default "value"; + String name() default "name"; + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.METHOD, ElementType.TYPE }) + @interface MetaAnnotationForMirrorThenAliasForTest { + @MirrorFor(attribute = "value") + String name() default ""; + @MirrorFor(attribute = "name") + String value() default ""; + } + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.METHOD, ElementType.TYPE }) + @MetaAnnotationForMirrorThenAliasForTest("Meta") + @interface AnnotationForMirrorThenAliasForTest { + @AliasFor(attribute = "name", annotation = MetaAnnotationForMirrorThenAliasForTest.class) + String childValue() default "value"; + } + @AnnotationForMirrorThenAliasForTest(childValue = "test") + static class ClassForAliasForAndMirrorTest{} + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.METHOD, ElementType.TYPE }) + @interface MetaAnnotationForMultiAliasForTest1 { + @MirrorFor(attribute = "value1") + String name() default ""; + @MirrorFor(attribute = "name") + String value1() default ""; + } + @MetaAnnotationForMultiAliasForTest1 + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.METHOD, ElementType.TYPE }) + @interface MetaAnnotationForMultiAliasForTest2 { + @AliasFor(attribute = "name", annotation = MetaAnnotationForMultiAliasForTest1.class) + String value2() default ""; + } + @MetaAnnotationForMultiAliasForTest2 + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.METHOD, ElementType.TYPE }) + @interface AnnotationForMultiAliasForTest { + @AliasFor(attribute = "value2", annotation = MetaAnnotationForMultiAliasForTest2.class) + String value3() default "value"; + } + @AnnotationForMultiAliasForTest(value3 = "test") + static class ClassForMultiAliasForTest{} + } From 17b48024ad7bb1832f1d303670ff19222d0b9fd5 Mon Sep 17 00:00:00 2001 From: huangchengxing <841396397@qq.com> Date: Fri, 8 Jul 2022 11:59:44 +0800 Subject: [PATCH 03/15] =?UTF-8?q?=E6=B7=BB=E5=8A=A0@ForceAliasFor=E6=B3=A8?= =?UTF-8?q?=E8=A7=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/hutool/core/annotation/AliasFor.java | 4 +-- .../hutool/core/annotation/ForceAliasFor.java | 31 +++++++++++++++++++ .../java/cn/hutool/core/annotation/Link.java | 11 ++++++- .../cn/hutool/core/annotation/MirrorFor.java | 4 +-- .../SyntheticMetaAnnotationTest.java | 11 ++++--- 5 files changed, 51 insertions(+), 10 deletions(-) create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/ForceAliasFor.java diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AliasFor.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AliasFor.java index 188b1e509..fa4020c99 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/AliasFor.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AliasFor.java @@ -3,12 +3,12 @@ package cn.hutool.core.annotation; import java.lang.annotation.*; /** - *

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

{@link Link}的子注解。表示“原始属性”将作为“关联属性”的别名。 *

    *
  • 当“原始属性”为默认值时,获取“关联属性”将返回“关联属性”本身的值;
  • *
  • 当“原始属性”不为默认值时,获取“关联属性”将返回“原始属性”的值;
  • *
- * 注意,该注解与{@link Link}或{@link MirrorFor}一起使用时,将只有被声明在最上面的注解会生效 + * 注意,该注解与{@link Link}、{@link ForceAliasFor}或{@link MirrorFor}一起使用时,将只有被声明在最上面的注解会生效 * * @author huangchengxing * @see Link diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/ForceAliasFor.java b/hutool-core/src/main/java/cn/hutool/core/annotation/ForceAliasFor.java new file mode 100644 index 000000000..cd36f56d0 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/ForceAliasFor.java @@ -0,0 +1,31 @@ +package cn.hutool.core.annotation; + +import java.lang.annotation.*; + +/** + *

{@link Link}的子注解。表示“原始属性”将强制作为“关联属性”的别名。效果等同于在“原始属性”上添加{@link Alias}注解, + * 任何情况下,获取“关联属性”的值都将直接返回“原始属性”的值 + * 注意,该注解与{@link Link}、{@link AliasFor}或{@link MirrorFor}一起使用时,将只有被声明在最上面的注解会生效 + * + * @author huangchengxing + * @see Link + * @see RelationType#FORCE_ALIAS_FOR + */ +@Link(type = RelationType.FORCE_ALIAS_FOR) +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) +public @interface ForceAliasFor { + + /** + * 产生关联的注解类型,当不指定时,默认指注释的属性所在的类 + */ + @Link(annotation = Link.class, attribute = "annotation", type = RelationType.FORCE_ALIAS_FOR) + Class annotation() default Annotation.class; + + /** + * {@link #annotation()}指定注解中关联的属性 + */ + @Link(annotation = Link.class, attribute = "attribute", type = RelationType.FORCE_ALIAS_FOR) + String attribute() default ""; +} 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 index d2f8ecacd..cb841fe79 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/Link.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/Link.java @@ -5,11 +5,20 @@ import java.lang.annotation.*; /** *

用于在同一注解中,或具有一定关联的不同注解的属性中,表明这些属性之间具有特定的关联关系。 * 在通过{@link SyntheticAnnotation}获取合成注解后,合成注解获取属性值时会根据该注解进行调整。
- * 注意:该注解的优先级低于{@link Alias};且与{@link MirrorFor}或{@link AliasFor}一起使用时,将只有被声明在最上面的注解会生效 + * + *

该注解存在三个字注解:{@link MirrorFor}、{@link ForceAliasFor}或{@link AliasFor}, + * 使用三个子注解等同于{@link Link}。但是需要注意的是, + * 当注解中的属性同时存在多个{@link Link}或基于{@link Link}的子注解时, + * 仅有声明在被注解的属性最上方的注解会生效,其余注解都将被忽略。 + * + * 注意:该注解的优先级低于{@link Alias} * * @author huangchengxing * @see SyntheticAnnotation * @see RelationType + * @see AliasFor + * @see MirrorFor + * @see ForceAliasFor */ @Documented @Retention(RetentionPolicy.RUNTIME) diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/MirrorFor.java b/hutool-core/src/main/java/cn/hutool/core/annotation/MirrorFor.java index 99ffdab4b..e006b6e79 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/MirrorFor.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/MirrorFor.java @@ -3,7 +3,7 @@ package cn.hutool.core.annotation; import java.lang.annotation.*; /** - *

表示注解的属性与指定的属性互为镜像,通过一个属性将能够获得对方的值。
+ *

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

    *
  • 互为镜像的两个属性,必须同时通过指定模式为{@code MIRROR_FOR}的{@link Link}注解指定对方;
  • @@ -11,7 +11,7 @@ import java.lang.annotation.*; *
  • 互为镜像的两个属性在获取值,且两者的值皆不同时,必须且仅允许有一个非默认值,该值被优先返回;
  • *
  • 互为镜像的两个属性,在值都为默认值或都不为默认值时,两者的值必须相等;
  • *
- * 注意,该注解与{@link Link}或{@link AliasFor}一起使用时,将只有被声明在最上面的注解会生效 + * 注意,该注解与{@link Link}、{@link ForceAliasFor}或{@link AliasFor}一起使用时,将只有被声明在最上面的注解会生效 * * @author huangchengxing * @see Link 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 1deacec39..dbc7964f5 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 @@ -222,11 +222,12 @@ public class SyntheticMetaAnnotationTest { @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD, ElementType.TYPE }) @interface AnnotationForceForAliasForTest { - @Link( - annotation = MetaAnnotationForForceAliasForTest.class, - attribute = "name", - type = RelationType.FORCE_ALIAS_FOR - ) + //@Link( + // annotation = MetaAnnotationForForceAliasForTest.class, + // attribute = "name", + // type = RelationType.FORCE_ALIAS_FOR + //) + @ForceAliasFor(annotation = MetaAnnotationForForceAliasForTest.class, attribute = "name") String value() default ""; } @AnnotationForceForAliasForTest From eae76eb275b8dca2803602a8f8a1c6f478431766 Mon Sep 17 00:00:00 2001 From: huangchengxing <841396397@qq.com> Date: Wed, 13 Jul 2022 22:34:13 +0800 Subject: [PATCH 04/15] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=EF=BC=8C=E4=BF=AE=E5=A4=8D=E4=B8=8D=E5=87=86=E7=A1=AE=E7=9A=84?= =?UTF-8?q?=E6=B3=A8=E9=87=8A=EF=BC=8C=E5=AE=8C=E5=96=84=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AliasForLinkAttributePostProcessor.java | 21 +++ ...nthesizedAnnotationAttributeProcessor.java | 15 ++ .../annotation/SynthesizedAnnotation.java | 19 ++- .../SynthesizedAnnotationPostProcessor.java | 11 ++ .../core/annotation/SyntheticAnnotation.java | 7 + .../annotation/SyntheticAnnotationUtil.java | 30 +++- .../annotation/SyntheticMetaAnnotation.java | 67 ++++---- ...bstractAnnotationAttributeWrapperTest.java | 99 ++++++++++++ .../AliasedAnnotationAttributeTest.java | 76 +++++++++ .../CacheableAnnotationAttributeTest.java | 61 ++++++++ .../ForceAliasedAnnotationAttributeTest.java | 76 +++++++++ .../MirroredAnnotationAttributeTest.java | 76 +++++++++ .../SynthesizedAnnotationSelectorTest.java | 146 ++++++++++++++++++ .../SyntheticMetaAnnotationTest.java | 93 ++++++++--- 14 files changed, 743 insertions(+), 54 deletions(-) create mode 100644 hutool-core/src/test/java/cn/hutool/core/annotation/AbstractAnnotationAttributeWrapperTest.java create mode 100644 hutool-core/src/test/java/cn/hutool/core/annotation/AliasedAnnotationAttributeTest.java create mode 100644 hutool-core/src/test/java/cn/hutool/core/annotation/CacheableAnnotationAttributeTest.java create mode 100644 hutool-core/src/test/java/cn/hutool/core/annotation/ForceAliasedAnnotationAttributeTest.java create mode 100644 hutool-core/src/test/java/cn/hutool/core/annotation/MirroredAnnotationAttributeTest.java create mode 100644 hutool-core/src/test/java/cn/hutool/core/annotation/SynthesizedAnnotationSelectorTest.java 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 index d775d6ab8..dcaff1438 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/AliasForLinkAttributePostProcessor.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AliasForLinkAttributePostProcessor.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.ObjectUtil; @@ -42,6 +43,7 @@ public class AliasForLinkAttributePostProcessor implements SynthesizedAnnotation final AnnotationAttribute aliasAttribute = aliasAnnotation.getAttributes().get(link.attribute()); SyntheticAnnotationUtil.checkLinkedAttributeNotNull(originalAttribute, aliasAttribute, link); SyntheticAnnotationUtil.checkAttributeType(originalAttribute, aliasAttribute); + checkCircularDependency(originalAttribute, aliasAttribute); // aliasFor if (RelationType.ALIAS_FOR.equals(link.type())) { @@ -81,4 +83,23 @@ public class AliasForLinkAttributePostProcessor implements SynthesizedAnnotation .ifPresent(t -> t.replaceAttribute(target.getAttributeName(), old -> wrapping.apply(old, originalAttribute))); } + /** + * 检查两个属性是否互为别名 + */ + private void checkCircularDependency(AnnotationAttribute original, AnnotationAttribute alias) { + Link annotation = SyntheticAnnotationUtil.getLink(alias, RelationType.ALIAS_FOR, RelationType.FORCE_ALIAS_FOR); + if (ObjectUtil.isNull(annotation)) { + return; + } + final Class aliasAnnotationType = SyntheticAnnotationUtil.getLinkedAnnotationType(annotation, alias.getAnnotationType()); + if (ObjectUtil.notEqual(aliasAnnotationType, original.getAnnotationType())) { + return; + } + Assert.notEquals( + annotation.attribute(), original.getAttributeName(), + "circular reference between the alias attribute [{}] and the original attribute [{}]", + alias.getAttribute(), original.getAttribute() + ); + } + } 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 4fd2ffd1d..520d60a90 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 @@ -1,5 +1,6 @@ package cn.hutool.core.annotation; +import cn.hutool.core.lang.Assert; import cn.hutool.core.map.multi.RowKeyTable; import cn.hutool.core.map.multi.Table; import cn.hutool.core.util.ObjectUtil; @@ -12,6 +13,10 @@ import java.util.Comparator; * 构建时需要传入比较器,获取属性值时将根据比较器对合成注解进行排序, * 然后选择具有所需属性的,排序最靠前的注解用于获取属性值 * + *

通过该处理器获取合成注解属性值时会出现隐式别名, + * 即子注解和元注解中同时存在类型和名称皆相同的属性时,元注解中属性总是会被该属性覆盖, + * 并且该覆盖关系并不会通过{@link Alias}或{@link Link}被传递到关联的属性中。 + * * @author huangchengxing */ public class CacheableSynthesizedAnnotationAttributeProcessor implements SynthesizedAnnotationAttributeProcessor { @@ -25,9 +30,19 @@ public class CacheableSynthesizedAnnotationAttributeProcessor implements Synthes * @param annotationComparator 注解比较器,排序更靠前的注解将被优先用于获取值 */ public CacheableSynthesizedAnnotationAttributeProcessor(Comparator annotationComparator) { + Assert.notNull(annotationComparator, "annotationComparator must not null"); this.annotationComparator = annotationComparator; } + /** + * 创建一个带缓存的注解值选择器, + * 默认按{@link SynthesizedAnnotation#getVerticalDistance()}和{@link SynthesizedAnnotation#getHorizontalDistance()}排序, + * 越靠前的越优先被取值。 + */ + public CacheableSynthesizedAnnotationAttributeProcessor() { + this(SyntheticAnnotationUtil.getChildPriorityAnnotationCompare()); + } + @SuppressWarnings("unchecked") @Override public T getAttributeValue(String attributeName, Class attributeType, Collection synthesizedAnnotations) { 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 5ff9e9bb3..fc51eb4d1 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 @@ -7,12 +7,16 @@ import java.util.Map; import java.util.function.UnaryOperator; /** - * 用于在{@link SyntheticAnnotation}中表示一个处于合成状态的注解对象 + *

用于在{@link SyntheticAnnotation}中表示一个处于合成状态的注解对象。
+ * 当对多个合成注解排序时,默认先按{@link #getVerticalDistance()}排序, + * 再按{@link #getHorizontalDistance()}排序。 + * 该顺序应当保证当合成注解与其他注解存在层级关系时, + * 离根对象最接近的注解被排在靠前的位置。 * * @author huangchengxing * @see SyntheticAnnotation */ -public interface SynthesizedAnnotation extends Annotation { +public interface SynthesizedAnnotation extends Annotation, Comparable { /** * 获取所属的合成注解 @@ -111,4 +115,15 @@ public interface SynthesizedAnnotation extends Annotation { */ Object getAttributeValue(String attributeName); + /** + * 按{@link #getVerticalDistance()}和{@link #getHorizontalDistance()}排序 + * + * @param o {@link SynthesizedAnnotation}对象 + * @return 比较值 + */ + @Override + default int compareTo(SynthesizedAnnotation o) { + return SyntheticAnnotationUtil.getChildPriorityAnnotationCompare().compare(this, o); + } + } 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 index 5bf11f1f0..7e5cce1f0 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationPostProcessor.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationPostProcessor.java @@ -10,7 +10,18 @@ import java.util.Comparator; * 当多个{@link SynthesizedAnnotationPostProcessor}需要一起执行时,将按照{@link #order()}的返回值进行排序, * 该值更小的处理器将被优先执行。 * + *

该接口存在多个实现类,调用者应当保证在任何时候,对一批后置处理器的调用顺序都符合: + *

    + *
  • {@link AliasAttributePostProcessor};
  • + *
  • {@link MirrorLinkAttributePostProcessor};
  • + *
  • {@link AliasForLinkAttributePostProcessor};
  • + *
  • 其他后置处理器;
  • + *
+ * * @author huangchengxing + * @see AliasAttributePostProcessor + * @see MirrorLinkAttributePostProcessor + * @see AliasForLinkAttributePostProcessor */ public interface SynthesizedAnnotationPostProcessor extends Comparable { 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 e9a564242..95a667f83 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 @@ -70,6 +70,13 @@ public interface SyntheticAnnotation extends Annotation, AnnotatedElement { */ SynthesizedAnnotation getSynthesizedAnnotation(Class annotationType); + /** + * 获取全部的合成注解 + * + * @return 合成注解 + */ + Collection getAllSyntheticAnnotations(); + /** * 获取当前的注解类型 * 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 index a007adc75..71746cdf1 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticAnnotationUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticAnnotationUtil.java @@ -6,6 +6,7 @@ import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ObjectUtil; import java.lang.annotation.Annotation; +import java.util.Comparator; /** * {@link SyntheticAnnotation}相关工具,用于内部使用 @@ -36,11 +37,23 @@ class SyntheticAnnotationUtil { */ static SynthesizedAnnotation getLinkedAnnotation( Link annotation, SyntheticAnnotation syntheticAnnotation, Class defaultType) { - final Class targetAnnotationType = ObjectUtil.equals(annotation.annotation(), Annotation.class) ? - defaultType : annotation.annotation(); + final Class targetAnnotationType = getLinkedAnnotationType(annotation, defaultType); return syntheticAnnotation.getSynthesizedAnnotation(targetAnnotationType); } + /** + * 若{@link Link#annotation()}获取的类型{@link Annotation#getClass()},则返回{@code defaultType}, + * 否则返回{@link Link#annotation()}指定的类型 + * + * @param annotation {@link Link}注解 + * @param defaultType 默认注解类型 + * @return 注解类型 + */ + static Class getLinkedAnnotationType(Link annotation, Class defaultType) { + return ObjectUtil.equals(annotation.annotation(), Annotation.class) ? + defaultType : annotation.annotation(); + } + /** * 校验两个注解属性的返回值类型是否一致 * @@ -64,8 +77,19 @@ class SyntheticAnnotationUtil { */ 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() + original.getAttribute(), annotation.attribute(), + getLinkedAnnotationType(annotation, original.getAnnotationType()) ); } + /** + * 获取按{@link SynthesizedAnnotation#getVerticalDistance()}和{@link SynthesizedAnnotation#getHorizontalDistance()}排序的比较器 + * + * @return 比较值 + */ + static Comparator getChildPriorityAnnotationCompare() { + return Comparator.comparing(SynthesizedAnnotation::getVerticalDistance) + .thenComparing(SynthesizedAnnotation::getHorizontalDistance); + } + } 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 11b04a714..26bc5f73f 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 @@ -15,29 +15,36 @@ import java.util.stream.Collectors; import java.util.stream.Stream; /** - * 表示一个根注解与根注解上的多层元注解合成的注解 + * {@link SyntheticAnnotation}的基本实现,表示一个根注解与根注解上的多层元注解合成的注解 * - *

假设现有注解A,A上存在元注解B,B上存在元注解C,则对A解析得到的合成注解X,则CBA都是X的元注解,X为根注解。
- * 通过{@link #isAnnotationPresent(Class)}可确定指定类型是注解是否是该合成注解的元注解,即是否为当前实例的“父类”。 - * 若指定注解是当前实例的元注解,则通过{@link #getAnnotation(Class)}可获得动态代理生成的对应的注解实例。
- * 需要注意的是,由于认为合并注解X以最初的根注解A作为元注解,因此{@link #getAnnotations()}或{@link #getDeclaredAnnotations()} - * 都将只能获得A。 + *

假设现有注解A,A上存在元注解B,B上存在元注解C,则对注解A进行解析, + * 将得到包含根注解A,以及其元注解B、C在内的合成元注解{@link SyntheticMetaAnnotation}。 + * 从{@link AnnotatedElement}的角度来说,得到的合成注解是一个同时承载有ABC三个注解对象的被注解元素, + * 因此通过调用{@link AnnotatedElement}的相关方法将返回对应符合语义的注解对象。 * - *

若认为该合成注解X在第0层,则根注解A在第1层,B在第2层......以此类推, - * 则相同或不同的层级中可能会出现类型相同的注解对象,此时将通过{@link SynthesizedAnnotationSelector}选择出最合适的注解对象, - * 该注解对象将在合成注解中作为唯一有效的元注解用于进行相关操作。
- * 默认情况下,将选择{@link SynthesizedAnnotationSelector#NEAREST_AND_OLDEST_PRIORITY}选择器实例, - * 即层级越低的注解离根注解距离近,则该注解优先级越高,即遵循“就近原则”。 + *

在扫描指定根注解及其元注解时,若在不同的层级出现了类型相同的注解实例, + * 将会根据实例化时指定的{@link SynthesizedAnnotationSelector}选择最优的注解, + * 完成对根注解及其元注解的扫描后,合成注解中每种类型的注解对象都将有且仅有一个。
+ * 默认情况下,将使用{@link SynthesizedAnnotationSelector#NEAREST_AND_OLDEST_PRIORITY}作为选择器, + * 此时若出现扫描时得到了多个同类型的注解对象,有且仅有最接近根注解的注解对象会被作为有效注解。 * - *

合成注解中获取到的注解中可能会具有一些同名且同类型的属性, - * 此时将根据{@link SynthesizedAnnotationAttributeProcessor}决定如何从这些注解的相同属性中获取属性值。
- * 默认情况下,将选择{@link CacheableSynthesizedAnnotationAttributeProcessor}用于获取属性, - * 该处理器将选择距离根注解最近的注解中的属性用于获取属性值,{@link #getAnnotation(Class)}获得的代理类实例的属性值遵循该规则。
- * 举个例子:若CBA同时存在属性y,则将X视为C,B或者A时,获得的y属性的值都与最底层元注解A的值保持一致。 - * 若两相同注解处于同一层级,则按照从其上一级“子注解”的{@link AnnotatedElement#getAnnotations()}的调用顺序排序。 + *

当扫描的注解对象经过{@link SynthesizedAnnotationSelector}处理后, + * 将会被转为{@link MetaAnnotation},并使用在实例化时指定的{@link AliasAttributePostProcessor} + * 进行后置处理。
+ * 默认情况下,将注册以下后置处理器以对{@link Alias}与{@link Link}和其扩展注解提供支持: + *

    + *
  • {@link AliasAttributePostProcessor};
  • + *
  • {@link MirrorLinkAttributePostProcessor};
  • + *
  • {@link AliasForLinkAttributePostProcessor};
  • + *
+ * 若用户需要自行扩展,则需要保证上述三个处理器被正确注入当前实例。 * - *

别名在合成注解中仍然有效,若注解X中任意属性上存在{@link Alias}注解,则{@link Alias#value()}指定的属性值将会覆盖注解属性的本身的值。
- * {@link Alias}注解仅能指定注解X中存在的属性作为别名,不允许指定元注解或子类注解的属性。 + *

{@link SyntheticMetaAnnotation}支持通过{@link #getAttribute(String, Class)}, + * 或通过{@link #syntheticAnnotation(Class)}获得注解代理对象后获取指定类型的注解属性值, + * 返回的属性值将根据合成注解中对应原始注解属性上的{@link Alias}与{@link Link}注解而有所变化。 + * 通过当前实例获取属性值时,将经过{@link SynthesizedAnnotationAttributeProcessor}的处理。
+ * 默认情况下,实例将会注册{@link CacheableSynthesizedAnnotationAttributeProcessor}, + * 该处理器将令元注解中与子注解类型与名称皆一致的属性被子注解的属性覆盖,并且缓存最终获取到的属性值。 * * @author huangchengxing * @see AnnotationUtil @@ -80,10 +87,7 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation { public SyntheticMetaAnnotation(Annotation source) { this( source, SynthesizedAnnotationSelector.NEAREST_AND_OLDEST_PRIORITY, - new CacheableSynthesizedAnnotationAttributeProcessor( - Comparator.comparing(SynthesizedAnnotation::getVerticalDistance) - .thenComparing(SynthesizedAnnotation::getHorizontalDistance) - ), + new CacheableSynthesizedAnnotationAttributeProcessor(), Arrays.asList( new AliasAttributePostProcessor(), new MirrorLinkAttributePostProcessor(), @@ -118,7 +122,6 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation { this.metaAnnotationMap = new LinkedHashMap<>(); // 初始化元注解信息,并进行后置处理 - // TODO 缓存元注解信息,避免重复解析 loadMetaAnnotations(); annotationPostProcessors.forEach(processor -> metaAnnotationMap.values().forEach(synthesized -> processor.process(synthesized, this)) @@ -185,13 +188,13 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation { } /** - * 获取根注解类型 + * 获取全部的已合成注解 * - * @return 注解类型 + * @return 合成注解 */ @Override - public Class annotationType() { - return this.getClass(); + public Collection getAllSyntheticAnnotations() { + return metaAnnotationMap.values(); } /** @@ -208,7 +211,7 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation { } /** - * 获取被合成的注解 + * 获取合成注解中包含的指定注解 * * @param annotationType 注解类型 * @param 注解类型 @@ -235,13 +238,15 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation { } /** - * 获取全部注解 + * 获取合成注解中包含的全部注解 * * @return 注解对象 */ @Override public Annotation[] getAnnotations() { - return getMetaAnnotationMap().values().toArray(new MetaAnnotation[0]); + return metaAnnotationMap.values().stream() + .map(SynthesizedAnnotation::getAnnotation) + .toArray(Annotation[]::new); } /** diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/AbstractAnnotationAttributeWrapperTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/AbstractAnnotationAttributeWrapperTest.java new file mode 100644 index 000000000..b828ab888 --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/AbstractAnnotationAttributeWrapperTest.java @@ -0,0 +1,99 @@ +package cn.hutool.core.annotation; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ReflectUtil; +import org.junit.Assert; +import org.junit.Test; + +import java.lang.annotation.*; +import java.lang.reflect.Method; + +public class AbstractAnnotationAttributeWrapperTest { + + @Test + public void workTest() { + Annotation annotation = ClassForTest1.class.getAnnotation(AnnotationForTest1.class); + Method valueMethod = ReflectUtil.getMethod(AnnotationForTest1.class, "value1"); + CacheableAnnotationAttribute valueAttribute = new CacheableAnnotationAttribute(annotation, valueMethod); + Method nameMethod = ReflectUtil.getMethod(AnnotationForTest1.class, "name1"); + CacheableAnnotationAttribute nameAttribute = new CacheableAnnotationAttribute(annotation, nameMethod); + TestAnnotationAttributeWrapper nameWrapper = new TestAnnotationAttributeWrapper(nameAttribute, valueAttribute); + + // 注解属性 + Assert.assertEquals(annotation, nameWrapper.getAnnotation()); + Assert.assertEquals(annotation.annotationType(), nameWrapper.getAnnotationType()); + Assert.assertEquals(nameAttribute, nameWrapper.getOriginal()); + Assert.assertEquals(valueAttribute, nameWrapper.getLinked()); + + // 方法属性 + Assert.assertEquals(nameMethod.getName(), nameWrapper.getAttributeName()); + Assert.assertEquals(nameMethod.getReturnType(), nameWrapper.getAttributeType()); + Assert.assertTrue(nameWrapper.isWrapped()); + Assert.assertEquals("value1", nameWrapper.getValue()); + } + + @Test + public void multiWrapperTest() { + // 包装第一层: name1 + value1 + Annotation annotation1 = ClassForTest1.class.getAnnotation(AnnotationForTest1.class); + Method value1Method = ReflectUtil.getMethod(AnnotationForTest1.class, "value1"); + CacheableAnnotationAttribute value1Attribute = new CacheableAnnotationAttribute(annotation1, value1Method); + Method name1Method = ReflectUtil.getMethod(AnnotationForTest1.class, "name1"); + CacheableAnnotationAttribute name1Attribute = new CacheableAnnotationAttribute(annotation1, name1Method); + TestAnnotationAttributeWrapper wrapper1 = new TestAnnotationAttributeWrapper(name1Attribute, value1Attribute); + Assert.assertEquals(name1Attribute, wrapper1.getNonWrappedOriginal()); + Assert.assertEquals(CollUtil.newArrayList(name1Attribute, value1Attribute), wrapper1.getAllLinkedNonWrappedAttributes()); + + // 包装第二层:( name1 + value1 ) + value2 + Annotation annotation2 = ClassForTest1.class.getAnnotation(AnnotationForTest2.class); + Method value2Method = ReflectUtil.getMethod(AnnotationForTest2.class, "value2"); + CacheableAnnotationAttribute value2Attribute = new CacheableAnnotationAttribute(annotation2, value2Method); + TestAnnotationAttributeWrapper wrapper2 = new TestAnnotationAttributeWrapper(wrapper1, value2Attribute); + Assert.assertEquals(name1Attribute, wrapper2.getNonWrappedOriginal()); + Assert.assertEquals(CollUtil.newArrayList(name1Attribute, value1Attribute, value2Attribute), wrapper2.getAllLinkedNonWrappedAttributes()); + + // 包装第二层:value3 + ( ( name1 + value1 ) + value2 ) + Annotation annotation3 = ClassForTest1.class.getAnnotation(AnnotationForTest3.class); + Method value3Method = ReflectUtil.getMethod(AnnotationForTest3.class, "value3"); + CacheableAnnotationAttribute value3Attribute = new CacheableAnnotationAttribute(annotation3, value3Method); + TestAnnotationAttributeWrapper wrapper3 = new TestAnnotationAttributeWrapper(value3Attribute, wrapper2); + Assert.assertEquals(value3Attribute, wrapper3.getNonWrappedOriginal()); + Assert.assertEquals(CollUtil.newArrayList(value3Attribute, name1Attribute, value1Attribute, value2Attribute), wrapper3.getAllLinkedNonWrappedAttributes()); + + } + + static class TestAnnotationAttributeWrapper extends AbstractAnnotationAttributeWrapper { + protected TestAnnotationAttributeWrapper(AnnotationAttribute original, AnnotationAttribute linked) { + super(original, linked); + } + @Override + public Object getValue() { + return linked.getValue(); + } + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.METHOD, ElementType.TYPE }) + @interface AnnotationForTest1 { + String value1() default ""; + String name1() default ""; + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.METHOD, ElementType.TYPE }) + @interface AnnotationForTest2 { + String value2() default ""; + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.METHOD, ElementType.TYPE }) + @interface AnnotationForTest3 { + String value3() default ""; + } + + @AnnotationForTest1(name1 = "name1", value1 = "value1") + @AnnotationForTest2(value2 = "value2") + @AnnotationForTest3(value3 = "value3") + static class ClassForTest1 {} + +} diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/AliasedAnnotationAttributeTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/AliasedAnnotationAttributeTest.java new file mode 100644 index 000000000..4d9d17a45 --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/AliasedAnnotationAttributeTest.java @@ -0,0 +1,76 @@ +package cn.hutool.core.annotation; + +import cn.hutool.core.util.ReflectUtil; +import org.junit.Assert; +import org.junit.Test; + +import java.lang.annotation.*; +import java.lang.reflect.Method; + +public class AliasedAnnotationAttributeTest { + + @Test + public void baseInfoTest() { + // 组合属性 + final Annotation annotation = ClassForTest1.class.getAnnotation(AnnotationForTest.class); + final Method valueMethod = ReflectUtil.getMethod(AnnotationForTest.class, "value"); + final CacheableAnnotationAttribute valueAttribute = new CacheableAnnotationAttribute(annotation, valueMethod); + final Method nameMethod = ReflectUtil.getMethod(AnnotationForTest.class, "name"); + final CacheableAnnotationAttribute nameAttribute = new CacheableAnnotationAttribute(annotation, nameMethod); + final AliasedAnnotationAttribute annotationAttribute = new AliasedAnnotationAttribute(valueAttribute, nameAttribute); + + // 注解属性 + Assert.assertEquals(annotation, annotationAttribute.getAnnotation()); + Assert.assertEquals(annotation.annotationType(), annotationAttribute.getAnnotationType()); + + // 方法属性 + Assert.assertEquals(valueMethod.getName(), annotationAttribute.getAttributeName()); + Assert.assertEquals(nameMethod.getReturnType(), annotationAttribute.getAttributeType()); + } + + @Test + public void workWhenValueDefaultTest() { + // 组合属性 + final Annotation annotation = ClassForTest1.class.getAnnotation(AnnotationForTest.class); + final Method valueMethod = ReflectUtil.getMethod(AnnotationForTest.class, "value"); + final CacheableAnnotationAttribute valueAttribute = new CacheableAnnotationAttribute(annotation, valueMethod); + final Method nameMethod = ReflectUtil.getMethod(AnnotationForTest.class, "name"); + final CacheableAnnotationAttribute nameAttribute = new CacheableAnnotationAttribute(annotation, nameMethod); + final AliasedAnnotationAttribute annotationAttribute = new AliasedAnnotationAttribute(valueAttribute, nameAttribute); + + // 值处理 + Assert.assertEquals("name", annotationAttribute.getValue()); + Assert.assertFalse(annotationAttribute.isValueEquivalentToDefaultValue()); + Assert.assertTrue(annotationAttribute.isWrapped()); + } + + @Test + public void workWhenValueNonDefaultTest() { + // 组合属性 + final Annotation annotation = ClassForTest2.class.getAnnotation(AnnotationForTest.class); + final Method valueMethod = ReflectUtil.getMethod(AnnotationForTest.class, "value"); + final CacheableAnnotationAttribute valueAttribute = new CacheableAnnotationAttribute(annotation, valueMethod); + final Method nameMethod = ReflectUtil.getMethod(AnnotationForTest.class, "name"); + final CacheableAnnotationAttribute nameAttribute = new CacheableAnnotationAttribute(annotation, nameMethod); + final AliasedAnnotationAttribute annotationAttribute = new AliasedAnnotationAttribute(valueAttribute, nameAttribute); + + // 值处理 + Assert.assertEquals("value", annotationAttribute.getValue()); + Assert.assertFalse(annotationAttribute.isValueEquivalentToDefaultValue()); + Assert.assertTrue(annotationAttribute.isWrapped()); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.METHOD, ElementType.TYPE }) + @interface AnnotationForTest { + String value() default ""; + String name() default ""; + } + + @AnnotationForTest(name = "name", value = "value") + static class ClassForTest1 {} + + @AnnotationForTest(value = "value") + static class ClassForTest2 {} + +} diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/CacheableAnnotationAttributeTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/CacheableAnnotationAttributeTest.java new file mode 100644 index 000000000..c1b92fcd1 --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/CacheableAnnotationAttributeTest.java @@ -0,0 +1,61 @@ +package cn.hutool.core.annotation; + +import cn.hutool.core.util.ReflectUtil; +import org.junit.Assert; +import org.junit.Test; + +import java.lang.annotation.*; +import java.lang.reflect.Method; + +public class CacheableAnnotationAttributeTest { + + @Test + public void baseInfoTest() { + final Annotation annotation = ClassForTest1.class.getAnnotation(AnnotationForTest.class); + final Method attribute = ReflectUtil.getMethod(AnnotationForTest.class, "value"); + final CacheableAnnotationAttribute annotationAttribute = new CacheableAnnotationAttribute(annotation, attribute); + // 注解属性 + Assert.assertEquals(annotation, annotationAttribute.getAnnotation()); + Assert.assertEquals(annotation.annotationType(), annotationAttribute.getAnnotationType()); + // 方法属性 + Assert.assertEquals(attribute.getName(), annotationAttribute.getAttributeName()); + Assert.assertEquals(attribute.getReturnType(), annotationAttribute.getAttributeType()); + } + + @Test + public void workWhenValueDefaultTest() { + final Annotation annotation = ClassForTest1.class.getAnnotation(AnnotationForTest.class); + final Method attribute = ReflectUtil.getMethod(AnnotationForTest.class, "value"); + final CacheableAnnotationAttribute annotationAttribute = new CacheableAnnotationAttribute(annotation, attribute); + + // 值处理 + Assert.assertEquals("", annotationAttribute.getValue()); + Assert.assertTrue(annotationAttribute.isValueEquivalentToDefaultValue()); + Assert.assertFalse(annotationAttribute.isWrapped()); + } + + @Test + public void workWhenValueNonDefaultTest() { + final Annotation annotation = ClassForTest2.class.getAnnotation(AnnotationForTest.class); + final Method attribute = ReflectUtil.getMethod(AnnotationForTest.class, "value"); + final CacheableAnnotationAttribute annotationAttribute = new CacheableAnnotationAttribute(annotation, attribute); + + // 值处理 + Assert.assertEquals("test", annotationAttribute.getValue()); + Assert.assertFalse(annotationAttribute.isValueEquivalentToDefaultValue()); + Assert.assertFalse(annotationAttribute.isWrapped()); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.METHOD, ElementType.TYPE }) + @interface AnnotationForTest { + String value() default ""; + } + + @AnnotationForTest("") + static class ClassForTest1 {} + + @AnnotationForTest("test") + static class ClassForTest2 {} + +} diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/ForceAliasedAnnotationAttributeTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/ForceAliasedAnnotationAttributeTest.java new file mode 100644 index 000000000..d5af87a62 --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/ForceAliasedAnnotationAttributeTest.java @@ -0,0 +1,76 @@ +package cn.hutool.core.annotation; + +import cn.hutool.core.util.ReflectUtil; +import org.junit.Assert; +import org.junit.Test; + +import java.lang.annotation.*; +import java.lang.reflect.Method; + +public class ForceAliasedAnnotationAttributeTest { + + @Test + public void baseInfoTest() { + // 组合属性 + final Annotation annotation = ClassForTest1.class.getAnnotation(AnnotationForTest.class); + final Method valueMethod = ReflectUtil.getMethod(AnnotationForTest.class, "value"); + final CacheableAnnotationAttribute valueAttribute = new CacheableAnnotationAttribute(annotation, valueMethod); + final Method nameMethod = ReflectUtil.getMethod(AnnotationForTest.class, "name"); + final CacheableAnnotationAttribute nameAttribute = new CacheableAnnotationAttribute(annotation, nameMethod); + final ForceAliasedAnnotationAttribute valueAnnotationAttribute = new ForceAliasedAnnotationAttribute(valueAttribute, nameAttribute); + + // 注解属性 + Assert.assertEquals(annotation, valueAnnotationAttribute.getAnnotation()); + Assert.assertEquals(annotation.annotationType(), valueAnnotationAttribute.getAnnotationType()); + + // 方法属性 + Assert.assertEquals(valueMethod.getName(), valueAnnotationAttribute.getAttributeName()); + Assert.assertEquals(valueMethod.getReturnType(), valueAnnotationAttribute.getAttributeType()); + } + + @Test + public void workWhenValueDefaultTest() { + // 组合属性 + final Annotation annotation = ClassForTest1.class.getAnnotation(AnnotationForTest.class); + final Method valueMethod = ReflectUtil.getMethod(AnnotationForTest.class, "value"); + final CacheableAnnotationAttribute valueAttribute = new CacheableAnnotationAttribute(annotation, valueMethod); + final Method nameMethod = ReflectUtil.getMethod(AnnotationForTest.class, "name"); + final CacheableAnnotationAttribute nameAttribute = new CacheableAnnotationAttribute(annotation, nameMethod); + final AliasedAnnotationAttribute valueAnnotationAttribute = new AliasedAnnotationAttribute(valueAttribute, nameAttribute); + + // 值处理 + Assert.assertEquals("name", valueAnnotationAttribute.getValue()); + Assert.assertFalse(valueAnnotationAttribute.isValueEquivalentToDefaultValue()); + Assert.assertTrue(valueAnnotationAttribute.isWrapped()); + } + + @Test + public void workWhenValueNonDefaultTest() { + // 组合属性 + final Annotation annotation = ClassForTest2.class.getAnnotation(AnnotationForTest.class); + final Method valueMethod = ReflectUtil.getMethod(AnnotationForTest.class, "value"); + final CacheableAnnotationAttribute valueAttribute = new CacheableAnnotationAttribute(annotation, valueMethod); + final Method nameMethod = ReflectUtil.getMethod(AnnotationForTest.class, "name"); + final CacheableAnnotationAttribute nameAttribute = new CacheableAnnotationAttribute(annotation, nameMethod); + final ForceAliasedAnnotationAttribute valueAnnotationAttribute = new ForceAliasedAnnotationAttribute(valueAttribute, nameAttribute); + + // 值处理 + Assert.assertEquals("", valueAnnotationAttribute.getValue()); + Assert.assertTrue(valueAnnotationAttribute.isValueEquivalentToDefaultValue()); + Assert.assertTrue(valueAnnotationAttribute.isWrapped()); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.METHOD, ElementType.TYPE }) + @interface AnnotationForTest { + String value() default ""; + String name() default ""; + } + + @AnnotationForTest(name = "name", value = "value") + static class ClassForTest1 {} + + @AnnotationForTest(value = "value") + static class ClassForTest2 {} + +} diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/MirroredAnnotationAttributeTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/MirroredAnnotationAttributeTest.java new file mode 100644 index 000000000..a4f648514 --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/MirroredAnnotationAttributeTest.java @@ -0,0 +1,76 @@ +package cn.hutool.core.annotation; + +import cn.hutool.core.util.ReflectUtil; +import org.junit.Assert; +import org.junit.Test; + +import java.lang.annotation.*; +import java.lang.reflect.Method; + +public class MirroredAnnotationAttributeTest { + + @Test + public void baseInfoTest() { + // 组合属性 + final Annotation annotation = ClassForTest1.class.getAnnotation(AnnotationForTest.class); + final Method valueMethod = ReflectUtil.getMethod(AnnotationForTest.class, "value"); + final CacheableAnnotationAttribute valueAttribute = new CacheableAnnotationAttribute(annotation, valueMethod); + final Method nameMethod = ReflectUtil.getMethod(AnnotationForTest.class, "name"); + final CacheableAnnotationAttribute nameAttribute = new CacheableAnnotationAttribute(annotation, nameMethod); + final MirroredAnnotationAttribute nameAnnotationAttribute = new MirroredAnnotationAttribute(nameAttribute, valueAttribute); + + // 注解属性 + Assert.assertEquals(annotation, nameAnnotationAttribute.getAnnotation()); + Assert.assertEquals(annotation.annotationType(), nameAnnotationAttribute.getAnnotationType()); + + // 方法属性 + Assert.assertEquals(nameMethod.getName(), nameAnnotationAttribute.getAttributeName()); + Assert.assertEquals(nameMethod.getReturnType(), nameAnnotationAttribute.getAttributeType()); + } + + @Test + public void workWhenValueDefaultTest() { + // 组合属性 + final Annotation annotation = ClassForTest2.class.getAnnotation(AnnotationForTest.class); + final Method valueMethod = ReflectUtil.getMethod(AnnotationForTest.class, "value"); + final CacheableAnnotationAttribute valueAttribute = new CacheableAnnotationAttribute(annotation, valueMethod); + final Method nameMethod = ReflectUtil.getMethod(AnnotationForTest.class, "name"); + final CacheableAnnotationAttribute nameAttribute = new CacheableAnnotationAttribute(annotation, nameMethod); + final MirroredAnnotationAttribute nameAnnotationAttribute = new MirroredAnnotationAttribute(nameAttribute, valueAttribute); + + // 值处理 + Assert.assertEquals("", nameAnnotationAttribute.getValue()); + Assert.assertTrue(nameAnnotationAttribute.isValueEquivalentToDefaultValue()); + Assert.assertTrue(nameAnnotationAttribute.isWrapped()); + } + + @Test + public void workWhenValueNonDefaultTest() { + // 组合属性 + final Annotation annotation = ClassForTest1.class.getAnnotation(AnnotationForTest.class); + final Method valueMethod = ReflectUtil.getMethod(AnnotationForTest.class, "value"); + final CacheableAnnotationAttribute valueAttribute = new CacheableAnnotationAttribute(annotation, valueMethod); + final Method nameMethod = ReflectUtil.getMethod(AnnotationForTest.class, "name"); + final CacheableAnnotationAttribute nameAttribute = new CacheableAnnotationAttribute(annotation, nameMethod); + final MirroredAnnotationAttribute nameAnnotationAttribute = new MirroredAnnotationAttribute(nameAttribute, valueAttribute); + + // 值处理 + Assert.assertEquals("name", nameAnnotationAttribute.getValue()); + Assert.assertFalse(nameAnnotationAttribute.isValueEquivalentToDefaultValue()); + Assert.assertTrue(nameAnnotationAttribute.isWrapped()); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.METHOD, ElementType.TYPE }) + @interface AnnotationForTest { + String value() default ""; + String name() default ""; + } + + @AnnotationForTest(value = "name") + static class ClassForTest1 {} + + @AnnotationForTest + static class ClassForTest2 {} + +} diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/SynthesizedAnnotationSelectorTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/SynthesizedAnnotationSelectorTest.java new file mode 100644 index 000000000..e27f8fb05 --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/SynthesizedAnnotationSelectorTest.java @@ -0,0 +1,146 @@ +package cn.hutool.core.annotation; + +import org.junit.Assert; +import org.junit.Test; + +import java.lang.annotation.Annotation; +import java.util.Map; +import java.util.function.UnaryOperator; + +public class SynthesizedAnnotationSelectorTest { + + @Test + public void nearestAndOldestPriorityTest() { + final SynthesizedAnnotationSelector selector = SynthesizedAnnotationSelector.NEAREST_AND_OLDEST_PRIORITY; + + TestSynthesizedAnnotation annotation1 = new TestSynthesizedAnnotation(0, 0); + TestSynthesizedAnnotation annotation2 = new TestSynthesizedAnnotation(0, 0); + Assert.assertEquals(annotation1, selector.choose(annotation1, annotation2)); + + annotation1 = new TestSynthesizedAnnotation(0, 1); + annotation2 = new TestSynthesizedAnnotation(0, 0); + Assert.assertEquals(annotation1, selector.choose(annotation1, annotation2)); + + annotation1 = new TestSynthesizedAnnotation(1, 0); + annotation2 = new TestSynthesizedAnnotation(0, 0); + Assert.assertEquals(annotation2, selector.choose(annotation1, annotation2)); + } + + @Test + public void nearestAndNewestPriorityTest() { + final SynthesizedAnnotationSelector selector = SynthesizedAnnotationSelector.NEAREST_AND_NEWEST_PRIORITY; + + TestSynthesizedAnnotation annotation1 = new TestSynthesizedAnnotation(0, 0); + TestSynthesizedAnnotation annotation2 = new TestSynthesizedAnnotation(0, 0); + Assert.assertEquals(annotation2, selector.choose(annotation1, annotation2)); + + annotation1 = new TestSynthesizedAnnotation(0, 1); + annotation2 = new TestSynthesizedAnnotation(0, 0); + Assert.assertEquals(annotation2, selector.choose(annotation1, annotation2)); + + annotation1 = new TestSynthesizedAnnotation(0, 0); + annotation2 = new TestSynthesizedAnnotation(1, 0); + Assert.assertEquals(annotation1, selector.choose(annotation1, annotation2)); + } + + @Test + public void farthestAndOldestPriorityTest() { + final SynthesizedAnnotationSelector selector = SynthesizedAnnotationSelector.FARTHEST_AND_OLDEST_PRIORITY; + + TestSynthesizedAnnotation annotation1 = new TestSynthesizedAnnotation(0, 0); + TestSynthesizedAnnotation annotation2 = new TestSynthesizedAnnotation(0, 0); + Assert.assertEquals(annotation1, selector.choose(annotation1, annotation2)); + + annotation1 = new TestSynthesizedAnnotation(0, 1); + annotation2 = new TestSynthesizedAnnotation(0, 0); + Assert.assertEquals(annotation1, selector.choose(annotation1, annotation2)); + + annotation1 = new TestSynthesizedAnnotation(0, 0); + annotation2 = new TestSynthesizedAnnotation(1, 0); + Assert.assertEquals(annotation2, selector.choose(annotation1, annotation2)); + } + + @Test + public void farthestAndNewestPriorityTest() { + final SynthesizedAnnotationSelector selector = SynthesizedAnnotationSelector.FARTHEST_AND_NEWEST_PRIORITY; + + TestSynthesizedAnnotation annotation1 = new TestSynthesizedAnnotation(0, 0); + TestSynthesizedAnnotation annotation2 = new TestSynthesizedAnnotation(0, 0); + Assert.assertEquals(annotation2, selector.choose(annotation1, annotation2)); + + annotation1 = new TestSynthesizedAnnotation(0, 1); + annotation2 = new TestSynthesizedAnnotation(0, 0); + Assert.assertEquals(annotation2, selector.choose(annotation1, annotation2)); + + annotation1 = new TestSynthesizedAnnotation(1, 0); + annotation2 = new TestSynthesizedAnnotation(0, 0); + Assert.assertEquals(annotation1, selector.choose(annotation1, annotation2)); + } + + static class TestSynthesizedAnnotation implements SynthesizedAnnotation { + + private final int verticalDistance; + private final int horizontalDistance; + + public TestSynthesizedAnnotation(int verticalDistance, int horizontalDistance) { + this.verticalDistance = verticalDistance; + this.horizontalDistance = horizontalDistance; + } + + @Override + public SyntheticAnnotation getOwner() { + return null; + } + + @Override + public Object getRoot() { + return null; + } + + @Override + public Annotation getAnnotation() { + return null; + } + + @Override + public int getVerticalDistance() { + return this.verticalDistance; + } + + @Override + public int getHorizontalDistance() { + return this.horizontalDistance; + } + + @Override + public boolean hasAttribute(String attributeName, Class returnType) { + return false; + } + + @Override + public Map getAttributes() { + return null; + } + + @Override + public void setAttribute(String attributeName, AnnotationAttribute attribute) { + + } + + @Override + public void replaceAttribute(String attributeName, UnaryOperator operator) { + + } + + @Override + public Object getAttributeValue(String attributeName) { + return null; + } + + @Override + public Class annotationType() { + return null; + } + } + +} 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 dbc7964f5..c9e358883 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 @@ -4,11 +4,9 @@ import cn.hutool.core.util.ReflectUtil; 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.annotation.*; import java.lang.reflect.Method; +import java.util.Arrays; /** * 合成注解{@link SyntheticMetaAnnotation}的测试用例 @@ -18,8 +16,38 @@ import java.lang.reflect.Method; public class SyntheticMetaAnnotationTest { @Test - public void testSynthesisAnnotation() { - ChildAnnotation rootAnnotation = AnnotatedClass.class.getAnnotation(ChildAnnotation.class); + public void baseSynthesisAnnotationWorkTest() { + // AnnotatedClass -> @ChildAnnotation -> @ParentAnnotation -> @GrandParentAnnotation + // -> @GrandParentAnnotation + final GrandParentAnnotation grandParentAnnotation = ChildAnnotation.class.getAnnotation(GrandParentAnnotation.class); + final ParentAnnotation parentAnnotation = ChildAnnotation.class.getAnnotation(ParentAnnotation.class); + final ChildAnnotation childAnnotation = AnnotatedClass.class.getAnnotation(ChildAnnotation.class); + final SyntheticMetaAnnotation syntheticMetaAnnotation = new SyntheticMetaAnnotation(childAnnotation); + + // Annotation & AnnotatedElement + Assert.assertEquals(SyntheticMetaAnnotation.class, syntheticMetaAnnotation.annotationType()); + Assert.assertTrue(syntheticMetaAnnotation.isAnnotationPresent(GrandParentAnnotation.class)); + Assert.assertTrue(syntheticMetaAnnotation.isAnnotationPresent(ParentAnnotation.class)); + Assert.assertTrue(syntheticMetaAnnotation.isAnnotationPresent(ChildAnnotation.class)); + Assert.assertEquals(grandParentAnnotation, syntheticMetaAnnotation.getAnnotation(GrandParentAnnotation.class)); + Assert.assertEquals(parentAnnotation, syntheticMetaAnnotation.getAnnotation(ParentAnnotation.class)); + Assert.assertEquals(childAnnotation, syntheticMetaAnnotation.getAnnotation(ChildAnnotation.class)); + Assert.assertEquals( + Arrays.asList(childAnnotation, grandParentAnnotation, parentAnnotation), + Arrays.asList(syntheticMetaAnnotation.getAnnotations()) + ); + Assert.assertArrayEquals(new Annotation[]{ childAnnotation }, syntheticMetaAnnotation.getDeclaredAnnotations()); + + // 扩展方法 + Assert.assertNotNull(syntheticMetaAnnotation.getSynthesizedAnnotation(GrandParentAnnotation.class)); + Assert.assertNotNull(syntheticMetaAnnotation.getSynthesizedAnnotation(ParentAnnotation.class)); + Assert.assertNotNull(syntheticMetaAnnotation.getSynthesizedAnnotation(ChildAnnotation.class)); + Assert.assertEquals(3, syntheticMetaAnnotation.getAllSyntheticAnnotations().size()); + } + + @Test + public void synthesisAnnotationAttributeTest() { + final ChildAnnotation rootAnnotation = AnnotatedClass.class.getAnnotation(ChildAnnotation.class); SyntheticMetaAnnotation syntheticMetaAnnotation = new SyntheticMetaAnnotation(rootAnnotation); Assert.assertEquals(syntheticMetaAnnotation.getSource(), rootAnnotation); Assert.assertEquals(syntheticMetaAnnotation.annotationType(), SyntheticMetaAnnotation.class); @@ -32,7 +60,7 @@ public class SyntheticMetaAnnotationTest { Assert.assertEquals("Child's Parent!", syntheticMetaAnnotation.getAttribute("parentValue", String.class)); Assert.assertEquals("Child's GrandParent!", syntheticMetaAnnotation.getAttribute("grandParentValue", String.class)); - ChildAnnotation childAnnotation = syntheticMetaAnnotation.syntheticAnnotation(ChildAnnotation.class); + final ChildAnnotation childAnnotation = syntheticMetaAnnotation.syntheticAnnotation(ChildAnnotation.class); Assert.assertTrue(syntheticMetaAnnotation.isAnnotationPresent(ChildAnnotation.class)); Assert.assertNotNull(childAnnotation); Assert.assertEquals("Child!", childAnnotation.childValue()); @@ -40,14 +68,14 @@ public class SyntheticMetaAnnotationTest { Assert.assertEquals(childAnnotation.grandParentType(), Integer.class); Assert.assertThrows(IllegalArgumentException.class, () -> new SyntheticMetaAnnotation(childAnnotation)); - ParentAnnotation parentAnnotation = syntheticMetaAnnotation.syntheticAnnotation(ParentAnnotation.class); + final ParentAnnotation parentAnnotation = syntheticMetaAnnotation.syntheticAnnotation(ParentAnnotation.class); Assert.assertTrue(syntheticMetaAnnotation.isAnnotationPresent(ParentAnnotation.class)); Assert.assertNotNull(parentAnnotation); Assert.assertEquals("Child's Parent!", parentAnnotation.parentValue()); Assert.assertEquals("java.lang.Void", parentAnnotation.grandParentType()); Assert.assertThrows(IllegalArgumentException.class, () -> new SyntheticMetaAnnotation(parentAnnotation)); - GrandParentAnnotation grandParentAnnotation = syntheticMetaAnnotation.syntheticAnnotation(GrandParentAnnotation.class); + final GrandParentAnnotation grandParentAnnotation = syntheticMetaAnnotation.syntheticAnnotation(GrandParentAnnotation.class); Assert.assertTrue(syntheticMetaAnnotation.isAnnotationPresent(GrandParentAnnotation.class)); Assert.assertNotNull(grandParentAnnotation); Assert.assertEquals("Child's GrandParent!", grandParentAnnotation.grandParentValue()); @@ -57,9 +85,9 @@ public class SyntheticMetaAnnotationTest { @Test public void linkTest() { - Method method = ReflectUtil.getMethod(AnnotationForLinkTest.class, "value"); - SyntheticAnnotation syntheticAnnotation = new SyntheticMetaAnnotation(method.getAnnotation(AliasFor.class)); - Link link = syntheticAnnotation.syntheticAnnotation(Link.class); + final Method method = ReflectUtil.getMethod(AnnotationForLinkTest.class, "value"); + final SyntheticAnnotation syntheticAnnotation = new SyntheticMetaAnnotation(method.getAnnotation(AliasFor.class)); + final Link link = syntheticAnnotation.syntheticAnnotation(Link.class); Assert.assertEquals(AnnotationForLinkTest.class, link.annotation()); Assert.assertEquals("name", link.attribute()); } @@ -82,7 +110,7 @@ public class SyntheticMetaAnnotationTest { synthetic = new SyntheticMetaAnnotation(annotation); syntheticAnnotation = synthetic.syntheticAnnotation(AnnotationForMirrorTest.class); AnnotationForMirrorTest finalSyntheticAnnotation = syntheticAnnotation; - Assert.assertThrows(IllegalArgumentException.class, () -> finalSyntheticAnnotation.name()); + Assert.assertThrows(IllegalArgumentException.class, finalSyntheticAnnotation::name); } @Test @@ -132,18 +160,30 @@ public class SyntheticMetaAnnotationTest { @Test public void multiAliasForTest() { - AnnotationForMultiAliasForTest annotation = ClassForMultiAliasForTest.class.getAnnotation(AnnotationForMultiAliasForTest.class); - SyntheticAnnotation synthetic = new SyntheticMetaAnnotation(annotation); + final AnnotationForMultiAliasForTest annotation = ClassForMultiAliasForTest.class.getAnnotation(AnnotationForMultiAliasForTest.class); + final SyntheticAnnotation synthetic = new SyntheticMetaAnnotation(annotation); - MetaAnnotationForMultiAliasForTest1 metaAnnotation1 = synthetic.syntheticAnnotation(MetaAnnotationForMultiAliasForTest1.class); + final MetaAnnotationForMultiAliasForTest1 metaAnnotation1 = synthetic.syntheticAnnotation(MetaAnnotationForMultiAliasForTest1.class); Assert.assertEquals("test", metaAnnotation1.name()); Assert.assertEquals("test", metaAnnotation1.value1()); - MetaAnnotationForMultiAliasForTest2 metaAnnotation2 = synthetic.syntheticAnnotation(MetaAnnotationForMultiAliasForTest2.class); + final MetaAnnotationForMultiAliasForTest2 metaAnnotation2 = synthetic.syntheticAnnotation(MetaAnnotationForMultiAliasForTest2.class); Assert.assertEquals("test", metaAnnotation2.value2()); - AnnotationForMultiAliasForTest childAnnotation = synthetic.syntheticAnnotation(AnnotationForMultiAliasForTest.class); + final AnnotationForMultiAliasForTest childAnnotation = synthetic.syntheticAnnotation(AnnotationForMultiAliasForTest.class); Assert.assertEquals("test", childAnnotation.value3()); } + @Test + public void implicitAliasTest() { + final AnnotationForImplicitAliasTest annotation = ClassForImplicitAliasTest.class.getAnnotation(AnnotationForImplicitAliasTest.class); + final SyntheticAnnotation synthetic = new SyntheticMetaAnnotation(annotation); + + final MetaAnnotationForImplicitAliasTest metaAnnotation = synthetic.syntheticAnnotation(MetaAnnotationForImplicitAliasTest.class); + Assert.assertEquals("Meta", metaAnnotation.name()); + Assert.assertEquals("Foo", metaAnnotation.value()); + final AnnotationForImplicitAliasTest childAnnotation = synthetic.syntheticAnnotation(AnnotationForImplicitAliasTest.class); + Assert.assertEquals("Foo", childAnnotation.value()); + } + // 注解结构如下: // AnnotatedClass -> @ChildAnnotation -> @ParentAnnotation -> @GrandParentAnnotation // -> @GrandParentAnnotation @@ -284,4 +324,21 @@ public class SyntheticMetaAnnotationTest { @AnnotationForMultiAliasForTest(value3 = "test") static class ClassForMultiAliasForTest{} + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.METHOD, ElementType.TYPE }) + @interface MetaAnnotationForImplicitAliasTest { + @MirrorFor(attribute = "value") + String name() default ""; + @MirrorFor(attribute = "name") + String value() default ""; + } + @MetaAnnotationForImplicitAliasTest("Meta") + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.METHOD, ElementType.TYPE }) + @interface AnnotationForImplicitAliasTest { + String value() default ""; + } + @AnnotationForImplicitAliasTest("Foo") + static class ClassForImplicitAliasTest {} + } From 18c7a7806206084a5141fe38bfb6805c3f058229 Mon Sep 17 00:00:00 2001 From: huangchengxing <841396397@qq.com> Date: Wed, 13 Jul 2022 22:36:08 +0800 Subject: [PATCH 05/15] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=96=B9=E6=B3=95?= =?UTF-8?q?=E3=80=81=E5=8F=98=E9=87=8F=E4=B8=8E=E7=B1=BB=E5=90=8D=EF=BC=8C?= =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...> AbstractWrappedAnnotationAttribute.java} | 10 +- .../AliasAttributePostProcessor.java | 7 +- .../AliasForLinkAttributePostProcessor.java | 21 +- .../AliasedAnnotationAttribute.java | 2 +- .../core/annotation/AnnotationAggregator.java | 49 ++++ .../core/annotation/AnnotationAttribute.java | 13 +- .../core/annotation/AnnotationUtil.java | 49 +--- .../ForceAliasedAnnotationAttribute.java | 2 +- .../java/cn/hutool/core/annotation/Link.java | 4 +- .../MirrorLinkAttributePostProcessor.java | 7 +- .../MirroredAnnotationAttribute.java | 2 +- .../hutool/core/annotation/RelationType.java | 6 +- .../annotation/SynthesizedAnnotation.java | 15 +- ...a => SynthesizedAnnotationAggregator.java} | 56 +---- ...nthesizedAnnotationAttributeProcessor.java | 2 +- .../SynthesizedAnnotationPostProcessor.java | 6 +- .../SynthesizedAnnotationSelector.java | 2 +- ... SynthesizedMetaAnnotationAggregator.java} | 39 ++-- .../annotation/SyntheticAnnotationProxy.java | 11 +- .../annotation/SyntheticAnnotationUtil.java | 19 +- ...r.java => WrappedAnnotationAttribute.java} | 8 +- ...stractWrappedAnnotationAttributeTest.java} | 21 +- .../AliasAttributePostProcessorTest.java | 190 +++++++++++++++ ...liasForLinkAttributePostProcessorTest.java | 218 ++++++++++++++++++ .../AliasedAnnotationAttributeTest.java | 12 +- ...sizedAnnotationAttributeProcessorTest.java | 100 ++++++++ .../MirrorLinkAttributePostProcessorTest.java | 192 +++++++++++++++ .../SynthesizedAnnotationSelectorTest.java | 12 +- .../SyntheticMetaAnnotationTest.java | 111 +++++---- 29 files changed, 945 insertions(+), 241 deletions(-) rename hutool-core/src/main/java/cn/hutool/core/annotation/{AbstractAnnotationAttributeWrapper.java => AbstractWrappedAnnotationAttribute.java} (81%) create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationAggregator.java rename hutool-core/src/main/java/cn/hutool/core/annotation/{SyntheticAnnotation.java => SynthesizedAnnotationAggregator.java} (70%) rename hutool-core/src/main/java/cn/hutool/core/annotation/{SyntheticMetaAnnotation.java => SynthesizedMetaAnnotationAggregator.java} (90%) rename hutool-core/src/main/java/cn/hutool/core/annotation/{AnnotationAttributeWrapper.java => WrappedAnnotationAttribute.java} (92%) rename hutool-core/src/test/java/cn/hutool/core/annotation/{AbstractAnnotationAttributeWrapperTest.java => AbstractWrappedAnnotationAttributeTest.java} (81%) create mode 100644 hutool-core/src/test/java/cn/hutool/core/annotation/AliasAttributePostProcessorTest.java create mode 100644 hutool-core/src/test/java/cn/hutool/core/annotation/AliasForLinkAttributePostProcessorTest.java create mode 100644 hutool-core/src/test/java/cn/hutool/core/annotation/CacheableSynthesizedAnnotationAttributeProcessorTest.java create mode 100644 hutool-core/src/test/java/cn/hutool/core/annotation/MirrorLinkAttributePostProcessorTest.java diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractAnnotationAttributeWrapper.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractWrappedAnnotationAttribute.java similarity index 81% rename from hutool-core/src/main/java/cn/hutool/core/annotation/AbstractAnnotationAttributeWrapper.java rename to hutool-core/src/main/java/cn/hutool/core/annotation/AbstractWrappedAnnotationAttribute.java index 9d4c170cd..2dc13d476 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractAnnotationAttributeWrapper.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractWrappedAnnotationAttribute.java @@ -8,19 +8,19 @@ import java.util.Collection; import java.util.List; /** - * {@link AnnotationAttributeWrapper}的基本实现 + * {@link WrappedAnnotationAttribute}的基本实现 * * @author huangchengxing * @see ForceAliasedAnnotationAttribute * @see AliasedAnnotationAttribute * @see MirroredAnnotationAttribute */ -public abstract class AbstractAnnotationAttributeWrapper implements AnnotationAttributeWrapper { +public abstract class AbstractWrappedAnnotationAttribute implements WrappedAnnotationAttribute { protected final AnnotationAttribute original; protected final AnnotationAttribute linked; - protected AbstractAnnotationAttributeWrapper(AnnotationAttribute original, AnnotationAttribute linked) { + protected AbstractWrappedAnnotationAttribute(AnnotationAttribute original, AnnotationAttribute linked) { Assert.notNull(original, "target must not null"); Assert.notNull(linked, "linked must not null"); this.original = original; @@ -43,7 +43,7 @@ public abstract class AbstractAnnotationAttributeWrapper implements AnnotationAt AnnotationAttribute next = original; while (next != null) { curr = next; - next = next.isWrapped() ? ((AnnotationAttributeWrapper)curr).getOriginal() : null; + next = next.isWrapped() ? ((WrappedAnnotationAttribute)curr).getOriginal() : null; } return curr; } @@ -63,7 +63,7 @@ public abstract class AbstractAnnotationAttributeWrapper implements AnnotationAt leafAttributes.add(curr); return; } - AnnotationAttributeWrapper wrappedAttribute = (AnnotationAttributeWrapper)curr; + WrappedAnnotationAttribute wrappedAttribute = (WrappedAnnotationAttribute)curr; collectLeafAttribute(wrappedAttribute.getOriginal(), leafAttributes); collectLeafAttribute(wrappedAttribute.getLinked(), leafAttributes); } 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 index 8eba6bb49..3c0c2f0cf 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/AliasAttributePostProcessor.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AliasAttributePostProcessor.java @@ -25,7 +25,7 @@ public class AliasAttributePostProcessor implements SynthesizedAnnotationPostPro } @Override - public void process(SynthesizedAnnotation annotation, SyntheticAnnotation syntheticAnnotation) { + public void process(SynthesizedAnnotation annotation, SynthesizedAnnotationAggregator synthesizedAnnotationAggregator) { final Map attributeMap = annotation.getAttributes(); // 记录别名与属性的关系 @@ -47,7 +47,6 @@ public class AliasAttributePostProcessor implements SynthesizedAnnotationPostPro final AnnotationAttribute resolvedAttribute = Opt.ofNullable(attributeName) .map(attributeAliasMappings::getRootNode) .map(TreeEntry::getValue) - .map(aliasAttribute -> (AnnotationAttribute)new ForceAliasedAnnotationAttribute(attribute, aliasAttribute)) .orElse(attribute); Assert.isTrue( ObjectUtil.isNull(resolvedAttribute) @@ -55,7 +54,9 @@ public class AliasAttributePostProcessor implements SynthesizedAnnotationPostPro "return type of the root alias method [{}] is inconsistent with the original [{}]", resolvedAttribute.getClass(), attribute.getAttributeType() ); - attributeMap.put(attributeName, resolvedAttribute); + if (attribute != resolvedAttribute) { + attributeMap.put(attributeName, new ForceAliasedAnnotationAttribute(attribute, resolvedAttribute)); + } }); 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 index dcaff1438..4fbe48766 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/AliasForLinkAttributePostProcessor.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AliasForLinkAttributePostProcessor.java @@ -24,7 +24,7 @@ public class AliasForLinkAttributePostProcessor implements SynthesizedAnnotation } @Override - public void process(SynthesizedAnnotation annotation, SyntheticAnnotation syntheticAnnotation) { + public void process(SynthesizedAnnotation annotation, SynthesizedAnnotationAggregator synthesizedAnnotationAggregator) { final Map attributeMap = new HashMap<>(annotation.getAttributes()); attributeMap.forEach((originalAttributeName, originalAttribute) -> { // 获取注解 @@ -36,7 +36,7 @@ public class AliasForLinkAttributePostProcessor implements SynthesizedAnnotation } // 获取注解属性 - final SynthesizedAnnotation aliasAnnotation = SyntheticAnnotationUtil.getLinkedAnnotation(link, syntheticAnnotation, annotation.annotationType()); + final SynthesizedAnnotation aliasAnnotation = SyntheticAnnotationUtil.getLinkedAnnotation(link, synthesizedAnnotationAggregator, annotation.annotationType()); if (ObjectUtil.isNull(aliasAnnotation)) { return; } @@ -47,11 +47,11 @@ public class AliasForLinkAttributePostProcessor implements SynthesizedAnnotation // aliasFor if (RelationType.ALIAS_FOR.equals(link.type())) { - wrappingLinkedAttribute(syntheticAnnotation, originalAttribute, aliasAttribute, AliasedAnnotationAttribute::new); + wrappingLinkedAttribute(synthesizedAnnotationAggregator, originalAttribute, aliasAttribute, AliasedAnnotationAttribute::new); return; } // forceAliasFor - wrappingLinkedAttribute(syntheticAnnotation, originalAttribute, aliasAttribute, ForceAliasedAnnotationAttribute::new); + wrappingLinkedAttribute(synthesizedAnnotationAggregator, originalAttribute, aliasAttribute, ForceAliasedAnnotationAttribute::new); }); } @@ -59,16 +59,16 @@ public class AliasForLinkAttributePostProcessor implements SynthesizedAnnotation * 对指定注解属性进行包装,若该属性已被包装过,则递归以其为根节点的树结构,对树上全部的叶子节点进行包装 */ private void wrappingLinkedAttribute( - SyntheticAnnotation syntheticAnnotation, AnnotationAttribute originalAttribute, AnnotationAttribute aliasAttribute, BinaryOperator wrapping) { + SynthesizedAnnotationAggregator synthesizedAnnotationAggregator, AnnotationAttribute originalAttribute, AnnotationAttribute aliasAttribute, BinaryOperator wrapping) { // 不是包装属性 if (!aliasAttribute.isWrapped()) { - processAttribute(syntheticAnnotation, originalAttribute, aliasAttribute, wrapping); + processAttribute(synthesizedAnnotationAggregator, originalAttribute, aliasAttribute, wrapping); return; } // 是包装属性 - final AbstractAnnotationAttributeWrapper wrapper = (AbstractAnnotationAttributeWrapper)aliasAttribute; + final AbstractWrappedAnnotationAttribute wrapper = (AbstractWrappedAnnotationAttribute)aliasAttribute; wrapper.getAllLinkedNonWrappedAttributes().forEach( - t -> processAttribute(syntheticAnnotation, originalAttribute, t, wrapping) + t -> processAttribute(synthesizedAnnotationAggregator, originalAttribute, t, wrapping) ); } @@ -76,10 +76,10 @@ public class AliasForLinkAttributePostProcessor implements SynthesizedAnnotation * 获取指定注解属性,然后将其再进行一层包装 */ private void processAttribute( - SyntheticAnnotation syntheticAnnotation, AnnotationAttribute originalAttribute, + SynthesizedAnnotationAggregator synthesizedAnnotationAggregator, AnnotationAttribute originalAttribute, AnnotationAttribute target, BinaryOperator wrapping) { Opt.ofNullable(target.getAnnotationType()) - .map(syntheticAnnotation::getSynthesizedAnnotation) + .map(synthesizedAnnotationAggregator::getSynthesizedAnnotation) .ifPresent(t -> t.replaceAttribute(target.getAttributeName(), old -> wrapping.apply(old, originalAttribute))); } @@ -87,6 +87,7 @@ public class AliasForLinkAttributePostProcessor implements SynthesizedAnnotation * 检查两个属性是否互为别名 */ private void checkCircularDependency(AnnotationAttribute original, AnnotationAttribute alias) { + SyntheticAnnotationUtil.checkLinkedSelf(original, alias); Link annotation = SyntheticAnnotationUtil.getLink(alias, RelationType.ALIAS_FOR, RelationType.FORCE_ALIAS_FOR); if (ObjectUtil.isNull(annotation)) { return; 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 index 36d8dda81..53997f0ee 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/AliasedAnnotationAttribute.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AliasedAnnotationAttribute.java @@ -9,7 +9,7 @@ package cn.hutool.core.annotation; * @see RelationType#FORCE_ALIAS_FOR * @see RelationType#ALIAS_FOR */ -public class AliasedAnnotationAttribute extends AbstractAnnotationAttributeWrapper { +public class AliasedAnnotationAttribute extends AbstractWrappedAnnotationAttribute { protected AliasedAnnotationAttribute(AnnotationAttribute origin, AnnotationAttribute linked) { super(origin, linked); diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationAggregator.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationAggregator.java new file mode 100644 index 000000000..25e15d1cf --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationAggregator.java @@ -0,0 +1,49 @@ +package cn.hutool.core.annotation; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; + +/** + * 表示一组被聚合在一起的注解对象 + * + * @author huangchengxing + */ +public interface AnnotationAggregator extends AnnotatedElement { + + /** + * 获取在聚合中存在的指定注解对象 + * + * @param annotationType 注解类型 + * @param 注解类型 + * @return 注解对象 + */ + @Override + T getAnnotation(Class annotationType); + + /** + * 在聚合中是否存在的指定类型注解对象 + * + * @param annotationType 注解类型 + * @return 是否 + */ + @Override + boolean isAnnotationPresent(Class annotationType); + + /** + * 获取聚合中的全部注解对象 + * + * @return 注解对象 + */ + @Override + Annotation[] getAnnotations(); + + /** + * 从聚合中获取指定类型的属性值 + * + * @param attributeName 属性名称 + * @param attributeType 属性类型 + * @return 属性值 + */ + Object getAttribute(String attributeName, Class attributeType); + +} 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 index aface5b92..77631b12d 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationAttribute.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationAttribute.java @@ -1,6 +1,5 @@ package cn.hutool.core.annotation; -import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ReflectUtil; import java.lang.annotation.Annotation; @@ -8,7 +7,7 @@ import java.lang.reflect.Method; /** *

表示注解的某个属性,等同于绑定的调用对象的{@link Method}方法。
- * 在{@link SyntheticAnnotation}的解析以及取值过程中, + * 在{@link SynthesizedAnnotationAggregator}的解析以及取值过程中, * 可以通过设置{@link SynthesizedAnnotation}的注解属性, * 从而使得可以从一个注解对象中属性获取另一个注解对象的属性值 * @@ -16,9 +15,9 @@ import java.lang.reflect.Method; * * @author huangchengxing * @see SynthesizedAnnotationPostProcessor - * @see AnnotationAttributeWrapper + * @see WrappedAnnotationAttribute * @see CacheableAnnotationAttribute - * @see AbstractAnnotationAttributeWrapper + * @see AbstractWrappedAnnotationAttribute * @see ForceAliasedAnnotationAttribute * @see AliasedAnnotationAttribute * @see MirroredAnnotationAttribute @@ -71,9 +70,7 @@ public interface AnnotationAttribute { * * @return 该注解属性的值是否等于默认值 */ - default boolean isValueEquivalentToDefaultValue() { - return ObjectUtil.equals(getValue(), getAttribute().getDefaultValue()); - } + boolean isValueEquivalentToDefaultValue(); /** * 获取属性类型 @@ -95,7 +92,7 @@ public interface AnnotationAttribute { } /** - * 当前注解属性是否已经被{@link AnnotationAttributeWrapper}包装 + * 当前注解属性是否已经被{@link WrappedAnnotationAttribute}包装 * * @return boolean */ 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 08c40fcb0..8662caf8b 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 @@ -3,10 +3,7 @@ package cn.hutool.core.annotation; import cn.hutool.core.annotation.scanner.*; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.exceptions.UtilException; -import cn.hutool.core.lang.Assert; -import cn.hutool.core.lang.Opt; import cn.hutool.core.util.ArrayUtil; -import cn.hutool.core.util.ClassUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ReflectUtil; @@ -15,10 +12,8 @@ import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.*; -import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; -import java.util.stream.Stream; /** * 注解工具类
@@ -356,11 +351,11 @@ public class AnnotationUtil { * @param annotationType 注解类 * @param 注解类型 * @return 合成注解 - * @see SyntheticAnnotation + * @see SynthesizedAnnotationAggregator */ public static T getSynthesisAnnotation(Annotation annotation, Class annotationType) { // TODO 缓存合成注解信息,避免重复解析 - return SyntheticAnnotation.of(annotation).syntheticAnnotation(annotationType); + return SynthesizedAnnotationAggregator.of(annotation).synthesize(annotationType); } /** @@ -374,7 +369,7 @@ public class AnnotationUtil { * @param annotationType 注解类 * @param 注解类型 * @return 合成注解 - * @see SyntheticAnnotation + * @see SynthesizedAnnotationAggregator */ public static List getAllSynthesisAnnotations(AnnotatedElement annotatedEle, Class annotationType) { AnnotationScanner[] scanners = new AnnotationScanner[]{ @@ -397,9 +392,13 @@ public class AnnotationUtil { * @param annotationType 注解类 * @param 注解类型 * @return 合成注解 - * @see SyntheticAnnotation + * @see SynthesizedAnnotationAggregator */ public static T getSyntheticAnnotation(AnnotatedElement annotatedEle, Class annotationType) { + T target = annotatedEle.getAnnotation(annotationType); + if (ObjectUtil.isNotNull(target)) { + return target; + } AnnotationScanner[] scanners = new AnnotationScanner[]{ new MetaAnnotationScanner(), new TypeAnnotationScanner(), new MethodAnnotationScanner(), new FieldAnnotationScanner() }; @@ -471,36 +470,4 @@ public class AnnotationUtil { return method.getParameterCount() == 0 && method.getReturnType() != void.class; } - /** - * 获取注解的全部属性值获取方法 - * - * @param annotationType 注解 - * @return 注解的全部属性值 - * @throws IllegalArgumentException 当别名属性在注解中不存在,或别名属性的值与原属性的值类型不一致时抛出 - */ - static Map getAttributeMethods(Class annotationType) { - // 获取全部注解属性值 - Map attributeMethods = Stream.of(annotationType.getDeclaredMethods()) - .filter(AnnotationUtil::isAttributeMethod) - .collect(Collectors.toMap(Method::getName, Function.identity())); - // 处理别名 - attributeMethods.forEach((methodName, method) -> { - String alias = Opt.ofNullable(method.getAnnotation(Alias.class)) - .map(Alias::value) - .orElse(null); - if (ObjectUtil.isNull(alias)) { - return; - } - // 存在别名,则将原本的值替换为别名对应的值 - Assert.isTrue(attributeMethods.containsKey(alias), "No method for alias: [{}]", alias); - Method aliasAttributeMethod = attributeMethods.get(alias); - Assert.isTrue( - ObjectUtil.isNull(aliasAttributeMethod) || ClassUtil.isAssignable(method.getReturnType(), aliasAttributeMethod.getReturnType()), - "Return type of the alias method [{}] is inconsistent with the original [{}]", - aliasAttributeMethod.getClass(), method.getParameterTypes() - ); - attributeMethods.put(methodName, aliasAttributeMethod); - }); - return attributeMethods; - } } 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 index 72318ffba..6638bcbd2 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/ForceAliasedAnnotationAttribute.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/ForceAliasedAnnotationAttribute.java @@ -10,7 +10,7 @@ package cn.hutool.core.annotation; * @see RelationType#ALIAS_FOR * @see RelationType#FORCE_ALIAS_FOR */ -public class ForceAliasedAnnotationAttribute extends AbstractAnnotationAttributeWrapper { +public class ForceAliasedAnnotationAttribute extends AbstractWrappedAnnotationAttribute { protected ForceAliasedAnnotationAttribute(AnnotationAttribute origin, AnnotationAttribute linked) { super(origin, linked); 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 index cb841fe79..6d1bb3f50 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/Link.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/Link.java @@ -4,7 +4,7 @@ import java.lang.annotation.*; /** *

用于在同一注解中,或具有一定关联的不同注解的属性中,表明这些属性之间具有特定的关联关系。 - * 在通过{@link SyntheticAnnotation}获取合成注解后,合成注解获取属性值时会根据该注解进行调整。
+ * 在通过{@link SynthesizedAnnotationAggregator}获取合成注解后,合成注解获取属性值时会根据该注解进行调整。
* *

该注解存在三个字注解:{@link MirrorFor}、{@link ForceAliasFor}或{@link AliasFor}, * 使用三个子注解等同于{@link Link}。但是需要注意的是, @@ -14,7 +14,7 @@ import java.lang.annotation.*; * 注意:该注解的优先级低于{@link Alias} * * @author huangchengxing - * @see SyntheticAnnotation + * @see SynthesizedAnnotationAggregator * @see RelationType * @see AliasFor * @see MirrorFor 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 index 7c370ff5a..8547b8a9b 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/MirrorLinkAttributePostProcessor.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/MirrorLinkAttributePostProcessor.java @@ -20,7 +20,7 @@ public class MirrorLinkAttributePostProcessor implements SynthesizedAnnotationPo } @Override - public void process(SynthesizedAnnotation annotation, SyntheticAnnotation syntheticAnnotation) { + public void process(SynthesizedAnnotation annotation, SynthesizedAnnotationAggregator synthesizedAnnotationAggregator) { Map attributeMap = new HashMap<>(annotation.getAttributes()); attributeMap.forEach((originalAttributeName, originalAttribute) -> { // 跳过已经解析的镜像属性 @@ -35,7 +35,7 @@ public class MirrorLinkAttributePostProcessor implements SynthesizedAnnotationPo } // 获取指定镜像属性所在的注解 - final SynthesizedAnnotation mirrorAnnotation = SyntheticAnnotationUtil.getLinkedAnnotation(link, syntheticAnnotation, annotation.annotationType()); + final SynthesizedAnnotation mirrorAnnotation = SyntheticAnnotationUtil.getLinkedAnnotation(link, synthesizedAnnotationAggregator, annotation.annotationType()); if (ObjectUtil.isNull(mirrorAnnotation)) { return; } @@ -46,7 +46,7 @@ public class MirrorLinkAttributePostProcessor implements SynthesizedAnnotationPo // 包装这一对镜像属性,并替换原注解中的对应属性 final AnnotationAttribute mirroredOriginalAttribute = new MirroredAnnotationAttribute(originalAttribute, mirrorAttribute); - syntheticAnnotation.getSynthesizedAnnotation(originalAttribute.getAnnotationType()) + synthesizedAnnotationAggregator.getSynthesizedAnnotation(originalAttribute.getAnnotationType()) .setAttribute(originalAttributeName, mirroredOriginalAttribute); final AnnotationAttribute mirroredTargetAttribute = new MirroredAnnotationAttribute(mirrorAttribute, originalAttribute); mirrorAnnotation.setAttribute(link.attribute(), mirroredTargetAttribute); @@ -65,6 +65,7 @@ public class MirrorLinkAttributePostProcessor implements SynthesizedAnnotationPo "mirror attribute [{}] of original attribute [{}] must marked by @Link, and also @LinkType.type() must is [{}]", mirror.getAttribute(), original.getAttribute(), RelationType.MIRROR_FOR ); + SyntheticAnnotationUtil.checkLinkedSelf(original, mirror); } } 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 index 20d2305ce..e8f0f8efd 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/MirroredAnnotationAttribute.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/MirroredAnnotationAttribute.java @@ -9,7 +9,7 @@ import cn.hutool.core.lang.Assert; * @see MirrorLinkAttributePostProcessor * @see RelationType#MIRROR_FOR */ -public class MirroredAnnotationAttribute extends AbstractAnnotationAttributeWrapper { +public class MirroredAnnotationAttribute extends AbstractWrappedAnnotationAttribute { public MirroredAnnotationAttribute(AnnotationAttribute origin, AnnotationAttribute linked) { super(origin, linked); 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 index ee739ae2d..accfd98ac 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/RelationType.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/RelationType.java @@ -3,8 +3,8 @@ package cn.hutool.core.annotation; /** *

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

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

    @@ -15,7 +15,7 @@ package cn.hutool.core.annotation; *
* * @author huangchengxing - * @see SyntheticAnnotation + * @see SynthesizedAnnotationAggregator * @see Link */ public enum RelationType { 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 fc51eb4d1..2607d94f7 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 @@ -7,14 +7,14 @@ import java.util.Map; import java.util.function.UnaryOperator; /** - *

用于在{@link SyntheticAnnotation}中表示一个处于合成状态的注解对象。
+ *

用于在{@link SynthesizedAnnotationAggregator}中表示一个处于合成状态的注解对象。
* 当对多个合成注解排序时,默认先按{@link #getVerticalDistance()}排序, * 再按{@link #getHorizontalDistance()}排序。 * 该顺序应当保证当合成注解与其他注解存在层级关系时, * 离根对象最接近的注解被排在靠前的位置。 * * @author huangchengxing - * @see SyntheticAnnotation + * @see SynthesizedAnnotationAggregator */ public interface SynthesizedAnnotation extends Annotation, Comparable { @@ -23,7 +23,7 @@ public interface SynthesizedAnnotation extends Annotation, Comparable合成注解一般被用于处理类层级结果中具有直接或间接关联的注解对象, * 当实例被创建时,会获取到这些注解对象,并使用{@link SynthesizedAnnotationSelector}对类型相同的注解进行过滤, * 并最终得到类型不重复的有效注解对象。这些有效注解将被包装为{@link SynthesizedAnnotation}, - * 然后最终用于“合成”一个{@link SyntheticAnnotation}。
+ * 然后最终用于“合成”一个{@link SynthesizedAnnotationAggregator}。
* {@link SynthesizedAnnotationSelector}是合成注解生命周期中的第一个钩子, * 自定义选择器以拦截原始注解被扫描的过程。 * @@ -21,7 +21,7 @@ import java.util.Collection; * {@link SynthesizedAnnotationPostProcessor}是合成注解生命周期中的第二个钩子, * 自定义后置处理器以拦截原始在转为待合成注解后的初始化过程。 * - *

合成注解允许通过{@link #syntheticAnnotation(Class)}合成一个指定的注解对象, + *

合成注解允许通过{@link #synthesize(Class)}合成一个指定的注解对象, * 该方法返回的注解对象可能是原始的注解对象,也有可能通过动态代理的方式生成, * 该对象实例的属性不一定来自对象本身,而是来自于经过{@link SynthesizedAnnotationAttributeProcessor} * 处理后的、用于合成当前实例的全部关联注解的相关属性。
@@ -37,9 +37,9 @@ import java.util.Collection; * @see SynthesizedAnnotationSelector * @see SynthesizedAnnotationAttributeProcessor * @see SynthesizedAnnotationPostProcessor - * @see SyntheticMetaAnnotation + * @see SynthesizedMetaAnnotationAggregator */ -public interface SyntheticAnnotation extends Annotation, AnnotatedElement { +public interface SynthesizedAnnotationAggregator extends Annotation, AnnotationAggregator { /** * 获取合成注解选择器 @@ -53,14 +53,14 @@ public interface SyntheticAnnotation extends Annotation, AnnotatedElement { * * @return 合成注解属性处理器 */ - SynthesizedAnnotationAttributeProcessor getAttributeProcessor(); + SynthesizedAnnotationAttributeProcessor getAnnotationAttributeProcessor(); /** * 获取合成注解属性后置处理器 * * @return 合成注解属性后置处理器 */ - Collection getSynthesizedAnnotationAttributePostProcessors(); + Collection getAnnotationAttributePostProcessors(); /** * 获取已合成的注解 @@ -75,7 +75,7 @@ public interface SyntheticAnnotation extends Annotation, AnnotatedElement { * * @return 合成注解 */ - Collection getAllSyntheticAnnotations(); + Collection getAllSynthesizedAnnotation(); /** * 获取当前的注解类型 @@ -87,33 +87,6 @@ public interface SyntheticAnnotation extends Annotation, AnnotatedElement { return this.getClass(); } - /** - * 获取指定注解对象 - * - * @param annotationType 注解类型 - * @param 注解类型 - * @return 注解对象 - */ - @Override - T getAnnotation(Class annotationType); - - /** - * 是否存在指定注解 - * - * @param annotationType 注解类型 - * @return 是否 - */ - @Override - boolean isAnnotationPresent(Class annotationType); - - /** - * 获取全部注解 - * - * @return 注解对象 - */ - @Override - Annotation[] getAnnotations(); - /** * 获取合成注解 * @@ -121,16 +94,7 @@ public interface SyntheticAnnotation extends Annotation, AnnotatedElement { * @param 注解类型 * @return 类型 */ - T syntheticAnnotation(Class annotationType); - - /** - * 获取属性值 - * - * @param attributeName 属性名称 - * @param attributeType 属性类型 - * @return 属性值 - */ - Object getAttribute(String attributeName, Class attributeType); + T synthesize(Class annotationType); /** * 基于指定根注解,构建包括其元注解在内的合成注解 @@ -139,8 +103,8 @@ public interface SyntheticAnnotation extends Annotation, AnnotatedElement { * @param 注解类型 * @return 合成注解 */ - static SyntheticAnnotation of(T rootAnnotation) { - return new SyntheticMetaAnnotation(rootAnnotation); + static SynthesizedAnnotationAggregator of(T rootAnnotation) { + return new SynthesizedMetaAnnotationAggregator(rootAnnotation); } } diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationAttributeProcessor.java b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationAttributeProcessor.java index ad0464f5b..cf8b44cc6 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationAttributeProcessor.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationAttributeProcessor.java @@ -3,7 +3,7 @@ package cn.hutool.core.annotation; import java.util.Collection; /** - * 合成注解属性选择器。用于在{@link SyntheticAnnotation}中从指定类型的合成注解里获取到对应的属性值 + * 合成注解属性选择器。用于在{@link SynthesizedAnnotationAggregator}中从指定类型的合成注解里获取到对应的属性值 * * @author huangchengxing */ 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 index 7e5cce1f0..c8028dd46 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationPostProcessor.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationPostProcessor.java @@ -5,7 +5,7 @@ import cn.hutool.core.comparator.CompareUtil; import java.util.Comparator; /** - *

被合成注解后置处理器,用于在{@link SyntheticAnnotation}加载完所有待合成注解后, + *

被合成注解后置处理器,用于在{@link SynthesizedAnnotationAggregator}加载完所有待合成注解后, * 再对加载好的{@link SynthesizedAnnotation}进行后置处理。
* 当多个{@link SynthesizedAnnotationPostProcessor}需要一起执行时,将按照{@link #order()}的返回值进行排序, * 该值更小的处理器将被优先执行。 @@ -49,8 +49,8 @@ public interface SynthesizedAnnotationPostProcessor extends Comparable - * 该接口用于在{@link SyntheticAnnotation}中用于从一批相同的注解对象中筛选最终用于合成注解对象。 + * 该接口用于在{@link SynthesizedAnnotationAggregator}中用于从一批相同的注解对象中筛选最终用于合成注解对象。 * * @author huangchengxing */ diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticMetaAnnotation.java b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedMetaAnnotationAggregator.java similarity index 90% rename from hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticMetaAnnotation.java rename to hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedMetaAnnotationAggregator.java index 26bc5f73f..98d29088e 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticMetaAnnotation.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedMetaAnnotationAggregator.java @@ -15,10 +15,10 @@ import java.util.stream.Collectors; import java.util.stream.Stream; /** - * {@link SyntheticAnnotation}的基本实现,表示一个根注解与根注解上的多层元注解合成的注解 + * {@link SynthesizedAnnotationAggregator}的基本实现,表示一个根注解与根注解上的多层元注解的聚合状态 * *

假设现有注解A,A上存在元注解B,B上存在元注解C,则对注解A进行解析, - * 将得到包含根注解A,以及其元注解B、C在内的合成元注解{@link SyntheticMetaAnnotation}。 + * 将得到包含根注解A,以及其元注解B、C在内的合成元注解聚合{@link SynthesizedMetaAnnotationAggregator}。 * 从{@link AnnotatedElement}的角度来说,得到的合成注解是一个同时承载有ABC三个注解对象的被注解元素, * 因此通过调用{@link AnnotatedElement}的相关方法将返回对应符合语义的注解对象。 * @@ -39,8 +39,8 @@ import java.util.stream.Stream; * * 若用户需要自行扩展,则需要保证上述三个处理器被正确注入当前实例。 * - *

{@link SyntheticMetaAnnotation}支持通过{@link #getAttribute(String, Class)}, - * 或通过{@link #syntheticAnnotation(Class)}获得注解代理对象后获取指定类型的注解属性值, + *

{@link SynthesizedMetaAnnotationAggregator}支持通过{@link #getAttribute(String, Class)}, + * 或通过{@link #synthesize(Class)}获得注解代理对象后获取指定类型的注解属性值, * 返回的属性值将根据合成注解中对应原始注解属性上的{@link Alias}与{@link Link}注解而有所变化。 * 通过当前实例获取属性值时,将经过{@link SynthesizedAnnotationAttributeProcessor}的处理。
* 默认情况下,实例将会注册{@link CacheableSynthesizedAnnotationAttributeProcessor}, @@ -50,7 +50,7 @@ import java.util.stream.Stream; * @see AnnotationUtil * @see SynthesizedAnnotationSelector */ -public class SyntheticMetaAnnotation implements SyntheticAnnotation { +public class SynthesizedMetaAnnotationAggregator implements SynthesizedAnnotationAggregator { /** * 根注解,即当前查找的注解 @@ -84,7 +84,7 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation { * * @param source 源注解 */ - public SyntheticMetaAnnotation(Annotation source) { + public SynthesizedMetaAnnotationAggregator(Annotation source) { this( source, SynthesizedAnnotationSelector.NEAREST_AND_OLDEST_PRIORITY, new CacheableSynthesizedAnnotationAttributeProcessor(), @@ -103,7 +103,7 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation { * @param annotationSelector 合成注解选择器 * @param attributeProcessor 注解属性处理器 */ - public SyntheticMetaAnnotation( + public SynthesizedMetaAnnotationAggregator( Annotation annotation, SynthesizedAnnotationSelector annotationSelector, SynthesizedAnnotationAttributeProcessor attributeProcessor, @@ -137,15 +137,6 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation { return source; } - /** - * 获取已解析的元注解信息 - * - * @return 已解析的元注解信息 - */ - Map, SynthesizedAnnotation> getMetaAnnotationMap() { - return metaAnnotationMap; - } - /** * 获取合成注解选择器 * @@ -162,7 +153,7 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation { * @return 合成注解属性处理器 */ @Override - public SynthesizedAnnotationAttributeProcessor getAttributeProcessor() { + public SynthesizedAnnotationAttributeProcessor getAnnotationAttributeProcessor() { return this.attributeProcessor; } @@ -172,7 +163,7 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation { * @return 合成注解属性后置处理器 */ @Override - public Collection getSynthesizedAnnotationAttributePostProcessors() { + public Collection getAnnotationAttributePostProcessors() { return this.postProcessors; } @@ -193,7 +184,7 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation { * @return 合成注解 */ @Override - public Collection getAllSyntheticAnnotations() { + public Collection getAllSynthesizedAnnotation() { return metaAnnotationMap.values(); } @@ -254,10 +245,10 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation { * * @param annotationType 注解类型 * @return 合成注解对象 - * @see SyntheticAnnotationProxy#create(Class, SyntheticAnnotation) + * @see SyntheticAnnotationProxy#create(Class, SynthesizedAnnotationAggregator) */ @Override - public T syntheticAnnotation(Class annotationType) { + public T synthesize(Class annotationType) { return SyntheticAnnotationProxy.create(annotationType, this); } @@ -299,14 +290,14 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation { */ public static class MetaAnnotation implements Annotation, SynthesizedAnnotation { - private final SyntheticAnnotation owner; + private final SynthesizedAnnotationAggregator owner; private final Annotation root; private final Annotation annotation; private final Map attributeMethodCaches; private final int verticalDistance; private final int horizontalDistance; - public MetaAnnotation(SyntheticAnnotation owner, Annotation root, Annotation annotation, int verticalDistance, int horizontalDistance) { + public MetaAnnotation(SynthesizedAnnotationAggregator owner, Annotation root, Annotation annotation, int verticalDistance, int horizontalDistance) { this.owner = owner; this.root = root; this.annotation = annotation; @@ -323,7 +314,7 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation { * @return 合成注解 */ @Override - public SyntheticAnnotation getOwner() { + public SynthesizedAnnotationAggregator getOwner() { return owner; } 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 e71208c8a..8a8688944 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 @@ -38,18 +38,18 @@ class SyntheticAnnotationProxy implements InvocationHandler { /** * 创建一个代理注解,生成的代理对象将是{@link SyntheticProxyAnnotation}与指定的注解类的子类。 *

    - *
  • 当作为{@code annotationType}所指定的类型使用时,其属性将通过合成它的{@link SyntheticAnnotation}获取;
  • + *
  • 当作为{@code annotationType}所指定的类型使用时,其属性将通过合成它的{@link SynthesizedAnnotationAggregator}获取;
  • *
  • 当作为{@link SyntheticProxyAnnotation}或{@link SynthesizedAnnotation}使用时,将可以获得原始注解实例的相关信息;
  • *
* * @param annotationType 注解类型 - * @param syntheticAnnotation 合成注解 + * @param synthesizedAnnotationAggregator 合成注解 * @return 代理注解 */ @SuppressWarnings("unchecked") static T create( - Class annotationType, SyntheticAnnotation syntheticAnnotation) { - final SynthesizedAnnotation annotation = syntheticAnnotation.getSynthesizedAnnotation(annotationType); + Class annotationType, SynthesizedAnnotationAggregator synthesizedAnnotationAggregator) { + final SynthesizedAnnotation annotation = synthesizedAnnotationAggregator.getSynthesizedAnnotation(annotationType); if (ObjectUtil.isNull(annotation)) { return null; } @@ -83,7 +83,6 @@ class SyntheticAnnotationProxy implements InvocationHandler { methods.put("getSyntheticAnnotation", (method, args) -> proxyGetSyntheticAnnotation()); methods.put("getSynthesizedAnnotation", (method, args) -> proxyGetSynthesizedAnnotation()); methods.put("getRoot", (method, args) -> annotation.getRoot()); - methods.put("isRoot", (method, args) -> annotation.isRoot()); 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])); @@ -135,7 +134,7 @@ class SyntheticAnnotationProxy implements InvocationHandler { * * @return 合成注解 */ - SyntheticAnnotation getSyntheticAnnotation(); + SynthesizedAnnotationAggregator getSyntheticAnnotation(); /** * 获取该代理注解对应的已合成注解 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 index 71746cdf1..dc054c90f 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticAnnotationUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticAnnotationUtil.java @@ -9,7 +9,7 @@ import java.lang.annotation.Annotation; import java.util.Comparator; /** - * {@link SyntheticAnnotation}相关工具,用于内部使用 + * {@link SynthesizedAnnotationAggregator}相关工具,用于内部使用 * * @author huangchengxing */ @@ -33,12 +33,12 @@ class SyntheticAnnotationUtil { * 从合成注解中获取{@link Link#type()}指定的注解对象 * * @param annotation {@link Link}注解 - * @param syntheticAnnotation 合成注解 + * @param synthesizedAnnotationAggregator 合成注解 */ static SynthesizedAnnotation getLinkedAnnotation( - Link annotation, SyntheticAnnotation syntheticAnnotation, Class defaultType) { + Link annotation, SynthesizedAnnotationAggregator synthesizedAnnotationAggregator, Class defaultType) { final Class targetAnnotationType = getLinkedAnnotationType(annotation, defaultType); - return syntheticAnnotation.getSynthesizedAnnotation(targetAnnotationType); + return synthesizedAnnotationAggregator.getSynthesizedAnnotation(targetAnnotationType); } /** @@ -68,6 +68,17 @@ class SyntheticAnnotationUtil { ); } + /** + * 检查{@link Link}指向的注解属性是否就是本身 + * + * @param original {@link Link}注解的属性 + * @param linked {@link Link}指向的注解属性 + */ + static void checkLinkedSelf(AnnotationAttribute original, AnnotationAttribute linked) { + boolean linkSelf = (original == linked) || ObjectUtil.equals(original.getAttribute(), linked.getAttribute()); + Assert.isFalse(linkSelf, "cannot link self [{}]", original.getAttribute()); + } + /** * 检查{@link Link}指向的注解属性是否存在 * diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationAttributeWrapper.java b/hutool-core/src/main/java/cn/hutool/core/annotation/WrappedAnnotationAttribute.java similarity index 92% rename from hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationAttributeWrapper.java rename to hutool-core/src/main/java/cn/hutool/core/annotation/WrappedAnnotationAttribute.java index 54caa5d98..6ca5a3f52 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationAttributeWrapper.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/WrappedAnnotationAttribute.java @@ -27,7 +27,7 @@ import java.util.Collection; * @see AliasedAnnotationAttribute * @see MirroredAnnotationAttribute */ -public interface AnnotationAttributeWrapper extends AnnotationAttribute { +public interface WrappedAnnotationAttribute extends AnnotationAttribute { // =========================== 新增方法 =========================== @@ -89,9 +89,7 @@ public interface AnnotationAttributeWrapper extends AnnotationAttribute { * @return 该注解属性的值是否等于默认值 */ @Override - default boolean isValueEquivalentToDefaultValue() { - return getOriginal().isValueEquivalentToDefaultValue() && getLinked().isValueEquivalentToDefaultValue(); - } + boolean isValueEquivalentToDefaultValue(); /** * 获取属性类型 @@ -115,7 +113,7 @@ public interface AnnotationAttributeWrapper extends AnnotationAttribute { } /** - * 当前注解属性是否已经被{@link AnnotationAttributeWrapper}包装 + * 当前注解属性是否已经被{@link WrappedAnnotationAttribute}包装 * * @return boolean */ diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/AbstractAnnotationAttributeWrapperTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/AbstractWrappedAnnotationAttributeTest.java similarity index 81% rename from hutool-core/src/test/java/cn/hutool/core/annotation/AbstractAnnotationAttributeWrapperTest.java rename to hutool-core/src/test/java/cn/hutool/core/annotation/AbstractWrappedAnnotationAttributeTest.java index b828ab888..66059a18d 100644 --- a/hutool-core/src/test/java/cn/hutool/core/annotation/AbstractAnnotationAttributeWrapperTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/AbstractWrappedAnnotationAttributeTest.java @@ -8,7 +8,7 @@ import org.junit.Test; import java.lang.annotation.*; import java.lang.reflect.Method; -public class AbstractAnnotationAttributeWrapperTest { +public class AbstractWrappedAnnotationAttributeTest { @Test public void workTest() { @@ -17,7 +17,9 @@ public class AbstractAnnotationAttributeWrapperTest { CacheableAnnotationAttribute valueAttribute = new CacheableAnnotationAttribute(annotation, valueMethod); Method nameMethod = ReflectUtil.getMethod(AnnotationForTest1.class, "name1"); CacheableAnnotationAttribute nameAttribute = new CacheableAnnotationAttribute(annotation, nameMethod); - TestAnnotationAttributeWrapper nameWrapper = new TestAnnotationAttributeWrapper(nameAttribute, valueAttribute); + AbstractWrappedAnnotationAttribute nameWrapper = new TestWrappedAnnotationAttribute(nameAttribute, valueAttribute); + + Assert.assertEquals(nameWrapper.getAnnotation(), annotation); // 注解属性 Assert.assertEquals(annotation, nameWrapper.getAnnotation()); @@ -40,7 +42,7 @@ public class AbstractAnnotationAttributeWrapperTest { CacheableAnnotationAttribute value1Attribute = new CacheableAnnotationAttribute(annotation1, value1Method); Method name1Method = ReflectUtil.getMethod(AnnotationForTest1.class, "name1"); CacheableAnnotationAttribute name1Attribute = new CacheableAnnotationAttribute(annotation1, name1Method); - TestAnnotationAttributeWrapper wrapper1 = new TestAnnotationAttributeWrapper(name1Attribute, value1Attribute); + AbstractWrappedAnnotationAttribute wrapper1 = new TestWrappedAnnotationAttribute(name1Attribute, value1Attribute); Assert.assertEquals(name1Attribute, wrapper1.getNonWrappedOriginal()); Assert.assertEquals(CollUtil.newArrayList(name1Attribute, value1Attribute), wrapper1.getAllLinkedNonWrappedAttributes()); @@ -48,7 +50,7 @@ public class AbstractAnnotationAttributeWrapperTest { Annotation annotation2 = ClassForTest1.class.getAnnotation(AnnotationForTest2.class); Method value2Method = ReflectUtil.getMethod(AnnotationForTest2.class, "value2"); CacheableAnnotationAttribute value2Attribute = new CacheableAnnotationAttribute(annotation2, value2Method); - TestAnnotationAttributeWrapper wrapper2 = new TestAnnotationAttributeWrapper(wrapper1, value2Attribute); + AbstractWrappedAnnotationAttribute wrapper2 = new TestWrappedAnnotationAttribute(wrapper1, value2Attribute); Assert.assertEquals(name1Attribute, wrapper2.getNonWrappedOriginal()); Assert.assertEquals(CollUtil.newArrayList(name1Attribute, value1Attribute, value2Attribute), wrapper2.getAllLinkedNonWrappedAttributes()); @@ -56,20 +58,25 @@ public class AbstractAnnotationAttributeWrapperTest { Annotation annotation3 = ClassForTest1.class.getAnnotation(AnnotationForTest3.class); Method value3Method = ReflectUtil.getMethod(AnnotationForTest3.class, "value3"); CacheableAnnotationAttribute value3Attribute = new CacheableAnnotationAttribute(annotation3, value3Method); - TestAnnotationAttributeWrapper wrapper3 = new TestAnnotationAttributeWrapper(value3Attribute, wrapper2); + AbstractWrappedAnnotationAttribute wrapper3 = new TestWrappedAnnotationAttribute(value3Attribute, wrapper2); Assert.assertEquals(value3Attribute, wrapper3.getNonWrappedOriginal()); Assert.assertEquals(CollUtil.newArrayList(value3Attribute, name1Attribute, value1Attribute, value2Attribute), wrapper3.getAllLinkedNonWrappedAttributes()); } - static class TestAnnotationAttributeWrapper extends AbstractAnnotationAttributeWrapper { - protected TestAnnotationAttributeWrapper(AnnotationAttribute original, AnnotationAttribute linked) { + static class TestWrappedAnnotationAttribute extends AbstractWrappedAnnotationAttribute { + protected TestWrappedAnnotationAttribute(AnnotationAttribute original, AnnotationAttribute linked) { super(original, linked); } @Override public Object getValue() { return linked.getValue(); } + + @Override + public boolean isValueEquivalentToDefaultValue() { + return getOriginal().isValueEquivalentToDefaultValue() && getLinked().isValueEquivalentToDefaultValue(); + } } @Retention(RetentionPolicy.RUNTIME) diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/AliasAttributePostProcessorTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/AliasAttributePostProcessorTest.java new file mode 100644 index 000000000..f7ad67e8b --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/AliasAttributePostProcessorTest.java @@ -0,0 +1,190 @@ +package cn.hutool.core.annotation; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.ReflectUtil; +import org.junit.Assert; +import org.junit.Test; + +import java.lang.annotation.*; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.function.UnaryOperator; + +public class AliasAttributePostProcessorTest { + + @Test + public void processTest() { + AliasAttributePostProcessor processor = new AliasAttributePostProcessor(); + + Map, SynthesizedAnnotation> annotationMap = new HashMap<>(); + SynthesizedAnnotationAggregator synthesizedAnnotationAggregator = new TestSynthesizedAnnotationAggregator(annotationMap); + AnnotationForTest annotation = ClassForTest.class.getAnnotation(AnnotationForTest.class); + SynthesizedAnnotation synthesizedAnnotation = new TestSynthesizedAnnotation(synthesizedAnnotationAggregator, annotation); + annotationMap.put(annotation.annotationType(), synthesizedAnnotation); + + processor.process(synthesizedAnnotation, synthesizedAnnotationAggregator); + AnnotationAttribute valueAttribute = synthesizedAnnotation.getAttributes().get("value"); + Assert.assertEquals(ReflectUtil.getMethod(AnnotationForTest.class, "value"), valueAttribute.getAttribute()); + Assert.assertTrue(valueAttribute.isWrapped()); + Assert.assertEquals(ForceAliasedAnnotationAttribute.class, valueAttribute.getClass()); + + AnnotationAttribute nameAttribute = synthesizedAnnotation.getAttributes().get("name"); + Assert.assertEquals(ReflectUtil.getMethod(AnnotationForTest.class, "name"), nameAttribute.getAttribute()); + Assert.assertFalse(nameAttribute.isWrapped()); + Assert.assertEquals(CacheableAnnotationAttribute.class, nameAttribute.getClass()); + + Assert.assertEquals(nameAttribute, ((WrappedAnnotationAttribute)valueAttribute).getLinked()); + } + + @AnnotationForTest + static class ClassForTest {} + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.METHOD, ElementType.TYPE }) + @interface AnnotationForTest { + @Alias("name") + String value() default ""; + String name() default ""; + } + + static class TestSynthesizedAnnotationAggregator implements SynthesizedAnnotationAggregator { + + private final Map, SynthesizedAnnotation> annotationMap; + + public TestSynthesizedAnnotationAggregator(Map, SynthesizedAnnotation> annotationMap) { + this.annotationMap = annotationMap; + } + + @Override + public SynthesizedAnnotationSelector getAnnotationSelector() { + return null; + } + + @Override + public SynthesizedAnnotationAttributeProcessor getAnnotationAttributeProcessor() { + return null; + } + + @Override + public Collection getAnnotationAttributePostProcessors() { + return null; + } + + @Override + public SynthesizedAnnotation getSynthesizedAnnotation(Class annotationType) { + return annotationMap.get(annotationType); + } + + @Override + public Collection getAllSynthesizedAnnotation() { + return null; + } + + @Override + public T getAnnotation(Class annotationType) { + return null; + } + + @Override + public boolean isAnnotationPresent(Class annotationType) { + return false; + } + + @Override + public Annotation[] getAnnotations() { + return new Annotation[0]; + } + + @Override + public T synthesize(Class annotationType) { + return null; + } + + @Override + public Object getAttribute(String attributeName, Class attributeType) { + return null; + } + + @Override + public Annotation[] getDeclaredAnnotations() { + return new Annotation[0]; + } + } + + static class TestSynthesizedAnnotation implements SynthesizedAnnotation { + + private final Annotation annotation; + private final SynthesizedAnnotationAggregator owner; + private final Map attributeMap; + + public TestSynthesizedAnnotation(SynthesizedAnnotationAggregator owner, Annotation annotation) { + this.owner = owner; + this.attributeMap = new HashMap<>(); + this.annotation = annotation; + for (Method declaredMethod : annotation.annotationType().getDeclaredMethods()) { + attributeMap.put(declaredMethod.getName(), new CacheableAnnotationAttribute(annotation, declaredMethod)); + } + } + + @Override + public SynthesizedAnnotationAggregator getOwner() { + return owner; + } + + @Override + public Object getRoot() { + return null; + } + + @Override + public Annotation getAnnotation() { + return annotation; + } + + @Override + public int getVerticalDistance() { + return 0; + } + + @Override + public int getHorizontalDistance() { + return 0; + } + + @Override + public boolean hasAttribute(String attributeName, Class returnType) { + return false; + } + + @Override + public Map getAttributes() { + return attributeMap; + } + + @Override + public void setAttribute(String attributeName, AnnotationAttribute attribute) { + attributeMap.put(attributeName, attribute); + } + + @Override + public void replaceAttribute(String attributeName, UnaryOperator operator) { + AnnotationAttribute annotationAttribute = attributeMap.get(attributeName); + if (ObjectUtil.isNotNull(annotationAttribute)) { + attributeMap.put(attributeName, operator.apply(annotationAttribute)); + } + } + + @Override + public Object getAttributeValue(String attributeName) { + return null; + } + + @Override + public Class annotationType() { + return annotation.annotationType(); + } + } + +} diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/AliasForLinkAttributePostProcessorTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/AliasForLinkAttributePostProcessorTest.java new file mode 100644 index 000000000..5d0c4ad37 --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/AliasForLinkAttributePostProcessorTest.java @@ -0,0 +1,218 @@ +package cn.hutool.core.annotation; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.ReflectUtil; +import org.junit.Assert; +import org.junit.Test; + +import java.lang.annotation.*; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.function.UnaryOperator; + +public class AliasForLinkAttributePostProcessorTest { + + @Test + public void processForceAliasForTest() { + AliasForLinkAttributePostProcessor processor = new AliasForLinkAttributePostProcessor(); + + Map, SynthesizedAnnotation> annotationMap = new HashMap<>(); + SynthesizedAnnotationAggregator synthesizedAnnotationAggregator = new TestSynthesizedAnnotationAggregator(annotationMap); + AnnotationForTest annotation = ClassForTest.class.getAnnotation(AnnotationForTest.class); + SynthesizedAnnotation synthesizedAnnotation = new TestSynthesizedAnnotation(synthesizedAnnotationAggregator, annotation); + annotationMap.put(annotation.annotationType(), synthesizedAnnotation); + + processor.process(synthesizedAnnotation, synthesizedAnnotationAggregator); + AnnotationAttribute valueAttribute = synthesizedAnnotation.getAttributes().get("value"); + Assert.assertEquals(ReflectUtil.getMethod(AnnotationForTest.class, "value"), valueAttribute.getAttribute()); + Assert.assertFalse(valueAttribute.isWrapped()); + Assert.assertEquals(CacheableAnnotationAttribute.class, valueAttribute.getClass()); + + AnnotationAttribute nameAttribute = synthesizedAnnotation.getAttributes().get("name"); + Assert.assertEquals(ReflectUtil.getMethod(AnnotationForTest.class, "name"), nameAttribute.getAttribute()); + Assert.assertTrue(nameAttribute.isWrapped()); + Assert.assertEquals(ForceAliasedAnnotationAttribute.class, nameAttribute.getClass()); + + Assert.assertEquals(valueAttribute, ((WrappedAnnotationAttribute)nameAttribute).getLinked()); + } + + @Test + public void processAliasForTest() { + AliasForLinkAttributePostProcessor processor = new AliasForLinkAttributePostProcessor(); + + Map, SynthesizedAnnotation> annotationMap = new HashMap<>(); + SynthesizedAnnotationAggregator synthesizedAnnotationAggregator = new TestSynthesizedAnnotationAggregator(annotationMap); + AnnotationForTest annotation = ClassForTest.class.getAnnotation(AnnotationForTest.class); + SynthesizedAnnotation synthesizedAnnotation = new TestSynthesizedAnnotation(synthesizedAnnotationAggregator, annotation); + annotationMap.put(annotation.annotationType(), synthesizedAnnotation); + + processor.process(synthesizedAnnotation, synthesizedAnnotationAggregator); + AnnotationAttribute valueAttribute = synthesizedAnnotation.getAttributes().get("value2"); + Assert.assertEquals(ReflectUtil.getMethod(AnnotationForTest.class, "value2"), valueAttribute.getAttribute()); + Assert.assertFalse(valueAttribute.isWrapped()); + Assert.assertEquals(CacheableAnnotationAttribute.class, valueAttribute.getClass()); + + AnnotationAttribute nameAttribute = synthesizedAnnotation.getAttributes().get("name2"); + Assert.assertEquals(ReflectUtil.getMethod(AnnotationForTest.class, "name2"), nameAttribute.getAttribute()); + Assert.assertTrue(nameAttribute.isWrapped()); + Assert.assertEquals(AliasedAnnotationAttribute.class, nameAttribute.getClass()); + + Assert.assertEquals(valueAttribute, ((WrappedAnnotationAttribute)nameAttribute).getLinked()); + } + + @AnnotationForTest + static class ClassForTest {} + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.METHOD, ElementType.TYPE }) + @interface AnnotationForTest { + @Link(attribute = "name", type = RelationType.FORCE_ALIAS_FOR) + String value() default ""; + String name() default ""; + + @Link(attribute = "name2", type = RelationType.ALIAS_FOR) + String value2() default ""; + String name2() default ""; + } + + static class TestSynthesizedAnnotationAggregator implements SynthesizedAnnotationAggregator { + + private final Map, SynthesizedAnnotation> annotationMap; + + public TestSynthesizedAnnotationAggregator(Map, SynthesizedAnnotation> annotationMap) { + this.annotationMap = annotationMap; + } + + @Override + public SynthesizedAnnotationSelector getAnnotationSelector() { + return null; + } + + @Override + public SynthesizedAnnotationAttributeProcessor getAnnotationAttributeProcessor() { + return null; + } + + @Override + public Collection getAnnotationAttributePostProcessors() { + return null; + } + + @Override + public SynthesizedAnnotation getSynthesizedAnnotation(Class annotationType) { + return annotationMap.get(annotationType); + } + + @Override + public Collection getAllSynthesizedAnnotation() { + return null; + } + + @Override + public T getAnnotation(Class annotationType) { + return null; + } + + @Override + public boolean isAnnotationPresent(Class annotationType) { + return false; + } + + @Override + public Annotation[] getAnnotations() { + return new Annotation[0]; + } + + @Override + public T synthesize(Class annotationType) { + return null; + } + + @Override + public Object getAttribute(String attributeName, Class attributeType) { + return null; + } + + @Override + public Annotation[] getDeclaredAnnotations() { + return new Annotation[0]; + } + } + + static class TestSynthesizedAnnotation implements SynthesizedAnnotation { + + private final Annotation annotation; + private final SynthesizedAnnotationAggregator owner; + private final Map attributeMap; + + public TestSynthesizedAnnotation(SynthesizedAnnotationAggregator owner, Annotation annotation) { + this.owner = owner; + this.attributeMap = new HashMap<>(); + this.annotation = annotation; + for (Method declaredMethod : annotation.annotationType().getDeclaredMethods()) { + attributeMap.put(declaredMethod.getName(), new CacheableAnnotationAttribute(annotation, declaredMethod)); + } + } + + @Override + public SynthesizedAnnotationAggregator getOwner() { + return owner; + } + + @Override + public Object getRoot() { + return null; + } + + @Override + public Annotation getAnnotation() { + return annotation; + } + + @Override + public int getVerticalDistance() { + return 0; + } + + @Override + public int getHorizontalDistance() { + return 0; + } + + @Override + public boolean hasAttribute(String attributeName, Class returnType) { + return false; + } + + @Override + public Map getAttributes() { + return attributeMap; + } + + @Override + public void setAttribute(String attributeName, AnnotationAttribute attribute) { + attributeMap.put(attributeName, attribute); + } + + @Override + public void replaceAttribute(String attributeName, UnaryOperator operator) { + AnnotationAttribute annotationAttribute = attributeMap.get(attributeName); + if (ObjectUtil.isNotNull(annotationAttribute)) { + attributeMap.put(attributeName, operator.apply(annotationAttribute)); + } + } + + @Override + public Object getAttributeValue(String attributeName) { + return null; + } + + @Override + public Class annotationType() { + return annotation.annotationType(); + } + } + +} diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/AliasedAnnotationAttributeTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/AliasedAnnotationAttributeTest.java index 4d9d17a45..8c0bc8cbc 100644 --- a/hutool-core/src/test/java/cn/hutool/core/annotation/AliasedAnnotationAttributeTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/AliasedAnnotationAttributeTest.java @@ -17,15 +17,16 @@ public class AliasedAnnotationAttributeTest { final CacheableAnnotationAttribute valueAttribute = new CacheableAnnotationAttribute(annotation, valueMethod); final Method nameMethod = ReflectUtil.getMethod(AnnotationForTest.class, "name"); final CacheableAnnotationAttribute nameAttribute = new CacheableAnnotationAttribute(annotation, nameMethod); - final AliasedAnnotationAttribute annotationAttribute = new AliasedAnnotationAttribute(valueAttribute, nameAttribute); + final AliasedAnnotationAttribute valueAnnotationAttribute = new AliasedAnnotationAttribute(valueAttribute, nameAttribute); // 注解属性 - Assert.assertEquals(annotation, annotationAttribute.getAnnotation()); - Assert.assertEquals(annotation.annotationType(), annotationAttribute.getAnnotationType()); + Assert.assertEquals(annotation, valueAnnotationAttribute.getAnnotation()); + Assert.assertEquals(annotation.annotationType(), valueAnnotationAttribute.getAnnotationType()); // 方法属性 - Assert.assertEquals(valueMethod.getName(), annotationAttribute.getAttributeName()); - Assert.assertEquals(nameMethod.getReturnType(), annotationAttribute.getAttributeType()); + Assert.assertEquals(valueMethod.getAnnotation(Alias.class), valueAnnotationAttribute.getAnnotation(Alias.class)); + Assert.assertEquals(valueMethod.getName(), valueAnnotationAttribute.getAttributeName()); + Assert.assertEquals(nameMethod.getReturnType(), valueAnnotationAttribute.getAttributeType()); } @Test @@ -63,6 +64,7 @@ public class AliasedAnnotationAttributeTest { @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD, ElementType.TYPE }) @interface AnnotationForTest { + @Alias("value") String value() default ""; String name() default ""; } diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/CacheableSynthesizedAnnotationAttributeProcessorTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/CacheableSynthesizedAnnotationAttributeProcessorTest.java new file mode 100644 index 000000000..c4db00adf --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/CacheableSynthesizedAnnotationAttributeProcessorTest.java @@ -0,0 +1,100 @@ +package cn.hutool.core.annotation; + +import cn.hutool.core.lang.Opt; +import cn.hutool.core.map.MapBuilder; +import cn.hutool.core.util.ClassUtil; +import org.junit.Assert; +import org.junit.Test; + +import java.lang.annotation.Annotation; +import java.util.Arrays; +import java.util.Map; +import java.util.function.UnaryOperator; + +public class CacheableSynthesizedAnnotationAttributeProcessorTest { + + @Test + public void getAttributeValueTest() { + CacheableSynthesizedAnnotationAttributeProcessor processor = new CacheableSynthesizedAnnotationAttributeProcessor(); + + Map values1 = MapBuilder. create().put("name", "name1").put("value", 111).build(); + SynthesizedAnnotation annotation1 = new TestSynthesizedAnnotation(1, 0, values1); + Map values2 = MapBuilder. create().put("name", "name2").put("value", "value2").build(); + SynthesizedAnnotation annotation2 = new TestSynthesizedAnnotation(0, 0, values2); + + Assert.assertEquals("name2", processor.getAttributeValue("name", String.class, Arrays.asList(annotation1, annotation2))); + Assert.assertEquals(Integer.valueOf(111), processor.getAttributeValue("value", Integer.class, Arrays.asList(annotation1, annotation2))); + } + + static class TestSynthesizedAnnotation implements SynthesizedAnnotation { + + private final int verticalDistance; + private final int horizontalDistance; + private final Map value; + + public TestSynthesizedAnnotation(int verticalDistance, int horizontalDistance, Map value) { + this.verticalDistance = verticalDistance; + this.horizontalDistance = horizontalDistance; + this.value = value; + } + + @Override + public SynthesizedAnnotationAggregator getOwner() { + return null; + } + + @Override + public Object getRoot() { + return null; + } + + @Override + public Annotation getAnnotation() { + return null; + } + + @Override + public int getVerticalDistance() { + return verticalDistance; + } + + @Override + public int getHorizontalDistance() { + return horizontalDistance; + } + + @Override + public boolean hasAttribute(String attributeName, Class returnType) { + return Opt.ofNullable(value.get(attributeName)) + .map(t -> ClassUtil.isAssignable(returnType, t.getClass())) + .orElse(false); + } + + @Override + public Map getAttributes() { + return null; + } + + @Override + public void setAttribute(String attributeName, AnnotationAttribute attribute) { + + } + + @Override + public void replaceAttribute(String attributeName, UnaryOperator operator) { + + } + + @Override + public Object getAttributeValue(String attributeName) { + return value.get(attributeName); + } + + @Override + public Class annotationType() { + return null; + } + + } + +} diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/MirrorLinkAttributePostProcessorTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/MirrorLinkAttributePostProcessorTest.java new file mode 100644 index 000000000..d6e8902db --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/MirrorLinkAttributePostProcessorTest.java @@ -0,0 +1,192 @@ +package cn.hutool.core.annotation; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.ReflectUtil; +import org.junit.Assert; +import org.junit.Test; + +import java.lang.annotation.*; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.function.UnaryOperator; + +public class MirrorLinkAttributePostProcessorTest { + + @Test + public void processTest() { + MirrorLinkAttributePostProcessor processor = new MirrorLinkAttributePostProcessor(); + + Map, SynthesizedAnnotation> annotationMap = new HashMap<>(); + SynthesizedAnnotationAggregator synthesizedAnnotationAggregator = new TestSynthesizedAnnotationAggregator(annotationMap); + AnnotationForTest annotation = ClassForTest.class.getAnnotation(AnnotationForTest.class); + SynthesizedAnnotation synthesizedAnnotation = new TestSynthesizedAnnotation(synthesizedAnnotationAggregator, annotation); + annotationMap.put(annotation.annotationType(), synthesizedAnnotation); + + processor.process(synthesizedAnnotation, synthesizedAnnotationAggregator); + AnnotationAttribute valueAttribute = synthesizedAnnotation.getAttributes().get("value"); + Assert.assertEquals(ReflectUtil.getMethod(AnnotationForTest.class, "value"), valueAttribute.getAttribute()); + Assert.assertTrue(valueAttribute.isWrapped()); + Assert.assertEquals(MirroredAnnotationAttribute.class, valueAttribute.getClass()); + + AnnotationAttribute nameAttribute = synthesizedAnnotation.getAttributes().get("name"); + Assert.assertEquals(ReflectUtil.getMethod(AnnotationForTest.class, "name"), nameAttribute.getAttribute()); + Assert.assertTrue(nameAttribute.isWrapped()); + Assert.assertEquals(MirroredAnnotationAttribute.class, nameAttribute.getClass()); + + Assert.assertEquals(((WrappedAnnotationAttribute)nameAttribute).getLinked(), ((WrappedAnnotationAttribute)valueAttribute).getOriginal()); + Assert.assertEquals(((WrappedAnnotationAttribute)nameAttribute).getOriginal(), ((WrappedAnnotationAttribute)valueAttribute).getLinked()); + } + + @AnnotationForTest + static class ClassForTest {} + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.METHOD, ElementType.TYPE }) + @interface AnnotationForTest { + @Link(attribute = "name", type = RelationType.MIRROR_FOR) + String value() default ""; + @Link(attribute = "value", type = RelationType.MIRROR_FOR) + String name() default ""; + } + + static class TestSynthesizedAnnotationAggregator implements SynthesizedAnnotationAggregator { + + private final Map, SynthesizedAnnotation> annotationMap; + + public TestSynthesizedAnnotationAggregator(Map, SynthesizedAnnotation> annotationMap) { + this.annotationMap = annotationMap; + } + + @Override + public SynthesizedAnnotationSelector getAnnotationSelector() { + return null; + } + + @Override + public SynthesizedAnnotationAttributeProcessor getAnnotationAttributeProcessor() { + return null; + } + + @Override + public Collection getAnnotationAttributePostProcessors() { + return null; + } + + @Override + public SynthesizedAnnotation getSynthesizedAnnotation(Class annotationType) { + return annotationMap.get(annotationType); + } + + @Override + public Collection getAllSynthesizedAnnotation() { + return null; + } + + @Override + public T getAnnotation(Class annotationType) { + return null; + } + + @Override + public boolean isAnnotationPresent(Class annotationType) { + return false; + } + + @Override + public Annotation[] getAnnotations() { + return new Annotation[0]; + } + + @Override + public T synthesize(Class annotationType) { + return null; + } + + @Override + public Object getAttribute(String attributeName, Class attributeType) { + return null; + } + + @Override + public Annotation[] getDeclaredAnnotations() { + return new Annotation[0]; + } + } + + static class TestSynthesizedAnnotation implements SynthesizedAnnotation { + + private final Annotation annotation; + private final SynthesizedAnnotationAggregator owner; + private final Map attributeMap; + + public TestSynthesizedAnnotation(SynthesizedAnnotationAggregator owner, Annotation annotation) { + this.owner = owner; + this.attributeMap = new HashMap<>(); + this.annotation = annotation; + for (Method declaredMethod : annotation.annotationType().getDeclaredMethods()) { + attributeMap.put(declaredMethod.getName(), new CacheableAnnotationAttribute(annotation, declaredMethod)); + } + } + + @Override + public SynthesizedAnnotationAggregator getOwner() { + return owner; + } + + @Override + public Object getRoot() { + return null; + } + + @Override + public Annotation getAnnotation() { + return annotation; + } + + @Override + public int getVerticalDistance() { + return 0; + } + + @Override + public int getHorizontalDistance() { + return 0; + } + + @Override + public boolean hasAttribute(String attributeName, Class returnType) { + return false; + } + + @Override + public Map getAttributes() { + return attributeMap; + } + + @Override + public void setAttribute(String attributeName, AnnotationAttribute attribute) { + attributeMap.put(attributeName, attribute); + } + + @Override + public void replaceAttribute(String attributeName, UnaryOperator operator) { + AnnotationAttribute annotationAttribute = attributeMap.get(attributeName); + if (ObjectUtil.isNotNull(annotationAttribute)) { + attributeMap.put(attributeName, operator.apply(annotationAttribute)); + } + } + + @Override + public Object getAttributeValue(String attributeName) { + return null; + } + + @Override + public Class annotationType() { + return annotation.annotationType(); + } + } + +} diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/SynthesizedAnnotationSelectorTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/SynthesizedAnnotationSelectorTest.java index e27f8fb05..cadb3d6f9 100644 --- a/hutool-core/src/test/java/cn/hutool/core/annotation/SynthesizedAnnotationSelectorTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/SynthesizedAnnotationSelectorTest.java @@ -10,8 +10,8 @@ import java.util.function.UnaryOperator; public class SynthesizedAnnotationSelectorTest { @Test - public void nearestAndOldestPriorityTest() { - final SynthesizedAnnotationSelector selector = SynthesizedAnnotationSelector.NEAREST_AND_OLDEST_PRIORITY; + public void chooseTest() { + final SynthesizedAnnotationSelector.NearestAndOldestPrioritySelector selector = (SynthesizedAnnotationSelector.NearestAndOldestPrioritySelector)SynthesizedAnnotationSelector.NEAREST_AND_OLDEST_PRIORITY; TestSynthesizedAnnotation annotation1 = new TestSynthesizedAnnotation(0, 0); TestSynthesizedAnnotation annotation2 = new TestSynthesizedAnnotation(0, 0); @@ -28,7 +28,7 @@ public class SynthesizedAnnotationSelectorTest { @Test public void nearestAndNewestPriorityTest() { - final SynthesizedAnnotationSelector selector = SynthesizedAnnotationSelector.NEAREST_AND_NEWEST_PRIORITY; + final SynthesizedAnnotationSelector.NearestAndNewestPrioritySelector selector = (SynthesizedAnnotationSelector.NearestAndNewestPrioritySelector)SynthesizedAnnotationSelector.NEAREST_AND_NEWEST_PRIORITY; TestSynthesizedAnnotation annotation1 = new TestSynthesizedAnnotation(0, 0); TestSynthesizedAnnotation annotation2 = new TestSynthesizedAnnotation(0, 0); @@ -45,7 +45,7 @@ public class SynthesizedAnnotationSelectorTest { @Test public void farthestAndOldestPriorityTest() { - final SynthesizedAnnotationSelector selector = SynthesizedAnnotationSelector.FARTHEST_AND_OLDEST_PRIORITY; + final SynthesizedAnnotationSelector.FarthestAndOldestPrioritySelector selector = (SynthesizedAnnotationSelector.FarthestAndOldestPrioritySelector)SynthesizedAnnotationSelector.FARTHEST_AND_OLDEST_PRIORITY; TestSynthesizedAnnotation annotation1 = new TestSynthesizedAnnotation(0, 0); TestSynthesizedAnnotation annotation2 = new TestSynthesizedAnnotation(0, 0); @@ -62,7 +62,7 @@ public class SynthesizedAnnotationSelectorTest { @Test public void farthestAndNewestPriorityTest() { - final SynthesizedAnnotationSelector selector = SynthesizedAnnotationSelector.FARTHEST_AND_NEWEST_PRIORITY; + final SynthesizedAnnotationSelector.FarthestAndNewestPrioritySelector selector = (SynthesizedAnnotationSelector.FarthestAndNewestPrioritySelector)SynthesizedAnnotationSelector.FARTHEST_AND_NEWEST_PRIORITY; TestSynthesizedAnnotation annotation1 = new TestSynthesizedAnnotation(0, 0); TestSynthesizedAnnotation annotation2 = new TestSynthesizedAnnotation(0, 0); @@ -88,7 +88,7 @@ public class SynthesizedAnnotationSelectorTest { } @Override - public SyntheticAnnotation getOwner() { + public SynthesizedAnnotationAggregator getOwner() { return null; } 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 c9e358883..b34f2d4dc 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 @@ -9,7 +9,7 @@ import java.lang.reflect.Method; import java.util.Arrays; /** - * 合成注解{@link SyntheticMetaAnnotation}的测试用例 + * 合成注解{@link SynthesizedMetaAnnotationAggregator}的测试用例 * * @author huangchengxing */ @@ -22,10 +22,10 @@ public class SyntheticMetaAnnotationTest { final GrandParentAnnotation grandParentAnnotation = ChildAnnotation.class.getAnnotation(GrandParentAnnotation.class); final ParentAnnotation parentAnnotation = ChildAnnotation.class.getAnnotation(ParentAnnotation.class); final ChildAnnotation childAnnotation = AnnotatedClass.class.getAnnotation(ChildAnnotation.class); - final SyntheticMetaAnnotation syntheticMetaAnnotation = new SyntheticMetaAnnotation(childAnnotation); + final SynthesizedMetaAnnotationAggregator syntheticMetaAnnotation = new SynthesizedMetaAnnotationAggregator(childAnnotation); // Annotation & AnnotatedElement - Assert.assertEquals(SyntheticMetaAnnotation.class, syntheticMetaAnnotation.annotationType()); + Assert.assertEquals(SynthesizedMetaAnnotationAggregator.class, syntheticMetaAnnotation.annotationType()); Assert.assertTrue(syntheticMetaAnnotation.isAnnotationPresent(GrandParentAnnotation.class)); Assert.assertTrue(syntheticMetaAnnotation.isAnnotationPresent(ParentAnnotation.class)); Assert.assertTrue(syntheticMetaAnnotation.isAnnotationPresent(ChildAnnotation.class)); @@ -42,15 +42,20 @@ public class SyntheticMetaAnnotationTest { Assert.assertNotNull(syntheticMetaAnnotation.getSynthesizedAnnotation(GrandParentAnnotation.class)); Assert.assertNotNull(syntheticMetaAnnotation.getSynthesizedAnnotation(ParentAnnotation.class)); Assert.assertNotNull(syntheticMetaAnnotation.getSynthesizedAnnotation(ChildAnnotation.class)); - Assert.assertEquals(3, syntheticMetaAnnotation.getAllSyntheticAnnotations().size()); + Assert.assertEquals(3, syntheticMetaAnnotation.getAllSynthesizedAnnotation().size()); + + // 属性 + Assert.assertEquals(SynthesizedAnnotationSelector.NEAREST_AND_OLDEST_PRIORITY, syntheticMetaAnnotation.getAnnotationSelector()); + Assert.assertEquals(CacheableSynthesizedAnnotationAttributeProcessor.class, syntheticMetaAnnotation.getAnnotationAttributeProcessor().getClass()); + Assert.assertEquals(3, syntheticMetaAnnotation.getAnnotationAttributePostProcessors().size()); } @Test public void synthesisAnnotationAttributeTest() { final ChildAnnotation rootAnnotation = AnnotatedClass.class.getAnnotation(ChildAnnotation.class); - SyntheticMetaAnnotation syntheticMetaAnnotation = new SyntheticMetaAnnotation(rootAnnotation); + SynthesizedMetaAnnotationAggregator syntheticMetaAnnotation = new SynthesizedMetaAnnotationAggregator(rootAnnotation); Assert.assertEquals(syntheticMetaAnnotation.getSource(), rootAnnotation); - Assert.assertEquals(syntheticMetaAnnotation.annotationType(), SyntheticMetaAnnotation.class); + Assert.assertEquals(syntheticMetaAnnotation.annotationType(), SynthesizedMetaAnnotationAggregator.class); Assert.assertEquals(1, syntheticMetaAnnotation.getDeclaredAnnotations().length); Assert.assertEquals(syntheticMetaAnnotation.getDeclaredAnnotations()[0], rootAnnotation); Assert.assertEquals(3, syntheticMetaAnnotation.getAnnotations().length); @@ -59,35 +64,55 @@ public class SyntheticMetaAnnotationTest { Assert.assertEquals("Child!", syntheticMetaAnnotation.getAttribute("childValueAlias", String.class)); Assert.assertEquals("Child's Parent!", syntheticMetaAnnotation.getAttribute("parentValue", String.class)); Assert.assertEquals("Child's GrandParent!", syntheticMetaAnnotation.getAttribute("grandParentValue", String.class)); + } - final ChildAnnotation childAnnotation = syntheticMetaAnnotation.syntheticAnnotation(ChildAnnotation.class); + @Test + public void syntheticAnnotationTest() { + final ChildAnnotation rootAnnotation = AnnotatedClass.class.getAnnotation(ChildAnnotation.class); + SynthesizedMetaAnnotationAggregator syntheticMetaAnnotation = new SynthesizedMetaAnnotationAggregator(rootAnnotation); + + final ChildAnnotation childAnnotation = syntheticMetaAnnotation.synthesize(ChildAnnotation.class); + SynthesizedAnnotation childSyntheticAnnotation = syntheticMetaAnnotation.getSynthesizedAnnotation(ChildAnnotation.class); + Assert.assertNotNull(childSyntheticAnnotation); + Assert.assertTrue(childSyntheticAnnotation.hasAttribute("childValue", String.class)); + Assert.assertEquals(AnnotatedClass.class.getAnnotation(ChildAnnotation.class), childSyntheticAnnotation.getRoot()); + Assert.assertEquals(AnnotatedClass.class.getAnnotation(ChildAnnotation.class), childSyntheticAnnotation.getAnnotation()); Assert.assertTrue(syntheticMetaAnnotation.isAnnotationPresent(ChildAnnotation.class)); Assert.assertNotNull(childAnnotation); Assert.assertEquals("Child!", childAnnotation.childValue()); Assert.assertEquals("Child!", childAnnotation.childValueAlias()); Assert.assertEquals(childAnnotation.grandParentType(), Integer.class); - Assert.assertThrows(IllegalArgumentException.class, () -> new SyntheticMetaAnnotation(childAnnotation)); + Assert.assertThrows(IllegalArgumentException.class, () -> new SynthesizedMetaAnnotationAggregator(childAnnotation)); - final ParentAnnotation parentAnnotation = syntheticMetaAnnotation.syntheticAnnotation(ParentAnnotation.class); - Assert.assertTrue(syntheticMetaAnnotation.isAnnotationPresent(ParentAnnotation.class)); + final ParentAnnotation parentAnnotation = syntheticMetaAnnotation.synthesize(ParentAnnotation.class); + SynthesizedAnnotation parentSyntheticAnnotation = syntheticMetaAnnotation.getSynthesizedAnnotation(ParentAnnotation.class); + Assert.assertNotNull(parentSyntheticAnnotation); + Assert.assertTrue(parentSyntheticAnnotation.hasAttribute("parentValue", String.class)); + Assert.assertEquals(AnnotatedClass.class.getAnnotation(ChildAnnotation.class), parentSyntheticAnnotation.getRoot()); + Assert.assertEquals(ChildAnnotation.class.getAnnotation(ParentAnnotation.class), parentSyntheticAnnotation.getAnnotation()); Assert.assertNotNull(parentAnnotation); Assert.assertEquals("Child's Parent!", parentAnnotation.parentValue()); Assert.assertEquals("java.lang.Void", parentAnnotation.grandParentType()); - Assert.assertThrows(IllegalArgumentException.class, () -> new SyntheticMetaAnnotation(parentAnnotation)); + Assert.assertThrows(IllegalArgumentException.class, () -> new SynthesizedMetaAnnotationAggregator(parentAnnotation)); - final GrandParentAnnotation grandParentAnnotation = syntheticMetaAnnotation.syntheticAnnotation(GrandParentAnnotation.class); + final GrandParentAnnotation grandParentAnnotation = syntheticMetaAnnotation.synthesize(GrandParentAnnotation.class); + SynthesizedAnnotation grandParentSyntheticAnnotation = syntheticMetaAnnotation.getSynthesizedAnnotation(GrandParentAnnotation.class); + Assert.assertNotNull(grandParentSyntheticAnnotation); + Assert.assertTrue(grandParentSyntheticAnnotation.hasAttribute("grandParentType", Class.class)); + Assert.assertEquals(AnnotatedClass.class.getAnnotation(ChildAnnotation.class), grandParentSyntheticAnnotation.getRoot()); + Assert.assertEquals(ChildAnnotation.class.getAnnotation(GrandParentAnnotation.class), grandParentSyntheticAnnotation.getAnnotation()); Assert.assertTrue(syntheticMetaAnnotation.isAnnotationPresent(GrandParentAnnotation.class)); Assert.assertNotNull(grandParentAnnotation); Assert.assertEquals("Child's GrandParent!", grandParentAnnotation.grandParentValue()); Assert.assertEquals(grandParentAnnotation.grandParentType(), Integer.class); - Assert.assertThrows(IllegalArgumentException.class, () -> new SyntheticMetaAnnotation(grandParentAnnotation)); + Assert.assertThrows(IllegalArgumentException.class, () -> new SynthesizedMetaAnnotationAggregator(grandParentAnnotation)); } @Test public void linkTest() { final Method method = ReflectUtil.getMethod(AnnotationForLinkTest.class, "value"); - final SyntheticAnnotation syntheticAnnotation = new SyntheticMetaAnnotation(method.getAnnotation(AliasFor.class)); - final Link link = syntheticAnnotation.syntheticAnnotation(Link.class); + final SynthesizedAnnotationAggregator synthesizedAnnotationAggregator = new SynthesizedMetaAnnotationAggregator(method.getAnnotation(AliasFor.class)); + final Link link = synthesizedAnnotationAggregator.synthesize(Link.class); Assert.assertEquals(AnnotationForLinkTest.class, link.annotation()); Assert.assertEquals("name", link.attribute()); } @@ -95,20 +120,20 @@ public class SyntheticMetaAnnotationTest { @Test public void mirrorAttributeTest() { AnnotationForMirrorTest annotation = ClassForMirrorTest.class.getAnnotation(AnnotationForMirrorTest.class); - SyntheticAnnotation synthetic = new SyntheticMetaAnnotation(annotation); - AnnotationForMirrorTest syntheticAnnotation = synthetic.syntheticAnnotation(AnnotationForMirrorTest.class); + SynthesizedAnnotationAggregator synthetic = new SynthesizedMetaAnnotationAggregator(annotation); + AnnotationForMirrorTest syntheticAnnotation = synthetic.synthesize(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); + synthetic = new SynthesizedMetaAnnotationAggregator(annotation); + syntheticAnnotation = synthetic.synthesize(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); + synthetic = new SynthesizedMetaAnnotationAggregator(annotation); + syntheticAnnotation = synthetic.synthesize(AnnotationForMirrorTest.class); AnnotationForMirrorTest finalSyntheticAnnotation = syntheticAnnotation; Assert.assertThrows(IllegalArgumentException.class, finalSyntheticAnnotation::name); } @@ -116,71 +141,71 @@ public class SyntheticMetaAnnotationTest { @Test public void aliasForTest() { AnnotationForAliasForTest annotation = ClassForAliasForTest.class.getAnnotation(AnnotationForAliasForTest.class); - SyntheticAnnotation synthetic = new SyntheticMetaAnnotation(annotation); - MetaAnnotationForAliasForTest metaAnnotation = synthetic.syntheticAnnotation(MetaAnnotationForAliasForTest.class); + SynthesizedAnnotationAggregator synthetic = new SynthesizedMetaAnnotationAggregator(annotation); + MetaAnnotationForAliasForTest metaAnnotation = synthetic.synthesize(MetaAnnotationForAliasForTest.class); Assert.assertEquals("Meta", metaAnnotation.name()); - AnnotationForAliasForTest childAnnotation = synthetic.syntheticAnnotation(AnnotationForAliasForTest.class); + AnnotationForAliasForTest childAnnotation = synthetic.synthesize(AnnotationForAliasForTest.class); Assert.assertEquals("", childAnnotation.value()); annotation = ClassForAliasForTest2.class.getAnnotation(AnnotationForAliasForTest.class); - synthetic = new SyntheticMetaAnnotation(annotation); - metaAnnotation = synthetic.syntheticAnnotation(MetaAnnotationForAliasForTest.class); + synthetic = new SynthesizedMetaAnnotationAggregator(annotation); + metaAnnotation = synthetic.synthesize(MetaAnnotationForAliasForTest.class); Assert.assertEquals("Foo", metaAnnotation.name()); - childAnnotation = synthetic.syntheticAnnotation(AnnotationForAliasForTest.class); + childAnnotation = synthetic.synthesize(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); + SynthesizedAnnotationAggregator synthetic = new SynthesizedMetaAnnotationAggregator(annotation); + MetaAnnotationForForceAliasForTest metaAnnotation = synthetic.synthesize(MetaAnnotationForForceAliasForTest.class); Assert.assertEquals("", metaAnnotation.name()); - AnnotationForceForAliasForTest childAnnotation = synthetic.syntheticAnnotation(AnnotationForceForAliasForTest.class); + AnnotationForceForAliasForTest childAnnotation = synthetic.synthesize(AnnotationForceForAliasForTest.class); Assert.assertEquals("", childAnnotation.value()); annotation = ClassForForceAliasForTest2.class.getAnnotation(AnnotationForceForAliasForTest.class); - synthetic = new SyntheticMetaAnnotation(annotation); - metaAnnotation = synthetic.syntheticAnnotation(MetaAnnotationForForceAliasForTest.class); + synthetic = new SynthesizedMetaAnnotationAggregator(annotation); + metaAnnotation = synthetic.synthesize(MetaAnnotationForForceAliasForTest.class); Assert.assertEquals("Foo", metaAnnotation.name()); - childAnnotation = synthetic.syntheticAnnotation(AnnotationForceForAliasForTest.class); + childAnnotation = synthetic.synthesize(AnnotationForceForAliasForTest.class); Assert.assertEquals("Foo", childAnnotation.value()); } @Test public void aliasForAndMirrorTest() { AnnotationForMirrorThenAliasForTest annotation = ClassForAliasForAndMirrorTest.class.getAnnotation(AnnotationForMirrorThenAliasForTest.class); - SyntheticAnnotation synthetic = new SyntheticMetaAnnotation(annotation); - MetaAnnotationForMirrorThenAliasForTest metaAnnotation = synthetic.syntheticAnnotation(MetaAnnotationForMirrorThenAliasForTest.class); + SynthesizedAnnotationAggregator synthetic = new SynthesizedMetaAnnotationAggregator(annotation); + MetaAnnotationForMirrorThenAliasForTest metaAnnotation = synthetic.synthesize(MetaAnnotationForMirrorThenAliasForTest.class); Assert.assertEquals("test", metaAnnotation.name()); Assert.assertEquals("test", metaAnnotation.value()); - AnnotationForMirrorThenAliasForTest childAnnotation = synthetic.syntheticAnnotation(AnnotationForMirrorThenAliasForTest.class); + AnnotationForMirrorThenAliasForTest childAnnotation = synthetic.synthesize(AnnotationForMirrorThenAliasForTest.class); Assert.assertEquals("test", childAnnotation.childValue()); } @Test public void multiAliasForTest() { final AnnotationForMultiAliasForTest annotation = ClassForMultiAliasForTest.class.getAnnotation(AnnotationForMultiAliasForTest.class); - final SyntheticAnnotation synthetic = new SyntheticMetaAnnotation(annotation); + final SynthesizedAnnotationAggregator synthetic = new SynthesizedMetaAnnotationAggregator(annotation); - final MetaAnnotationForMultiAliasForTest1 metaAnnotation1 = synthetic.syntheticAnnotation(MetaAnnotationForMultiAliasForTest1.class); + final MetaAnnotationForMultiAliasForTest1 metaAnnotation1 = synthetic.synthesize(MetaAnnotationForMultiAliasForTest1.class); Assert.assertEquals("test", metaAnnotation1.name()); Assert.assertEquals("test", metaAnnotation1.value1()); - final MetaAnnotationForMultiAliasForTest2 metaAnnotation2 = synthetic.syntheticAnnotation(MetaAnnotationForMultiAliasForTest2.class); + final MetaAnnotationForMultiAliasForTest2 metaAnnotation2 = synthetic.synthesize(MetaAnnotationForMultiAliasForTest2.class); Assert.assertEquals("test", metaAnnotation2.value2()); - final AnnotationForMultiAliasForTest childAnnotation = synthetic.syntheticAnnotation(AnnotationForMultiAliasForTest.class); + final AnnotationForMultiAliasForTest childAnnotation = synthetic.synthesize(AnnotationForMultiAliasForTest.class); Assert.assertEquals("test", childAnnotation.value3()); } @Test public void implicitAliasTest() { final AnnotationForImplicitAliasTest annotation = ClassForImplicitAliasTest.class.getAnnotation(AnnotationForImplicitAliasTest.class); - final SyntheticAnnotation synthetic = new SyntheticMetaAnnotation(annotation); + final SynthesizedAnnotationAggregator synthetic = new SynthesizedMetaAnnotationAggregator(annotation); - final MetaAnnotationForImplicitAliasTest metaAnnotation = synthetic.syntheticAnnotation(MetaAnnotationForImplicitAliasTest.class); + final MetaAnnotationForImplicitAliasTest metaAnnotation = synthetic.synthesize(MetaAnnotationForImplicitAliasTest.class); Assert.assertEquals("Meta", metaAnnotation.name()); Assert.assertEquals("Foo", metaAnnotation.value()); - final AnnotationForImplicitAliasTest childAnnotation = synthetic.syntheticAnnotation(AnnotationForImplicitAliasTest.class); + final AnnotationForImplicitAliasTest childAnnotation = synthetic.synthesize(AnnotationForImplicitAliasTest.class); Assert.assertEquals("Foo", childAnnotation.value()); } From b29b0c393228a6d0eb55ffb16623f2f22a2e8823 Mon Sep 17 00:00:00 2001 From: huangchengxing <841396397@qq.com> Date: Sat, 9 Jul 2022 15:23:00 +0800 Subject: [PATCH 06/15] =?UTF-8?q?=E5=B0=86AnnotationUtil=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E5=90=88=E6=88=90=E6=B3=A8=E8=A7=A3=E7=9A=84=E6=96=B9=E6=B3=95?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E6=94=B9=E4=B8=BA=E4=BB=A5SynthesizedAnnotat?= =?UTF-8?q?ion=E7=BB=93=E5=B0=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/annotation/AnnotationUtil.java | 56 +++++++++---------- .../annotation/SyntheticAnnotationProxy.java | 2 +- .../annotation/SyntheticAnnotationUtil.java | 2 +- 3 files changed, 30 insertions(+), 30 deletions(-) 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 8662caf8b..570ef8b6c 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 @@ -353,34 +353,11 @@ public class AnnotationUtil { * @return 合成注解 * @see SynthesizedAnnotationAggregator */ - public static T getSynthesisAnnotation(Annotation annotation, Class annotationType) { + public static T getSynthesizedAnnotation(Annotation annotation, Class annotationType) { // TODO 缓存合成注解信息,避免重复解析 return SynthesizedAnnotationAggregator.of(annotation).synthesize(annotationType); } - /** - * 获取元素上所有指定注解 - *
    - *
  • 若元素是类,则递归解析全部父类和全部父接口上的注解;
  • - *
  • 若元素是方法、属性或注解,则只解析其直接声明的注解;
  • - *
- * - * @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission - * @param annotationType 注解类 - * @param 注解类型 - * @return 合成注解 - * @see SynthesizedAnnotationAggregator - */ - public static List getAllSynthesisAnnotations(AnnotatedElement annotatedEle, Class annotationType) { - AnnotationScanner[] scanners = new AnnotationScanner[]{ - new MetaAnnotationScanner(), new TypeAnnotationScanner(), new MethodAnnotationScanner(), new FieldAnnotationScanner() - }; - return AnnotationScanner.scanByAnySupported(annotatedEle, scanners).stream() - .map(annotation -> getSynthesisAnnotation(annotation, annotationType)) - .filter(Objects::nonNull) - .collect(Collectors.toList()); - } - /** * 获取元素上距离指定元素最接近的合成注解 *
    @@ -394,7 +371,7 @@ public class AnnotationUtil { * @return 合成注解 * @see SynthesizedAnnotationAggregator */ - public static T getSyntheticAnnotation(AnnotatedElement annotatedEle, Class annotationType) { + public static T getSynthesizedAnnotation(AnnotatedElement annotatedEle, Class annotationType) { T target = annotatedEle.getAnnotation(annotationType); if (ObjectUtil.isNotNull(target)) { return target; @@ -403,10 +380,33 @@ public class AnnotationUtil { new MetaAnnotationScanner(), new TypeAnnotationScanner(), new MethodAnnotationScanner(), new FieldAnnotationScanner() }; return AnnotationScanner.scanByAnySupported(annotatedEle, scanners).stream() - .map(annotation -> getSynthesisAnnotation(annotation, annotationType)) + .map(annotation -> getSynthesizedAnnotation(annotation, annotationType)) + .filter(Objects::nonNull) + .findFirst() + .orElse(null); + } + + /** + * 获取元素上所有指定注解 + *
      + *
    • 若元素是类,则递归解析全部父类和全部父接口上的注解;
    • + *
    • 若元素是方法、属性或注解,则只解析其直接声明的注解;
    • + *
    + * + * @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission + * @param annotationType 注解类 + * @param 注解类型 + * @return 合成注解 + * @see SynthesizedAnnotationAggregator + */ + public static List getAllSynthesizedAnnotations(AnnotatedElement annotatedEle, Class annotationType) { + AnnotationScanner[] scanners = new AnnotationScanner[]{ + new MetaAnnotationScanner(), new TypeAnnotationScanner(), new MethodAnnotationScanner(), new FieldAnnotationScanner() + }; + return AnnotationScanner.scanByAnySupported(annotatedEle, scanners).stream() + .map(annotation -> getSynthesizedAnnotation(annotation, annotationType)) .filter(Objects::nonNull) - .findFirst() - .orElse(null); + .collect(Collectors.toList()); } /** 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 8a8688944..5fb175cc5 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 @@ -80,7 +80,7 @@ class SyntheticAnnotationProxy implements InvocationHandler { void loadMethods() { methods.put("toString", (method, args) -> proxyToString()); methods.put("hashCode", (method, args) -> proxyHashCode()); - methods.put("getSyntheticAnnotation", (method, args) -> proxyGetSyntheticAnnotation()); + methods.put("getSynthesizedAnnotation", (method, args) -> proxyGetSyntheticAnnotation()); methods.put("getSynthesizedAnnotation", (method, args) -> proxyGetSynthesizedAnnotation()); methods.put("getRoot", (method, args) -> annotation.getRoot()); methods.put("getVerticalDistance", (method, args) -> annotation.getVerticalDistance()); 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 index dc054c90f..9a5f8f5a4 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticAnnotationUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticAnnotationUtil.java @@ -24,7 +24,7 @@ class SyntheticAnnotationUtil { */ static Link getLink(AnnotationAttribute attribute, RelationType... relationTypes) { return Opt.ofNullable(attribute) - .map(t -> AnnotationUtil.getSyntheticAnnotation(attribute.getAttribute(), Link.class)) + .map(t -> AnnotationUtil.getSynthesizedAnnotation(attribute.getAttribute(), Link.class)) .filter(a -> ArrayUtil.contains(relationTypes, a.type())) .get(); } From a352783b5c62c82e09699cd153551e012c118806 Mon Sep 17 00:00:00 2001 From: huangchengxing <841396397@qq.com> Date: Wed, 13 Jul 2022 22:39:41 +0800 Subject: [PATCH 07/15] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=EF=BC=8C=E8=B0=83=E6=95=B4=E5=8F=98=E9=87=8F=E3=80=81=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E4=B8=8E=E7=B1=BB=E5=90=8D=EF=BC=8C=E5=AE=8C=E5=96=84?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AbstractLinkAnnotationPostProcessor.java | 160 ++++++++++++++ .../AbstractSynthesizedAnnotation.java | 195 ++++++++++++++++++ ...java => AliasAnnotationPostProcessor.java} | 14 +- .../AliasForLinkAttributePostProcessor.java | 106 ---------- .../AliasLinkAnnotationPostProcessor.java | 126 +++++++++++ .../AliasedAnnotationAttribute.java | 3 +- ...nthesizedAnnotationAttributeProcessor.java | 2 +- .../ForceAliasedAnnotationAttribute.java | 4 +- .../MirrorLinkAnnotationPostProcessor.java | 77 +++++++ .../MirrorLinkAttributePostProcessor.java | 71 ------- .../MirroredAnnotationAttribute.java | 2 +- .../annotation/SynthesizedAnnotation.java | 21 +- .../SynthesizedAnnotationPostProcessor.java | 20 +- .../SynthesizedMetaAnnotationAggregator.java | 176 ++-------------- .../annotation/SyntheticAnnotationProxy.java | 6 +- .../annotation/SyntheticAnnotationUtil.java | 106 ---------- ... => AliasAnnotationPostProcessorTest.java} | 4 +- ...AliasLinkAnnotationPostProcessorTest.java} | 6 +- ...irrorLinkAnnotationPostProcessorTest.java} | 4 +- .../scanner/TypeAnnotationScannerTest.java | 4 +- 20 files changed, 625 insertions(+), 482 deletions(-) create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/AbstractLinkAnnotationPostProcessor.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/AbstractSynthesizedAnnotation.java rename hutool-core/src/main/java/cn/hutool/core/annotation/{AliasAttributePostProcessor.java => AliasAnnotationPostProcessor.java} (75%) delete mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/AliasForLinkAttributePostProcessor.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/AliasLinkAnnotationPostProcessor.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessor.java delete mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/MirrorLinkAttributePostProcessor.java delete mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticAnnotationUtil.java rename hutool-core/src/test/java/cn/hutool/core/annotation/{AliasAttributePostProcessorTest.java => AliasAnnotationPostProcessorTest.java} (97%) rename hutool-core/src/test/java/cn/hutool/core/annotation/{AliasForLinkAttributePostProcessorTest.java => AliasLinkAnnotationPostProcessorTest.java} (96%) rename hutool-core/src/test/java/cn/hutool/core/annotation/{MirrorLinkAttributePostProcessorTest.java => MirrorLinkAnnotationPostProcessorTest.java} (97%) diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractLinkAnnotationPostProcessor.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractLinkAnnotationPostProcessor.java new file mode 100644 index 000000000..4c781101f --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractLinkAnnotationPostProcessor.java @@ -0,0 +1,160 @@ +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; +import java.util.HashMap; +import java.util.Map; + +/** + * {@link SynthesizedAnnotationPostProcessor}的基本实现, + * 用于处理注解中带有{@link Link}注解的属性。 + * + * @author huangchengxing + * @see MirrorLinkAnnotationPostProcessor + * @see AliasLinkAnnotationPostProcessor + */ +public abstract class AbstractLinkAnnotationPostProcessor implements SynthesizedAnnotationPostProcessor { + + /** + * 若一个注解属性上存在{@link Link}注解,注解的{@link Link#type()}返回值在{@link #processTypes()}中存在, + * 且此{@link Link}指定的注解对象在当前的{@link SynthesizedAnnotationAggregator}中存在, + * 则从聚合器中获取类型对应的合成注解对象,与该对象中的指定属性,然后将全部关联数据交给 + * {@link #processLinkedAttribute}处理。 + * + * @param synthesizedAnnotation 合成的注解 + * @param aggregator 合成注解聚合器 + */ + @Override + public void process(SynthesizedAnnotation synthesizedAnnotation, SynthesizedAnnotationAggregator aggregator) { + final Map attributeMap = new HashMap<>(synthesizedAnnotation.getAttributes()); + attributeMap.forEach((originalAttributeName, originalAttribute) -> { + // 获取注解 + final Link link = getLinkAnnotation(originalAttribute, processTypes()); + if (ObjectUtil.isNull(link)) { + return; + } + // 获取注解属性 + final SynthesizedAnnotation linkedAnnotation = getLinkedAnnotation(link, aggregator, synthesizedAnnotation.annotationType()); + if (ObjectUtil.isNull(linkedAnnotation)) { + return; + } + final AnnotationAttribute linkedAttribute = linkedAnnotation.getAttributes().get(link.attribute()); + // 处理 + processLinkedAttribute(aggregator, link, synthesizedAnnotation, originalAttribute, + linkedAnnotation, linkedAttribute + ); + }); + } + + // =========================== 抽象方法 =========================== + + /** + * 当属性上存在{@link Link}注解时,仅当{@link Link#type()}在本方法返回值内存在时才进行处理 + * + * @return 支持处理的{@link RelationType}类型 + */ + protected abstract RelationType[] processTypes(); + + /** + * 对关联的合成注解对象及其关联属性的处理 + * + * @param aggregator 合成注解聚合器 + * @param annotation {@code originalAttribute}上的{@link Link}注解对象 + * @param originalAnnotation 当前正在处理的{@link SynthesizedAnnotation}对象 + * @param originalAttribute {@code originalAnnotation}上的待处理的属性 + * @param linkedAnnotation {@link Link}指向的关联注解对象 + * @param linkedAttribute {@link Link}指向的{@code originalAnnotation}中的关联属性,该参数可能为空 + */ + protected abstract void processLinkedAttribute( + SynthesizedAnnotationAggregator aggregator, Link annotation, + SynthesizedAnnotation originalAnnotation, AnnotationAttribute originalAttribute, + SynthesizedAnnotation linkedAnnotation, AnnotationAttribute linkedAttribute + ); + + // =========================== @Link注解的处理 =========================== + + /** + * 从注解属性上获取指定类型的{@link Link}注解 + * + * @param attribute 注解属性 + * @param relationTypes 类型 + * @return 注解 + */ + protected Link getLinkAnnotation(AnnotationAttribute attribute, RelationType... relationTypes) { + return Opt.ofNullable(attribute) + .map(t -> AnnotationUtil.getSynthesizedAnnotation(attribute.getAttribute(), Link.class)) + .filter(a -> ArrayUtil.contains(relationTypes, a.type())) + .get(); + } + + /** + * 从合成注解中获取{@link Link#type()}指定的注解对象 + * + * @param annotation {@link Link}注解 + * @param synthesizedAnnotationAggregator 合成注解 + */ + protected SynthesizedAnnotation getLinkedAnnotation( + Link annotation, SynthesizedAnnotationAggregator synthesizedAnnotationAggregator, Class defaultType) { + final Class targetAnnotationType = getLinkedAnnotationType(annotation, defaultType); + return synthesizedAnnotationAggregator.getSynthesizedAnnotation(targetAnnotationType); + } + + /** + * 若{@link Link#annotation()}获取的类型{@link Annotation#getClass()},则返回{@code defaultType}, + * 否则返回{@link Link#annotation()}指定的类型 + * + * @param annotation {@link Link}注解 + * @param defaultType 默认注解类型 + * @return 注解类型 + */ + protected Class getLinkedAnnotationType(Link annotation, Class defaultType) { + return ObjectUtil.equals(annotation.annotation(), Annotation.class) ? + defaultType : annotation.annotation(); + } + + // =========================== 注解属性的校验 =========================== + + /** + * 校验两个注解属性的返回值类型是否一致 + * + * @param original 原属性 + * @param alias 别名属性 + */ + protected 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 linked {@link Link}指向的注解属性 + */ + protected void checkLinkedSelf(AnnotationAttribute original, AnnotationAttribute linked) { + boolean linkSelf = (original == linked) || ObjectUtil.equals(original.getAttribute(), linked.getAttribute()); + Assert.isFalse(linkSelf, "cannot link self [{}]", original.getAttribute()); + } + + /** + * 检查{@link Link}指向的注解属性是否存在 + * + * @param original {@link Link}注解的属性 + * @param linkedAttribute {@link Link}指向的注解属性 + * @param annotation {@link Link}注解 + */ + protected void checkLinkedAttributeNotNull(AnnotationAttribute original, AnnotationAttribute linkedAttribute, Link annotation) { + Assert.notNull(linkedAttribute, "cannot find linked attribute [{}] of original [{}] in [{}]", + original.getAttribute(), annotation.attribute(), + getLinkedAnnotationType(annotation, original.getAnnotationType()) + ); + } + +} diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractSynthesizedAnnotation.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractSynthesizedAnnotation.java new file mode 100644 index 000000000..bf33bfadb --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractSynthesizedAnnotation.java @@ -0,0 +1,195 @@ +package cn.hutool.core.annotation; + +import cn.hutool.core.lang.Opt; +import cn.hutool.core.util.ClassUtil; +import cn.hutool.core.util.ObjectUtil; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; +import java.util.function.UnaryOperator; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * {@link SynthesizedAnnotation}的基本实现 + * + * @param 根对象类型 + * @author huangchengxing + */ +public abstract class AbstractSynthesizedAnnotation implements Annotation, SynthesizedAnnotation { + + private final SynthesizedAnnotationAggregator owner; + private final R root; + private final Annotation annotation; + private final Map attributeMethodCaches; + private final int verticalDistance; + private final int horizontalDistance; + + /** + * 创建一个合成注解 + * + * @param owner 合成注解所属的合成注解聚合器 + * @param root 根对象 + * @param annotation 被合成的注解对象 + * @param verticalDistance 距离根对象的水平距离 + * @param horizontalDistance 距离根对象的垂直距离 + */ + protected AbstractSynthesizedAnnotation( + SynthesizedAnnotationAggregator owner, R root, Annotation annotation, int verticalDistance, int horizontalDistance) { + this.owner = owner; + this.root = root; + this.annotation = annotation; + this.verticalDistance = verticalDistance; + this.horizontalDistance = horizontalDistance; + this.attributeMethodCaches = new HashMap<>(); + this.attributeMethodCaches.putAll(loadAttributeMethods()); + } + + /** + * 加载注解属性 + * + * @return 注解属性 + */ + protected Map loadAttributeMethods() { + return Stream.of(annotation.annotationType().getDeclaredMethods()) + .filter(AnnotationUtil::isAttributeMethod) + .collect(Collectors.toMap(Method::getName, method -> new CacheableAnnotationAttribute(annotation, method))); + } + + /** + * 元注解是否存在该属性 + * + * @param attributeName 属性名 + * @return 是否存在该属性 + */ + public boolean hasAttribute(String attributeName) { + return attributeMethodCaches.containsKey(attributeName); + } + + /** + * 元注解是否存在该属性,且该属性的值类型是指定类型或其子类 + * + * @param attributeName 属性名 + * @param returnType 返回值类型 + * @return 是否存在该属性 + */ + @Override + public boolean hasAttribute(String attributeName, Class returnType) { + return Opt.ofNullable(attributeMethodCaches.get(attributeName)) + .filter(method -> ClassUtil.isAssignable(returnType, method.getAttributeType())) + .isPresent(); + } + + /** + * 获取该注解的全部属性 + * + * @return 注解属性 + */ + @Override + public Map getAttributes() { + return this.attributeMethodCaches; + } + + /** + * 设置属性值 + * + * @param attributeName 属性名称 + * @param attribute 注解属性 + */ + @Override + public void setAttribute(String attributeName, AnnotationAttribute attribute) { + attributeMethodCaches.put(attributeName, attribute); + } + + /** + * 替换属性值 + * + * @param attributeName 属性名 + * @param operator 替换操作 + */ + @Override + public void replaceAttribute(String attributeName, UnaryOperator operator) { + AnnotationAttribute old = attributeMethodCaches.get(attributeName); + if (ObjectUtil.isNotNull(old)) { + attributeMethodCaches.put(attributeName, operator.apply(old)); + } + } + + /** + * 获取属性值 + * + * @param attributeName 属性名 + * @return 属性值 + */ + @Override + public Object getAttributeValue(String attributeName) { + return Opt.ofNullable(attributeMethodCaches.get(attributeName)) + .map(AnnotationAttribute::getValue) + .get(); + } + + /** + * 获取所属的合成注解集合器 + * + * @return 合成注解 + */ + @Override + public SynthesizedAnnotationAggregator getOwner() { + return owner; + } + + /** + * 获取该合成注解对应的根节点 + * + * @return 合成注解对应的根节点 + */ + @Override + public R getRoot() { + return root; + } + + /** + * 获取被合成的注解对象 + * + * @return 注解对象 + */ + @Override + public Annotation getAnnotation() { + return annotation; + } + + /** + * 获取该合成注解与根对象的垂直距离。 + * 默认情况下,该距离即为当前注解与根对象之间相隔的层级数。 + * + * @return 合成注解与根对象的垂直距离 + */ + @Override + public int getVerticalDistance() { + return verticalDistance; + } + + /** + * 获取该合成注解与根对象的水平距离。 + * 默认情况下,该距离即为当前注解与根对象之间相隔的已经被扫描到的注解数。 + * + * @return 合成注解与根对象的水平距离 + */ + @Override + public int getHorizontalDistance() { + return horizontalDistance; + } + + /** + * 获取被合成的注解类型 + * + * @return 被合成的注解类型 + */ + @Override + public Class annotationType() { + return annotation.annotationType(); + } + +} diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AliasAttributePostProcessor.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AliasAnnotationPostProcessor.java similarity index 75% rename from hutool-core/src/main/java/cn/hutool/core/annotation/AliasAttributePostProcessor.java rename to hutool-core/src/main/java/cn/hutool/core/annotation/AliasAnnotationPostProcessor.java index 3c0c2f0cf..a06578a6f 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/AliasAttributePostProcessor.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AliasAnnotationPostProcessor.java @@ -11,13 +11,15 @@ import cn.hutool.core.util.ObjectUtil; import java.util.Map; /** - * 用于处理合成注解属性中{@link Alias}的映射关系, - * 该处理器会令{@link Alias#value()}指向的属性值强制覆盖当前属性 + *

    用于处理注解对象中带有{@link Alias}注解的属性。
    + * 当该处理器执行完毕后,{@link Alias}注解指向的目标注解的属性将会被包装并替换为 + * {@link ForceAliasedAnnotationAttribute}。 * * @author huangchengxing + * @see Alias * @see ForceAliasedAnnotationAttribute */ -public class AliasAttributePostProcessor implements SynthesizedAnnotationPostProcessor { +public class AliasAnnotationPostProcessor implements SynthesizedAnnotationPostProcessor { @Override public int order() { @@ -25,8 +27,8 @@ public class AliasAttributePostProcessor implements SynthesizedAnnotationPostPro } @Override - public void process(SynthesizedAnnotation annotation, SynthesizedAnnotationAggregator synthesizedAnnotationAggregator) { - final Map attributeMap = annotation.getAttributes(); + public void process(SynthesizedAnnotation synthesizedAnnotation, SynthesizedAnnotationAggregator aggregator) { + final Map attributeMap = synthesizedAnnotation.getAttributes(); // 记录别名与属性的关系 final ForestMap attributeAliasMappings = new LinkedForestMap<>(false); @@ -58,7 +60,7 @@ public class AliasAttributePostProcessor implements SynthesizedAnnotationPostPro attributeMap.put(attributeName, new ForceAliasedAnnotationAttribute(attribute, resolvedAttribute)); } }); - annotation.setAttributes(attributeMap); + synthesizedAnnotation.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 deleted file mode 100644 index 4fbe48766..000000000 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/AliasForLinkAttributePostProcessor.java +++ /dev/null @@ -1,106 +0,0 @@ -package cn.hutool.core.annotation; - -import cn.hutool.core.lang.Assert; -import cn.hutool.core.lang.Opt; -import cn.hutool.core.util.ObjectUtil; - -import java.util.HashMap; -import java.util.Map; -import java.util.function.BinaryOperator; - -/** - * 处理注解中带有{@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, SynthesizedAnnotationAggregator synthesizedAnnotationAggregator) { - final 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; - } - - // 获取注解属性 - final SynthesizedAnnotation aliasAnnotation = SyntheticAnnotationUtil.getLinkedAnnotation(link, synthesizedAnnotationAggregator, annotation.annotationType()); - if (ObjectUtil.isNull(aliasAnnotation)) { - return; - } - final AnnotationAttribute aliasAttribute = aliasAnnotation.getAttributes().get(link.attribute()); - SyntheticAnnotationUtil.checkLinkedAttributeNotNull(originalAttribute, aliasAttribute, link); - SyntheticAnnotationUtil.checkAttributeType(originalAttribute, aliasAttribute); - checkCircularDependency(originalAttribute, aliasAttribute); - - // aliasFor - if (RelationType.ALIAS_FOR.equals(link.type())) { - wrappingLinkedAttribute(synthesizedAnnotationAggregator, originalAttribute, aliasAttribute, AliasedAnnotationAttribute::new); - return; - } - // forceAliasFor - wrappingLinkedAttribute(synthesizedAnnotationAggregator, originalAttribute, aliasAttribute, ForceAliasedAnnotationAttribute::new); - }); - } - - /** - * 对指定注解属性进行包装,若该属性已被包装过,则递归以其为根节点的树结构,对树上全部的叶子节点进行包装 - */ - private void wrappingLinkedAttribute( - SynthesizedAnnotationAggregator synthesizedAnnotationAggregator, AnnotationAttribute originalAttribute, AnnotationAttribute aliasAttribute, BinaryOperator wrapping) { - // 不是包装属性 - if (!aliasAttribute.isWrapped()) { - processAttribute(synthesizedAnnotationAggregator, originalAttribute, aliasAttribute, wrapping); - return; - } - // 是包装属性 - final AbstractWrappedAnnotationAttribute wrapper = (AbstractWrappedAnnotationAttribute)aliasAttribute; - wrapper.getAllLinkedNonWrappedAttributes().forEach( - t -> processAttribute(synthesizedAnnotationAggregator, originalAttribute, t, wrapping) - ); - } - - /** - * 获取指定注解属性,然后将其再进行一层包装 - */ - private void processAttribute( - SynthesizedAnnotationAggregator synthesizedAnnotationAggregator, AnnotationAttribute originalAttribute, - AnnotationAttribute target, BinaryOperator wrapping) { - Opt.ofNullable(target.getAnnotationType()) - .map(synthesizedAnnotationAggregator::getSynthesizedAnnotation) - .ifPresent(t -> t.replaceAttribute(target.getAttributeName(), old -> wrapping.apply(old, originalAttribute))); - } - - /** - * 检查两个属性是否互为别名 - */ - private void checkCircularDependency(AnnotationAttribute original, AnnotationAttribute alias) { - SyntheticAnnotationUtil.checkLinkedSelf(original, alias); - Link annotation = SyntheticAnnotationUtil.getLink(alias, RelationType.ALIAS_FOR, RelationType.FORCE_ALIAS_FOR); - if (ObjectUtil.isNull(annotation)) { - return; - } - final Class aliasAnnotationType = SyntheticAnnotationUtil.getLinkedAnnotationType(annotation, alias.getAnnotationType()); - if (ObjectUtil.notEqual(aliasAnnotationType, original.getAnnotationType())) { - return; - } - Assert.notEquals( - annotation.attribute(), original.getAttributeName(), - "circular reference between the alias attribute [{}] and the original attribute [{}]", - alias.getAttribute(), original.getAttribute() - ); - } - -} diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AliasLinkAnnotationPostProcessor.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AliasLinkAnnotationPostProcessor.java new file mode 100644 index 000000000..5f3e26f5a --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AliasLinkAnnotationPostProcessor.java @@ -0,0 +1,126 @@ +package cn.hutool.core.annotation; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.lang.Opt; +import cn.hutool.core.util.ObjectUtil; + +import java.util.function.BinaryOperator; + +/** + *

    用于处理注解对象中带有{@link Link}注解,且{@link Link#type()}为 + * {@link RelationType#ALIAS_FOR}或{@link RelationType#FORCE_ALIAS_FOR}的属性。
    + * 当该处理器执行完毕后,{@link Link}注解指向的目标注解的属性将会被包装并替换为 + * {@link AliasedAnnotationAttribute}或{@link ForceAliasedAnnotationAttribute}。 + * + * @author huangchengxing + * @see RelationType#ALIAS_FOR + * @see AliasedAnnotationAttribute + * @see RelationType#FORCE_ALIAS_FOR + * @see ForceAliasedAnnotationAttribute + */ +public class AliasLinkAnnotationPostProcessor extends AbstractLinkAnnotationPostProcessor { + + private static final RelationType[] PROCESSED_RELATION_TYPES = new RelationType[]{ RelationType.ALIAS_FOR, RelationType.FORCE_ALIAS_FOR }; + + @Override + public int order() { + return Integer.MIN_VALUE + 2; + } + + /** + * 该处理器只处理{@link Link#type()}类型为{@link RelationType#ALIAS_FOR}和{@link RelationType#FORCE_ALIAS_FOR}的注解属性 + * + * @return 含有{@link RelationType#ALIAS_FOR}和{@link RelationType#FORCE_ALIAS_FOR}的数组 + */ + @Override + protected RelationType[] processTypes() { + return PROCESSED_RELATION_TYPES; + } + + /** + * 获取{@link Link}指向的目标注解属性,并根据{@link Link#type()}的类型是 + * {@link RelationType#ALIAS_FOR}或{@link RelationType#FORCE_ALIAS_FOR} + * 将目标注解属性包装为{@link AliasedAnnotationAttribute}或{@link ForceAliasedAnnotationAttribute}, + * 然后用包装后注解属性在对应的合成注解中替换原始的目标注解属性 + * + * @param aggregator 合成注解聚合器 + * @param annotation {@code originalAttribute}上的{@link Link}注解对象 + * @param originalAnnotation 当前正在处理的{@link SynthesizedAnnotation}对象 + * @param originalAttribute {@code originalAnnotation}上的待处理的属性 + * @param linkedAnnotation {@link Link}指向的关联注解对象 + * @param linkedAttribute {@link Link}指向的{@code originalAnnotation}中的关联属性,该参数可能为空 + */ + @Override + protected void processLinkedAttribute( + SynthesizedAnnotationAggregator aggregator, Link annotation, + SynthesizedAnnotation originalAnnotation, AnnotationAttribute originalAttribute, + SynthesizedAnnotation linkedAnnotation, AnnotationAttribute linkedAttribute) { + // 校验别名关系 + checkAliasRelation(annotation, originalAttribute, linkedAttribute); + // 处理aliasFor类型的关系 + if (RelationType.ALIAS_FOR.equals(annotation.type())) { + wrappingLinkedAttribute(aggregator, originalAttribute, linkedAttribute, AliasedAnnotationAttribute::new); + return; + } + // 处理forceAliasFor类型的关系 + wrappingLinkedAttribute(aggregator, originalAttribute, linkedAttribute, ForceAliasedAnnotationAttribute::new); + } + + /** + * 对指定注解属性进行包装,若该属性已被包装过,则递归以其为根节点的树结构,对树上全部的叶子节点进行包装 + */ + private void wrappingLinkedAttribute( + SynthesizedAnnotationAggregator synthesizedAnnotationAggregator, AnnotationAttribute originalAttribute, AnnotationAttribute aliasAttribute, BinaryOperator wrapping) { + // 不是包装属性 + if (!aliasAttribute.isWrapped()) { + processAttribute(synthesizedAnnotationAggregator, originalAttribute, aliasAttribute, wrapping); + return; + } + // 是包装属性 + final AbstractWrappedAnnotationAttribute wrapper = (AbstractWrappedAnnotationAttribute)aliasAttribute; + wrapper.getAllLinkedNonWrappedAttributes().forEach( + t -> processAttribute(synthesizedAnnotationAggregator, originalAttribute, t, wrapping) + ); + } + + /** + * 获取指定注解属性,然后将其再进行一层包装 + */ + private void processAttribute( + SynthesizedAnnotationAggregator synthesizedAnnotationAggregator, AnnotationAttribute originalAttribute, + AnnotationAttribute target, BinaryOperator wrapping) { + Opt.ofNullable(target.getAnnotationType()) + .map(synthesizedAnnotationAggregator::getSynthesizedAnnotation) + .ifPresent(t -> t.replaceAttribute(target.getAttributeName(), old -> wrapping.apply(old, originalAttribute))); + } + + /** + * 基本校验 + */ + private void checkAliasRelation(Link annotation, AnnotationAttribute originalAttribute, AnnotationAttribute linkedAttribute) { + checkLinkedAttributeNotNull(originalAttribute, linkedAttribute, annotation); + checkAttributeType(originalAttribute, linkedAttribute); + checkCircularDependency(originalAttribute, linkedAttribute); + } + + /** + * 检查两个属性是否互为别名 + */ + private void checkCircularDependency(AnnotationAttribute original, AnnotationAttribute alias) { + checkLinkedSelf(original, alias); + Link annotation = getLinkAnnotation(alias, RelationType.ALIAS_FOR, RelationType.FORCE_ALIAS_FOR); + if (ObjectUtil.isNull(annotation)) { + return; + } + final Class aliasAnnotationType = getLinkedAnnotationType(annotation, alias.getAnnotationType()); + if (ObjectUtil.notEqual(aliasAnnotationType, original.getAnnotationType())) { + return; + } + Assert.notEquals( + annotation.attribute(), original.getAttributeName(), + "circular reference between the alias attribute [{}] and the original attribute [{}]", + alias.getAttribute(), original.getAttribute() + ); + } + +} 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 index 53997f0ee..1c78d8175 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/AliasedAnnotationAttribute.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AliasedAnnotationAttribute.java @@ -5,8 +5,7 @@ package cn.hutool.core.annotation; * 当别名属性值为默认值时,优先返回原属性的值,当别名属性不为默认值时,优先返回别名属性的值 * * @author huangchengxing - * @see AliasForLinkAttributePostProcessor - * @see RelationType#FORCE_ALIAS_FOR + * @see AliasLinkAnnotationPostProcessor * @see RelationType#ALIAS_FOR */ public class AliasedAnnotationAttribute extends AbstractWrappedAnnotationAttribute { 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 520d60a90..e4596446b 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 @@ -40,7 +40,7 @@ public class CacheableSynthesizedAnnotationAttributeProcessor implements Synthes * 越靠前的越优先被取值。 */ public CacheableSynthesizedAnnotationAttributeProcessor() { - this(SyntheticAnnotationUtil.getChildPriorityAnnotationCompare()); + this(SynthesizedAnnotation.DEFAULT_CHILD_PRIORITY_COMPARATOR); } @SuppressWarnings("unchecked") 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 index 6638bcbd2..312219c39 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/ForceAliasedAnnotationAttribute.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/ForceAliasedAnnotationAttribute.java @@ -5,8 +5,8 @@ package cn.hutool.core.annotation; * 当调用{@link #getValue()}时,总是返回{@link #linked}的值 * * @author huangchengxing - * @see AliasAttributePostProcessor - * @see AliasForLinkAttributePostProcessor + * @see AliasAnnotationPostProcessor + * @see AliasLinkAnnotationPostProcessor * @see RelationType#ALIAS_FOR * @see RelationType#FORCE_ALIAS_FOR */ diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessor.java b/hutool-core/src/main/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessor.java new file mode 100644 index 000000000..e44b1889f --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessor.java @@ -0,0 +1,77 @@ +package cn.hutool.core.annotation; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ObjectUtil; + +/** + *

    用于处理注解对象中带有{@link Link}注解,且{@link Link#type()}为{@link RelationType#MIRROR_FOR}的属性。
    + * 当该处理器执行完毕后,原始合成注解中被{@link Link}注解的属性与{@link Link}注解指向的目标注解的属性, + * 都将会被被包装并替换为{@link MirroredAnnotationAttribute}。 + * + * @author huangchengxing + * @see RelationType#MIRROR_FOR + * @see MirroredAnnotationAttribute + */ +public class MirrorLinkAnnotationPostProcessor extends AbstractLinkAnnotationPostProcessor { + + private static final RelationType[] PROCESSED_RELATION_TYPES = new RelationType[]{ RelationType.MIRROR_FOR }; + + @Override + public int order() { + return Integer.MIN_VALUE + 1; + } + + /** + * 该处理器只处理{@link Link#type()}类型为{@link RelationType#MIRROR_FOR}的注解属性 + * + * @return 仅有{@link RelationType#MIRROR_FOR}数组 + */ + @Override + protected RelationType[] processTypes() { + return PROCESSED_RELATION_TYPES; + } + + /** + * 将存在镜像关系的合成注解属性分别包装为{@link MirroredAnnotationAttribute}对象, + * 并使用包装后{@link MirroredAnnotationAttribute}替换在它们对应合成注解实例中的{@link AnnotationAttribute} + * + * @param aggregator 合成注解聚合器 + * @param annotation {@code originalAttribute}上的{@link Link}注解对象 + * @param originalAnnotation 当前正在处理的{@link SynthesizedAnnotation}对象 + * @param originalAttribute {@code originalAnnotation}上的待处理的属性 + * @param linkedAnnotation {@link Link}指向的关联注解对象 + * @param linkedAttribute {@link Link}指向的{@code originalAnnotation}中的关联属性,该参数可能为空 + */ + @Override + protected void processLinkedAttribute( + SynthesizedAnnotationAggregator aggregator, Link annotation, + SynthesizedAnnotation originalAnnotation, AnnotationAttribute originalAttribute, + SynthesizedAnnotation linkedAnnotation, AnnotationAttribute linkedAttribute) { + // 校验镜像关系 + checkMirrorRelation(annotation, originalAttribute, linkedAttribute); + // 包装这一对镜像属性,并替换原注解中的对应属性 + final AnnotationAttribute mirroredOriginalAttribute = new MirroredAnnotationAttribute(originalAttribute, linkedAttribute); + originalAnnotation.setAttribute(originalAttribute.getAttributeName(), mirroredOriginalAttribute); + final AnnotationAttribute mirroredTargetAttribute = new MirroredAnnotationAttribute(linkedAttribute, originalAttribute); + linkedAnnotation.setAttribute(annotation.attribute(), mirroredTargetAttribute); + } + + /** + * 基本校验 + */ + private void checkMirrorRelation(Link annotation, AnnotationAttribute original, AnnotationAttribute mirror) { + // 镜像属性必须存在 + checkLinkedAttributeNotNull(original, mirror, annotation); + // 镜像属性返回值必须一致 + checkAttributeType(original, mirror); + // 镜像属性上必须存在对应的注解 + final Link mirrorAttributeAnnotation = getLinkAnnotation(mirror, RelationType.MIRROR_FOR); + 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 + ); + checkLinkedSelf(original, mirror); + } + +} 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 deleted file mode 100644 index 8547b8a9b..000000000 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/MirrorLinkAttributePostProcessor.java +++ /dev/null @@ -1,71 +0,0 @@ -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, SynthesizedAnnotationAggregator synthesizedAnnotationAggregator) { - 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, synthesizedAnnotationAggregator, 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); - synthesizedAnnotationAggregator.getSynthesizedAnnotation(originalAttribute.getAnnotationType()) - .setAttribute(originalAttributeName, mirroredOriginalAttribute); - final AnnotationAttribute mirroredTargetAttribute = new MirroredAnnotationAttribute(mirrorAttribute, originalAttribute); - mirrorAnnotation.setAttribute(link.attribute(), mirroredTargetAttribute); - }); - } - - private void checkMirrorRelation(Link annotation, AnnotationAttribute original, AnnotationAttribute mirror) { - // 镜像属性必须存在 - SyntheticAnnotationUtil.checkLinkedAttributeNotNull(original, mirror, annotation); - // 镜像属性返回值必须一致 - SyntheticAnnotationUtil.checkAttributeType(original, mirror); - // 镜像属性上必须存在对应的注解 - final Link mirrorAttributeAnnotation = SyntheticAnnotationUtil.getLink(mirror, RelationType.MIRROR_FOR); - 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 - ); - SyntheticAnnotationUtil.checkLinkedSelf(original, mirror); - } - -} 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 index e8f0f8efd..6bb800079 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/MirroredAnnotationAttribute.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/MirroredAnnotationAttribute.java @@ -6,7 +6,7 @@ import cn.hutool.core.lang.Assert; * 表示存在对应镜像属性的注解属性,当获取值时将根据{@link RelationType#MIRROR_FOR}的规则进行处理 * * @author huangchengxing - * @see MirrorLinkAttributePostProcessor + * @see MirrorLinkAnnotationPostProcessor * @see RelationType#MIRROR_FOR */ public class MirroredAnnotationAttribute extends AbstractWrappedAnnotationAttribute { 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 2607d94f7..483525e2e 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 @@ -3,15 +3,15 @@ package cn.hutool.core.annotation; import cn.hutool.core.collection.CollUtil; import java.lang.annotation.Annotation; +import java.util.Comparator; import java.util.Map; import java.util.function.UnaryOperator; /** *

    用于在{@link SynthesizedAnnotationAggregator}中表示一个处于合成状态的注解对象。
    - * 当对多个合成注解排序时,默认先按{@link #getVerticalDistance()}排序, - * 再按{@link #getHorizontalDistance()}排序。 - * 该顺序应当保证当合成注解与其他注解存在层级关系时, - * 离根对象最接近的注解被排在靠前的位置。 + * 当对多个合成注解排序时,默认使用{@link #DEFAULT_CHILD_PRIORITY_COMPARATOR}进行排序, + * 从保证合成注解按{@link #getVerticalDistance()}与{@link #getHorizontalDistance()}的返回值保持有序, + * 从而使得距离根元素更接近的注解对象在被处理是具有更高的优先级。 * * @author huangchengxing * @see SynthesizedAnnotationAggregator @@ -19,7 +19,16 @@ import java.util.function.UnaryOperator; public interface SynthesizedAnnotation extends Annotation, Comparable { /** - * 获取所属的合成注解 + * {@link SynthesizedAnnotation}使用的默认的比较器, + * 按照按{@link #getVerticalDistance()}和{@link #getHorizontalDistance()}的返回值进行排序。
    + * 一般情况下,排序越小的合成注解应当被优先处理。 + */ + Comparator DEFAULT_CHILD_PRIORITY_COMPARATOR = Comparator + .comparing(SynthesizedAnnotation::getVerticalDistance) + .thenComparing(SynthesizedAnnotation::getHorizontalDistance); + + /** + * 获取所属的合成注解聚合器 * * @return 合成注解 */ @@ -114,7 +123,7 @@ public interface SynthesizedAnnotation extends Annotation, Comparable该接口存在多个实现类,调用者应当保证在任何时候,对一批后置处理器的调用顺序都符合: *

      - *
    • {@link AliasAttributePostProcessor};
    • - *
    • {@link MirrorLinkAttributePostProcessor};
    • - *
    • {@link AliasForLinkAttributePostProcessor};
    • + *
    • {@link AliasAnnotationPostProcessor};
    • + *
    • {@link MirrorLinkAnnotationPostProcessor};
    • + *
    • {@link AliasLinkAnnotationPostProcessor};
    • *
    • 其他后置处理器;
    • *
    * * @author huangchengxing - * @see AliasAttributePostProcessor - * @see MirrorLinkAttributePostProcessor - * @see AliasForLinkAttributePostProcessor + * @see AliasAnnotationPostProcessor + * @see MirrorLinkAnnotationPostProcessor + * @see AliasLinkAnnotationPostProcessor */ public interface SynthesizedAnnotationPostProcessor extends Comparable { @@ -46,11 +46,11 @@ public interface SynthesizedAnnotationPostProcessor extends Comparable当扫描的注解对象经过{@link SynthesizedAnnotationSelector}处理后, - * 将会被转为{@link MetaAnnotation},并使用在实例化时指定的{@link AliasAttributePostProcessor} + * 将会被转为{@link MetaAnnotation},并使用在实例化时指定的{@link AliasAnnotationPostProcessor} * 进行后置处理。
    * 默认情况下,将注册以下后置处理器以对{@link Alias}与{@link Link}和其扩展注解提供支持: *
      - *
    • {@link AliasAttributePostProcessor};
    • - *
    • {@link MirrorLinkAttributePostProcessor};
    • - *
    • {@link AliasForLinkAttributePostProcessor};
    • + *
    • {@link AliasAnnotationPostProcessor};
    • + *
    • {@link MirrorLinkAnnotationPostProcessor};
    • + *
    • {@link AliasLinkAnnotationPostProcessor};
    • *
    * 若用户需要自行扩展,则需要保证上述三个处理器被正确注入当前实例。 * @@ -89,9 +84,9 @@ public class SynthesizedMetaAnnotationAggregator implements SynthesizedAnnotatio source, SynthesizedAnnotationSelector.NEAREST_AND_OLDEST_PRIORITY, new CacheableSynthesizedAnnotationAttributeProcessor(), Arrays.asList( - new AliasAttributePostProcessor(), - new MirrorLinkAttributePostProcessor(), - new AliasForLinkAttributePostProcessor() + new AliasAnnotationPostProcessor(), + new MirrorLinkAnnotationPostProcessor(), + new AliasLinkAnnotationPostProcessor() ) ); } @@ -284,160 +279,23 @@ public class SynthesizedMetaAnnotationAggregator implements SynthesizedAnnotatio } /** - * 元注解包装类 + * 注解包装类,表示{@link #source}以及{@link #source}所属层级结构中的全部关联注解对象 * * @author huangchengxing */ - public static class MetaAnnotation implements Annotation, SynthesizedAnnotation { - - private final SynthesizedAnnotationAggregator owner; - private final Annotation root; - private final Annotation annotation; - private final Map attributeMethodCaches; - private final int verticalDistance; - private final int horizontalDistance; - - public MetaAnnotation(SynthesizedAnnotationAggregator 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 = Stream.of(annotation.annotationType().getDeclaredMethods()) - .filter(AnnotationUtil::isAttributeMethod) - .collect(Collectors.toMap(Method::getName, method -> new CacheableAnnotationAttribute(annotation, method))); - } + public static class MetaAnnotation extends AbstractSynthesizedAnnotation { /** - * 获取所属的合成注解 + * 创建一个合成注解 * - * @return 合成注解 + * @param owner 合成注解所属的合成注解聚合器 + * @param root 根对象 + * @param annotation 被合成的注解对象 + * @param verticalDistance 距离根对象的水平距离 + * @param horizontalDistance 距离根对象的垂直距离 */ - @Override - public SynthesizedAnnotationAggregator getOwner() { - return owner; - } - - /** - * 获取注解类型 - * - * @return 注解类型 - */ - @Override - public Class annotationType() { - return annotation.annotationType(); - } - - /** - * 获取根注解 - * - * @return 根注解 - */ - @Override - public Annotation getRoot() { - return this.root; - } - - /** - * 获取元注解 - * - * @return 元注解 - */ - @Override - public Annotation getAnnotation() { - return annotation; - } - - /** - * 获取该合成注解与根注解之间相隔的层级数 - * - * @return 该合成注解与根注解之间相隔的层级数 - */ - @Override - public int getVerticalDistance() { - return verticalDistance; - } - - /** - * 获取该合成注解与根注解之间相隔的注解树 - * - * @return 该合成注解与根注解之间相隔的注解树 - */ - @Override - public int getHorizontalDistance() { - return horizontalDistance; - } - - /** - * 元注解是否存在该属性 - * - * @param attributeName 属性名 - * @return 是否存在该属性 - */ - public boolean hasAttribute(String attributeName) { - return attributeMethodCaches.containsKey(attributeName); - } - - /** - * 元注解是否存在该属性,且该属性的值类型是指定类型或其子类 - * - * @param attributeName 属性名 - * @param returnType 返回值类型 - * @return 是否存在该属性 - */ - @Override - public boolean hasAttribute(String attributeName, Class returnType) { - return Opt.ofNullable(attributeMethodCaches.get(attributeName)) - .filter(method -> ClassUtil.isAssignable(returnType, method.getAttributeType())) - .isPresent(); - } - - /** - * 获取该注解的全部属性 - * - * @return 注解属性 - */ - @Override - public Map getAttributes() { - return this.attributeMethodCaches; - } - - /** - * 设置属性值 - * - * @param attributeName 属性名称 - * @param attribute 注解属性 - */ - @Override - public void setAttribute(String attributeName, AnnotationAttribute attribute) { - attributeMethodCaches.put(attributeName, attribute); - } - - /** - * 替换属性值 - * - * @param attributeName 属性名 - * @param operator 替换操作 - */ - @Override - public void replaceAttribute(String attributeName, UnaryOperator operator) { - AnnotationAttribute old = attributeMethodCaches.get(attributeName); - if (ObjectUtil.isNotNull(old)) { - attributeMethodCaches.put(attributeName, operator.apply(old)); - } - } - - /** - * 获取属性值 - * - * @param attributeName 属性名 - * @return 属性值 - */ - @Override - public Object getAttributeValue(String attributeName) { - return Opt.ofNullable(attributeMethodCaches.get(attributeName)) - .map(AnnotationAttribute::getValue) - .get(); + protected MetaAnnotation(SynthesizedAnnotationAggregator owner, Annotation root, Annotation annotation, int verticalDistance, int horizontalDistance) { + super(owner, root, annotation, verticalDistance, horizontalDistance); } } 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 5fb175cc5..f33557d7f 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 @@ -80,7 +80,7 @@ class SyntheticAnnotationProxy implements InvocationHandler { void loadMethods() { methods.put("toString", (method, args) -> proxyToString()); methods.put("hashCode", (method, args) -> proxyHashCode()); - methods.put("getSynthesizedAnnotation", (method, args) -> proxyGetSyntheticAnnotation()); + methods.put("getSynthesizedAnnotationAggregator", (method, args) -> proxyGetSynthesizedAnnotationAggregator()); methods.put("getSynthesizedAnnotation", (method, args) -> proxyGetSynthesizedAnnotation()); methods.put("getRoot", (method, args) -> annotation.getRoot()); methods.put("getVerticalDistance", (method, args) -> annotation.getVerticalDistance()); @@ -110,7 +110,7 @@ class SyntheticAnnotationProxy implements InvocationHandler { return Objects.hash(annotation.getOwner(), annotation); } - private Object proxyGetSyntheticAnnotation() { + private Object proxyGetSynthesizedAnnotationAggregator() { return annotation.getOwner(); } @@ -134,7 +134,7 @@ class SyntheticAnnotationProxy implements InvocationHandler { * * @return 合成注解 */ - SynthesizedAnnotationAggregator getSyntheticAnnotation(); + SynthesizedAnnotationAggregator getSynthesizedAnnotationAggregator(); /** * 获取该代理注解对应的已合成注解 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 deleted file mode 100644 index 9a5f8f5a4..000000000 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticAnnotationUtil.java +++ /dev/null @@ -1,106 +0,0 @@ -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; -import java.util.Comparator; - -/** - * {@link SynthesizedAnnotationAggregator}相关工具,用于内部使用 - * - * @author huangchengxing - */ -class SyntheticAnnotationUtil { - - /** - * 从注解属性上获取指定类型的{@link Link}注解 - * - * @param attribute 注解属性 - * @param relationTypes 类型 - * @return 注解 - */ - static Link getLink(AnnotationAttribute attribute, RelationType... relationTypes) { - return Opt.ofNullable(attribute) - .map(t -> AnnotationUtil.getSynthesizedAnnotation(attribute.getAttribute(), Link.class)) - .filter(a -> ArrayUtil.contains(relationTypes, a.type())) - .get(); - } - - /** - * 从合成注解中获取{@link Link#type()}指定的注解对象 - * - * @param annotation {@link Link}注解 - * @param synthesizedAnnotationAggregator 合成注解 - */ - static SynthesizedAnnotation getLinkedAnnotation( - Link annotation, SynthesizedAnnotationAggregator synthesizedAnnotationAggregator, Class defaultType) { - final Class targetAnnotationType = getLinkedAnnotationType(annotation, defaultType); - return synthesizedAnnotationAggregator.getSynthesizedAnnotation(targetAnnotationType); - } - - /** - * 若{@link Link#annotation()}获取的类型{@link Annotation#getClass()},则返回{@code defaultType}, - * 否则返回{@link Link#annotation()}指定的类型 - * - * @param annotation {@link Link}注解 - * @param defaultType 默认注解类型 - * @return 注解类型 - */ - static Class getLinkedAnnotationType(Link annotation, Class defaultType) { - return ObjectUtil.equals(annotation.annotation(), Annotation.class) ? - defaultType : annotation.annotation(); - } - - /** - * 校验两个注解属性的返回值类型是否一致 - * - * @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 linked {@link Link}指向的注解属性 - */ - static void checkLinkedSelf(AnnotationAttribute original, AnnotationAttribute linked) { - boolean linkSelf = (original == linked) || ObjectUtil.equals(original.getAttribute(), linked.getAttribute()); - Assert.isFalse(linkSelf, "cannot link self [{}]", original.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(), - getLinkedAnnotationType(annotation, original.getAnnotationType()) - ); - } - - /** - * 获取按{@link SynthesizedAnnotation#getVerticalDistance()}和{@link SynthesizedAnnotation#getHorizontalDistance()}排序的比较器 - * - * @return 比较值 - */ - static Comparator getChildPriorityAnnotationCompare() { - return Comparator.comparing(SynthesizedAnnotation::getVerticalDistance) - .thenComparing(SynthesizedAnnotation::getHorizontalDistance); - } - -} diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/AliasAttributePostProcessorTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/AliasAnnotationPostProcessorTest.java similarity index 97% rename from hutool-core/src/test/java/cn/hutool/core/annotation/AliasAttributePostProcessorTest.java rename to hutool-core/src/test/java/cn/hutool/core/annotation/AliasAnnotationPostProcessorTest.java index f7ad67e8b..8c792a1c9 100644 --- a/hutool-core/src/test/java/cn/hutool/core/annotation/AliasAttributePostProcessorTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/AliasAnnotationPostProcessorTest.java @@ -12,11 +12,11 @@ import java.util.HashMap; import java.util.Map; import java.util.function.UnaryOperator; -public class AliasAttributePostProcessorTest { +public class AliasAnnotationPostProcessorTest { @Test public void processTest() { - AliasAttributePostProcessor processor = new AliasAttributePostProcessor(); + AliasAnnotationPostProcessor processor = new AliasAnnotationPostProcessor(); Map, SynthesizedAnnotation> annotationMap = new HashMap<>(); SynthesizedAnnotationAggregator synthesizedAnnotationAggregator = new TestSynthesizedAnnotationAggregator(annotationMap); diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/AliasForLinkAttributePostProcessorTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/AliasLinkAnnotationPostProcessorTest.java similarity index 96% rename from hutool-core/src/test/java/cn/hutool/core/annotation/AliasForLinkAttributePostProcessorTest.java rename to hutool-core/src/test/java/cn/hutool/core/annotation/AliasLinkAnnotationPostProcessorTest.java index 5d0c4ad37..339b012d5 100644 --- a/hutool-core/src/test/java/cn/hutool/core/annotation/AliasForLinkAttributePostProcessorTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/AliasLinkAnnotationPostProcessorTest.java @@ -12,11 +12,11 @@ import java.util.HashMap; import java.util.Map; import java.util.function.UnaryOperator; -public class AliasForLinkAttributePostProcessorTest { +public class AliasLinkAnnotationPostProcessorTest { @Test public void processForceAliasForTest() { - AliasForLinkAttributePostProcessor processor = new AliasForLinkAttributePostProcessor(); + AliasLinkAnnotationPostProcessor processor = new AliasLinkAnnotationPostProcessor(); Map, SynthesizedAnnotation> annotationMap = new HashMap<>(); SynthesizedAnnotationAggregator synthesizedAnnotationAggregator = new TestSynthesizedAnnotationAggregator(annotationMap); @@ -40,7 +40,7 @@ public class AliasForLinkAttributePostProcessorTest { @Test public void processAliasForTest() { - AliasForLinkAttributePostProcessor processor = new AliasForLinkAttributePostProcessor(); + AliasLinkAnnotationPostProcessor processor = new AliasLinkAnnotationPostProcessor(); Map, SynthesizedAnnotation> annotationMap = new HashMap<>(); SynthesizedAnnotationAggregator synthesizedAnnotationAggregator = new TestSynthesizedAnnotationAggregator(annotationMap); diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/MirrorLinkAttributePostProcessorTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessorTest.java similarity index 97% rename from hutool-core/src/test/java/cn/hutool/core/annotation/MirrorLinkAttributePostProcessorTest.java rename to hutool-core/src/test/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessorTest.java index d6e8902db..2c741e32d 100644 --- a/hutool-core/src/test/java/cn/hutool/core/annotation/MirrorLinkAttributePostProcessorTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessorTest.java @@ -12,11 +12,11 @@ import java.util.HashMap; import java.util.Map; import java.util.function.UnaryOperator; -public class MirrorLinkAttributePostProcessorTest { +public class MirrorLinkAnnotationPostProcessorTest { @Test public void processTest() { - MirrorLinkAttributePostProcessor processor = new MirrorLinkAttributePostProcessor(); + MirrorLinkAnnotationPostProcessor processor = new MirrorLinkAnnotationPostProcessor(); Map, SynthesizedAnnotation> annotationMap = new HashMap<>(); SynthesizedAnnotationAggregator synthesizedAnnotationAggregator = new TestSynthesizedAnnotationAggregator(annotationMap); diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/scanner/TypeAnnotationScannerTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/scanner/TypeAnnotationScannerTest.java index 0b3f31f81..c9eed2014 100644 --- a/hutool-core/src/test/java/cn/hutool/core/annotation/scanner/TypeAnnotationScannerTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/scanner/TypeAnnotationScannerTest.java @@ -36,7 +36,7 @@ public class TypeAnnotationScannerTest { annotations.forEach(a -> Assert.assertEquals(a.annotationType(), AnnotationForScannerTest.class)); // 不查找父类 - scanner = new TypeAnnotationScanner().setIncludeSupperClass(false); + scanner = new TypeAnnotationScanner().setIncludeSuperClass(false); annotations = scanner.getAnnotations(Example.class); Assert.assertEquals(1, annotations.size()); annotations.forEach(a -> Assert.assertEquals(a.annotationType(), AnnotationForScannerTest.class)); @@ -88,7 +88,7 @@ public class TypeAnnotationScannerTest { // 不查找父类 map.clear(); new TypeAnnotationScanner() - .setIncludeSupperClass(false) + .setIncludeSuperClass(false) .scan( (index, annotation) -> map.computeIfAbsent(index, i -> new ArrayList<>()).add(annotation), Example.class, null From da7dbc2ba309579edd7650969bfef2e06940fb02 Mon Sep 17 00:00:00 2001 From: huangchengxing <841396397@qq.com> Date: Wed, 13 Jul 2022 22:40:03 +0800 Subject: [PATCH 08/15] fix code --- .../AbstractTypeAnnotationScanner.java | 23 +++++++++---------- .../scanner/MethodAnnotationScanner.java | 2 +- .../scanner/TypeAnnotationScanner.java | 6 ++--- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/AbstractTypeAnnotationScanner.java b/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/AbstractTypeAnnotationScanner.java index a30b61593..d91e2af00 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/AbstractTypeAnnotationScanner.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/AbstractTypeAnnotationScanner.java @@ -24,8 +24,7 @@ public abstract class AbstractTypeAnnotationScanner> filter, Set> excludeTypes) { + protected AbstractTypeAnnotationScanner(boolean includeSuperClass, boolean includeInterfaces, Predicate> filter, Set> excludeTypes) { Assert.notNull(filter, "filter must not null"); Assert.notNull(excludeTypes, "excludeTypes must not null"); - this.includeSupperClass = includeSupperClass; + this.includeSuperClass = includeSuperClass; this.includeInterfaces = includeInterfaces; this.filter = filter; this.excludeTypes = excludeTypes; @@ -82,8 +81,8 @@ public abstract class AbstractTypeAnnotationScanner> nextClassQueue, Class targetClass) { - if (includeSupperClass) { + if (includeSuperClass) { final Class superClass = targetClass.getSuperclass(); if (!ObjectUtil.equals(superClass, Object.class) && ObjectUtil.isNotNull(superClass)) { nextClassQueue.add(superClass); diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/MethodAnnotationScanner.java b/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/MethodAnnotationScanner.java index 8202c8d80..ac054f88f 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/MethodAnnotationScanner.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/MethodAnnotationScanner.java @@ -96,7 +96,7 @@ public class MethodAnnotationScanner extends AbstractTypeAnnotationScanner Date: Wed, 13 Jul 2022 15:11:24 +0800 Subject: [PATCH 09/15] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=95=9C=E5=83=8F?= =?UTF-8?q?=E5=B1=9E=E6=80=A7=E4=BC=9A=E8=A2=AB=E9=87=8D=E5=A4=8D=E5=8C=85?= =?UTF-8?q?=E8=A3=85=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AbstractLinkAnnotationPostProcessor.java | 4 +- .../MirrorLinkAnnotationPostProcessor.java | 55 +++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractLinkAnnotationPostProcessor.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractLinkAnnotationPostProcessor.java index 4c781101f..6dbd38142 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractLinkAnnotationPostProcessor.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractLinkAnnotationPostProcessor.java @@ -44,7 +44,9 @@ public abstract class AbstractLinkAnnotationPostProcessor implements Synthesized } final AnnotationAttribute linkedAttribute = linkedAnnotation.getAttributes().get(link.attribute()); // 处理 - processLinkedAttribute(aggregator, link, synthesizedAnnotation, originalAttribute, + processLinkedAttribute( + aggregator, link, + synthesizedAnnotation, synthesizedAnnotation.getAttributes().get(originalAttributeName), linkedAnnotation, linkedAttribute ); }); diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessor.java b/hutool-core/src/main/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessor.java index e44b1889f..9b1601989 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessor.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessor.java @@ -2,6 +2,7 @@ package cn.hutool.core.annotation; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; /** *

    用于处理注解对象中带有{@link Link}注解,且{@link Link#type()}为{@link RelationType#MIRROR_FOR}的属性。
    @@ -47,6 +48,17 @@ public class MirrorLinkAnnotationPostProcessor extends AbstractLinkAnnotationPos SynthesizedAnnotationAggregator aggregator, Link annotation, SynthesizedAnnotation originalAnnotation, AnnotationAttribute originalAttribute, SynthesizedAnnotation linkedAnnotation, AnnotationAttribute linkedAttribute) { + + // 镜像属性必然成对出现,因此此处必定存在三种情况: + // 1.两属性都不为镜像属性,此时继续进行后续处理; + // 2.两属性都为镜像属性,并且指向对方,此时无需后续处理; + // 3.两属性仅有任意一属性为镜像属性,此时镜像属性必然未指向当前原始属性,此时应该抛出异常; + if (originalAttribute instanceof MirroredAnnotationAttribute + || linkedAttribute instanceof MirroredAnnotationAttribute) { + checkMirrored(originalAttribute, linkedAttribute); + return; + } + // 校验镜像关系 checkMirrorRelation(annotation, originalAttribute, linkedAttribute); // 包装这一对镜像属性,并替换原注解中的对应属性 @@ -56,6 +68,49 @@ public class MirrorLinkAnnotationPostProcessor extends AbstractLinkAnnotationPos linkedAnnotation.setAttribute(annotation.attribute(), mirroredTargetAttribute); } + /** + * 检查映射关系是否正确 + */ + private void checkMirrored(AnnotationAttribute original, AnnotationAttribute mirror) { + final boolean originalAttributeMirrored = original instanceof MirroredAnnotationAttribute; + final boolean mirrorAttributeMirrored = mirror instanceof MirroredAnnotationAttribute; + + // 校验通过 + final boolean passed = originalAttributeMirrored && mirrorAttributeMirrored + && ObjectUtil.equals(((MirroredAnnotationAttribute)original).getLinked(), ((MirroredAnnotationAttribute)mirror).getOriginal()); + if (passed) { + return; + } + + // 校验失败,拼装异常信息用于抛出异常 + String errorMsg; + // 原始字段已经跟其他字段形成镜像 + if (originalAttributeMirrored && !mirrorAttributeMirrored) { + errorMsg = StrUtil.format( + "attribute [{}] cannot mirror for [{}], because it's already mirrored for [{}]", + original.getAttribute(), mirror.getAttribute(), ((MirroredAnnotationAttribute)original).getLinked() + ); + } + // 镜像字段已经跟其他字段形成镜像 + else if (!originalAttributeMirrored && mirrorAttributeMirrored) { + errorMsg = StrUtil.format( + "attribute [{}] cannot mirror for [{}], because it's already mirrored for [{}]", + mirror.getAttribute(), original.getAttribute(), ((MirroredAnnotationAttribute)mirror).getLinked() + ); + } + // 两者都形成了镜像,但是都未指向对方,理论上不会存在该情况 + else { + errorMsg = StrUtil.format( + "attribute [{}] cannot mirror for [{}], because [{}] already mirrored for [{}] and [{}] already mirrored for [{}]", + mirror.getAttribute(), original.getAttribute(), + mirror.getAttribute(), ((MirroredAnnotationAttribute)mirror).getLinked(), + original.getAttribute(), ((MirroredAnnotationAttribute)original).getLinked() + ); + } + + throw new IllegalArgumentException(errorMsg); + } + /** * 基本校验 */ From 18f67274b81179c061d9336f6e90b9e8de51d7c9 Mon Sep 17 00:00:00 2001 From: huangchengxing <841396397@qq.com> Date: Wed, 13 Jul 2022 22:42:16 +0800 Subject: [PATCH 10/15] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=EF=BC=8C=E8=B0=83=E6=95=B4=E7=B1=BB=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AbstractLinkAnnotationPostProcessor.java | 8 +-- .../AbstractSynthesizedAnnotation.java | 8 +-- .../core/annotation/AggregateAnnotation.java | 27 ++++++++++ .../AliasAnnotationPostProcessor.java | 2 +- .../AliasLinkAnnotationPostProcessor.java | 6 +-- .../core/annotation/AnnotationAggregator.java | 49 ------------------- .../core/annotation/AnnotationAttribute.java | 2 +- .../core/annotation/AnnotationUtil.java | 8 +-- .../java/cn/hutool/core/annotation/Link.java | 4 +- .../MirrorLinkAnnotationPostProcessor.java | 2 +- .../hutool/core/annotation/RelationType.java | 6 +-- ...va => SynthesizedAggregateAnnotation.java} | 35 ++++++++----- .../annotation/SynthesizedAnnotation.java | 6 +-- ...nthesizedAnnotationAttributeProcessor.java | 2 +- .../SynthesizedAnnotationPostProcessor.java | 19 ++++++- .../SynthesizedAnnotationSelector.java | 2 +- ...> SynthesizedMetaAggregateAnnotation.java} | 32 +++++------- .../annotation/SyntheticAnnotationProxy.java | 10 ++-- .../scanner/MethodAnnotationScanner.java | 2 +- .../AliasAnnotationPostProcessorTest.java | 17 +++---- .../AliasLinkAnnotationPostProcessorTest.java | 19 +++---- ...sizedAnnotationAttributeProcessorTest.java | 2 +- ...MirrorLinkAnnotationPostProcessorTest.java | 16 +++--- .../SynthesizedAnnotationSelectorTest.java | 2 +- .../SyntheticMetaAnnotationTest.java | 48 +++++++++--------- 25 files changed, 158 insertions(+), 176 deletions(-) create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/AggregateAnnotation.java delete mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationAggregator.java rename hutool-core/src/main/java/cn/hutool/core/annotation/{SynthesizedAnnotationAggregator.java => SynthesizedAggregateAnnotation.java} (78%) rename hutool-core/src/main/java/cn/hutool/core/annotation/{SynthesizedMetaAnnotationAggregator.java => SynthesizedMetaAggregateAnnotation.java} (89%) diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractLinkAnnotationPostProcessor.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractLinkAnnotationPostProcessor.java index 6dbd38142..d510176ad 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractLinkAnnotationPostProcessor.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractLinkAnnotationPostProcessor.java @@ -21,7 +21,7 @@ public abstract class AbstractLinkAnnotationPostProcessor implements Synthesized /** * 若一个注解属性上存在{@link Link}注解,注解的{@link Link#type()}返回值在{@link #processTypes()}中存在, - * 且此{@link Link}指定的注解对象在当前的{@link SynthesizedAnnotationAggregator}中存在, + * 且此{@link Link}指定的注解对象在当前的{@link SynthesizedAggregateAnnotation}中存在, * 则从聚合器中获取类型对应的合成注解对象,与该对象中的指定属性,然后将全部关联数据交给 * {@link #processLinkedAttribute}处理。 * @@ -29,7 +29,7 @@ public abstract class AbstractLinkAnnotationPostProcessor implements Synthesized * @param aggregator 合成注解聚合器 */ @Override - public void process(SynthesizedAnnotation synthesizedAnnotation, SynthesizedAnnotationAggregator aggregator) { + public void process(SynthesizedAnnotation synthesizedAnnotation, SynthesizedAggregateAnnotation aggregator) { final Map attributeMap = new HashMap<>(synthesizedAnnotation.getAttributes()); attributeMap.forEach((originalAttributeName, originalAttribute) -> { // 获取注解 @@ -72,7 +72,7 @@ public abstract class AbstractLinkAnnotationPostProcessor implements Synthesized * @param linkedAttribute {@link Link}指向的{@code originalAnnotation}中的关联属性,该参数可能为空 */ protected abstract void processLinkedAttribute( - SynthesizedAnnotationAggregator aggregator, Link annotation, + SynthesizedAggregateAnnotation aggregator, Link annotation, SynthesizedAnnotation originalAnnotation, AnnotationAttribute originalAttribute, SynthesizedAnnotation linkedAnnotation, AnnotationAttribute linkedAttribute ); @@ -100,7 +100,7 @@ public abstract class AbstractLinkAnnotationPostProcessor implements Synthesized * @param synthesizedAnnotationAggregator 合成注解 */ protected SynthesizedAnnotation getLinkedAnnotation( - Link annotation, SynthesizedAnnotationAggregator synthesizedAnnotationAggregator, Class defaultType) { + Link annotation, SynthesizedAggregateAnnotation synthesizedAnnotationAggregator, Class defaultType) { final Class targetAnnotationType = getLinkedAnnotationType(annotation, defaultType); return synthesizedAnnotationAggregator.getSynthesizedAnnotation(targetAnnotationType); } diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractSynthesizedAnnotation.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractSynthesizedAnnotation.java index bf33bfadb..c8b906cc5 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractSynthesizedAnnotation.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractSynthesizedAnnotation.java @@ -20,7 +20,7 @@ import java.util.stream.Stream; */ public abstract class AbstractSynthesizedAnnotation implements Annotation, SynthesizedAnnotation { - private final SynthesizedAnnotationAggregator owner; + private final SynthesizedAggregateAnnotation owner; private final R root; private final Annotation annotation; private final Map attributeMethodCaches; @@ -37,7 +37,7 @@ public abstract class AbstractSynthesizedAnnotation implements Annotation, Sy * @param horizontalDistance 距离根对象的垂直距离 */ protected AbstractSynthesizedAnnotation( - SynthesizedAnnotationAggregator owner, R root, Annotation annotation, int verticalDistance, int horizontalDistance) { + SynthesizedAggregateAnnotation owner, R root, Annotation annotation, int verticalDistance, int horizontalDistance) { this.owner = owner; this.root = root; this.annotation = annotation; @@ -53,7 +53,7 @@ public abstract class AbstractSynthesizedAnnotation implements Annotation, Sy * @return 注解属性 */ protected Map loadAttributeMethods() { - return Stream.of(annotation.annotationType().getDeclaredMethods()) + return Stream.of(ClassUtil.getDeclaredMethods(annotation.annotationType())) .filter(AnnotationUtil::isAttributeMethod) .collect(Collectors.toMap(Method::getName, method -> new CacheableAnnotationAttribute(annotation, method))); } @@ -136,7 +136,7 @@ public abstract class AbstractSynthesizedAnnotation implements Annotation, Sy * @return 合成注解 */ @Override - public SynthesizedAnnotationAggregator getOwner() { + public SynthesizedAggregateAnnotation getOwner() { return owner; } diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AggregateAnnotation.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AggregateAnnotation.java new file mode 100644 index 000000000..000c99175 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AggregateAnnotation.java @@ -0,0 +1,27 @@ +package cn.hutool.core.annotation; + +import java.lang.annotation.Annotation; + +/** + * 表示一组被聚合在一起的注解对象 + * + * @author huangchengxing + */ +public interface AggregateAnnotation extends Annotation { + + /** + * 在聚合中是否存在的指定类型注解对象 + * + * @param annotationType 注解类型 + * @return 是否 + */ + boolean isAnnotationPresent(Class annotationType); + + /** + * 获取聚合中的全部注解对象 + * + * @return 注解对象 + */ + Annotation[] getAnnotations(); + +} diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AliasAnnotationPostProcessor.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AliasAnnotationPostProcessor.java index a06578a6f..fe8aa890d 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/AliasAnnotationPostProcessor.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AliasAnnotationPostProcessor.java @@ -27,7 +27,7 @@ public class AliasAnnotationPostProcessor implements SynthesizedAnnotationPostPr } @Override - public void process(SynthesizedAnnotation synthesizedAnnotation, SynthesizedAnnotationAggregator aggregator) { + public void process(SynthesizedAnnotation synthesizedAnnotation, SynthesizedAggregateAnnotation aggregator) { final Map attributeMap = synthesizedAnnotation.getAttributes(); // 记录别名与属性的关系 diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AliasLinkAnnotationPostProcessor.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AliasLinkAnnotationPostProcessor.java index 5f3e26f5a..d5fe28f18 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/AliasLinkAnnotationPostProcessor.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AliasLinkAnnotationPostProcessor.java @@ -52,7 +52,7 @@ public class AliasLinkAnnotationPostProcessor extends AbstractLinkAnnotationPost */ @Override protected void processLinkedAttribute( - SynthesizedAnnotationAggregator aggregator, Link annotation, + SynthesizedAggregateAnnotation aggregator, Link annotation, SynthesizedAnnotation originalAnnotation, AnnotationAttribute originalAttribute, SynthesizedAnnotation linkedAnnotation, AnnotationAttribute linkedAttribute) { // 校验别名关系 @@ -70,7 +70,7 @@ public class AliasLinkAnnotationPostProcessor extends AbstractLinkAnnotationPost * 对指定注解属性进行包装,若该属性已被包装过,则递归以其为根节点的树结构,对树上全部的叶子节点进行包装 */ private void wrappingLinkedAttribute( - SynthesizedAnnotationAggregator synthesizedAnnotationAggregator, AnnotationAttribute originalAttribute, AnnotationAttribute aliasAttribute, BinaryOperator wrapping) { + SynthesizedAggregateAnnotation synthesizedAnnotationAggregator, AnnotationAttribute originalAttribute, AnnotationAttribute aliasAttribute, BinaryOperator wrapping) { // 不是包装属性 if (!aliasAttribute.isWrapped()) { processAttribute(synthesizedAnnotationAggregator, originalAttribute, aliasAttribute, wrapping); @@ -87,7 +87,7 @@ public class AliasLinkAnnotationPostProcessor extends AbstractLinkAnnotationPost * 获取指定注解属性,然后将其再进行一层包装 */ private void processAttribute( - SynthesizedAnnotationAggregator synthesizedAnnotationAggregator, AnnotationAttribute originalAttribute, + SynthesizedAggregateAnnotation synthesizedAnnotationAggregator, AnnotationAttribute originalAttribute, AnnotationAttribute target, BinaryOperator wrapping) { Opt.ofNullable(target.getAnnotationType()) .map(synthesizedAnnotationAggregator::getSynthesizedAnnotation) diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationAggregator.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationAggregator.java deleted file mode 100644 index 25e15d1cf..000000000 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationAggregator.java +++ /dev/null @@ -1,49 +0,0 @@ -package cn.hutool.core.annotation; - -import java.lang.annotation.Annotation; -import java.lang.reflect.AnnotatedElement; - -/** - * 表示一组被聚合在一起的注解对象 - * - * @author huangchengxing - */ -public interface AnnotationAggregator extends AnnotatedElement { - - /** - * 获取在聚合中存在的指定注解对象 - * - * @param annotationType 注解类型 - * @param 注解类型 - * @return 注解对象 - */ - @Override - T getAnnotation(Class annotationType); - - /** - * 在聚合中是否存在的指定类型注解对象 - * - * @param annotationType 注解类型 - * @return 是否 - */ - @Override - boolean isAnnotationPresent(Class annotationType); - - /** - * 获取聚合中的全部注解对象 - * - * @return 注解对象 - */ - @Override - Annotation[] getAnnotations(); - - /** - * 从聚合中获取指定类型的属性值 - * - * @param attributeName 属性名称 - * @param attributeType 属性类型 - * @return 属性值 - */ - Object getAttribute(String attributeName, Class attributeType); - -} 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 index 77631b12d..d69d5772b 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationAttribute.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationAttribute.java @@ -7,7 +7,7 @@ import java.lang.reflect.Method; /** *

    表示注解的某个属性,等同于绑定的调用对象的{@link Method}方法。
    - * 在{@link SynthesizedAnnotationAggregator}的解析以及取值过程中, + * 在{@link SynthesizedAggregateAnnotation}的解析以及取值过程中, * 可以通过设置{@link SynthesizedAnnotation}的注解属性, * 从而使得可以从一个注解对象中属性获取另一个注解对象的属性值 * 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 570ef8b6c..b2d947111 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 @@ -351,11 +351,11 @@ public class AnnotationUtil { * @param annotationType 注解类 * @param 注解类型 * @return 合成注解 - * @see SynthesizedAnnotationAggregator + * @see SynthesizedAggregateAnnotation */ public static T getSynthesizedAnnotation(Annotation annotation, Class annotationType) { // TODO 缓存合成注解信息,避免重复解析 - return SynthesizedAnnotationAggregator.of(annotation).synthesize(annotationType); + return SynthesizedAggregateAnnotation.of(annotation).synthesize(annotationType); } /** @@ -369,7 +369,7 @@ public class AnnotationUtil { * @param annotationType 注解类 * @param 注解类型 * @return 合成注解 - * @see SynthesizedAnnotationAggregator + * @see SynthesizedAggregateAnnotation */ public static T getSynthesizedAnnotation(AnnotatedElement annotatedEle, Class annotationType) { T target = annotatedEle.getAnnotation(annotationType); @@ -397,7 +397,7 @@ public class AnnotationUtil { * @param annotationType 注解类 * @param 注解类型 * @return 合成注解 - * @see SynthesizedAnnotationAggregator + * @see SynthesizedAggregateAnnotation */ public static List getAllSynthesizedAnnotations(AnnotatedElement annotatedEle, Class annotationType) { AnnotationScanner[] scanners = new AnnotationScanner[]{ 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 index 6d1bb3f50..f04683ab5 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/Link.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/Link.java @@ -4,7 +4,7 @@ import java.lang.annotation.*; /** *

    用于在同一注解中,或具有一定关联的不同注解的属性中,表明这些属性之间具有特定的关联关系。 - * 在通过{@link SynthesizedAnnotationAggregator}获取合成注解后,合成注解获取属性值时会根据该注解进行调整。
    + * 在通过{@link SynthesizedAggregateAnnotation}获取合成注解后,合成注解获取属性值时会根据该注解进行调整。
    * *

    该注解存在三个字注解:{@link MirrorFor}、{@link ForceAliasFor}或{@link AliasFor}, * 使用三个子注解等同于{@link Link}。但是需要注意的是, @@ -14,7 +14,7 @@ import java.lang.annotation.*; * 注意:该注解的优先级低于{@link Alias} * * @author huangchengxing - * @see SynthesizedAnnotationAggregator + * @see SynthesizedAggregateAnnotation * @see RelationType * @see AliasFor * @see MirrorFor diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessor.java b/hutool-core/src/main/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessor.java index 9b1601989..f523473e8 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessor.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessor.java @@ -45,7 +45,7 @@ public class MirrorLinkAnnotationPostProcessor extends AbstractLinkAnnotationPos */ @Override protected void processLinkedAttribute( - SynthesizedAnnotationAggregator aggregator, Link annotation, + SynthesizedAggregateAnnotation aggregator, Link annotation, SynthesizedAnnotation originalAnnotation, AnnotationAttribute originalAttribute, SynthesizedAnnotation linkedAnnotation, AnnotationAttribute linkedAttribute) { 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 index accfd98ac..1c2248cf3 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/RelationType.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/RelationType.java @@ -3,8 +3,8 @@ package cn.hutool.core.annotation; /** *

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

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

      @@ -15,7 +15,7 @@ package cn.hutool.core.annotation; *
    * * @author huangchengxing - * @see SynthesizedAnnotationAggregator + * @see SynthesizedAggregateAnnotation * @see Link */ public enum RelationType { diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationAggregator.java b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAggregateAnnotation.java similarity index 78% rename from hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationAggregator.java rename to hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAggregateAnnotation.java index 1dd24f2fb..fe85356a1 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationAggregator.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAggregateAnnotation.java @@ -1,16 +1,15 @@ package cn.hutool.core.annotation; import java.lang.annotation.Annotation; -import java.lang.reflect.AnnotatedElement; import java.util.Collection; /** - * 表示基于特定规则聚合的一组注解对象 + * 表示基于特定规则聚合,将一组注解聚合而来的注解对象 * *

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

    合成注解可以作为一个特殊的{@link Annotation}或者{@link AnnotatedElement}, - * 当调用{@link Annotation}的方法时,应当返回当前实例本身的有效信息, - * 而当调用{@link AnnotatedElement}的方法时,应当返回用于合成该对象的相关注解的信息。 - * * @author huangchengxing * @see SynthesizedAnnotation * @see SynthesizedAnnotationSelector * @see SynthesizedAnnotationAttributeProcessor * @see SynthesizedAnnotationPostProcessor - * @see SynthesizedMetaAnnotationAggregator + * @see SynthesizedMetaAggregateAnnotation */ -public interface SynthesizedAnnotationAggregator extends Annotation, AnnotationAggregator { +public interface SynthesizedAggregateAnnotation extends Annotation, AggregateAnnotation { + + /** + * 获取在聚合中存在的指定注解对象 + * + * @param annotationType 注解类型 + * @param 注解类型 + * @return 注解对象 + */ + T getAnnotation(Class annotationType); /** * 获取合成注解选择器 @@ -87,6 +91,15 @@ public interface SynthesizedAnnotationAggregator extends Annotation, AnnotationA return this.getClass(); } + /** + * 从聚合中获取指定类型的属性值 + * + * @param attributeName 属性名称 + * @param attributeType 属性类型 + * @return 属性值 + */ + Object getAttribute(String attributeName, Class attributeType); + /** * 获取合成注解 * @@ -103,8 +116,8 @@ public interface SynthesizedAnnotationAggregator extends Annotation, AnnotationA * @param 注解类型 * @return 合成注解 */ - static SynthesizedAnnotationAggregator of(T rootAnnotation) { - return new SynthesizedMetaAnnotationAggregator(rootAnnotation); + static SynthesizedAggregateAnnotation of(T rootAnnotation) { + return new SynthesizedMetaAggregateAnnotation(rootAnnotation); } } 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 483525e2e..40bd70499 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 @@ -8,13 +8,13 @@ import java.util.Map; import java.util.function.UnaryOperator; /** - *

    用于在{@link SynthesizedAnnotationAggregator}中表示一个处于合成状态的注解对象。
    + *

    用于在{@link SynthesizedAggregateAnnotation}中表示一个处于合成状态的注解对象。
    * 当对多个合成注解排序时,默认使用{@link #DEFAULT_CHILD_PRIORITY_COMPARATOR}进行排序, * 从保证合成注解按{@link #getVerticalDistance()}与{@link #getHorizontalDistance()}的返回值保持有序, * 从而使得距离根元素更接近的注解对象在被处理是具有更高的优先级。 * * @author huangchengxing - * @see SynthesizedAnnotationAggregator + * @see SynthesizedAggregateAnnotation */ public interface SynthesizedAnnotation extends Annotation, Comparable { @@ -32,7 +32,7 @@ public interface SynthesizedAnnotation extends Annotation, Comparable被合成注解后置处理器,用于在{@link SynthesizedAnnotationAggregator}加载完所有待合成注解后, + *

    被合成注解后置处理器,用于在{@link SynthesizedAggregateAnnotation}加载完所有待合成注解后, * 再对加载好的{@link SynthesizedAnnotation}进行后置处理。
    * 当多个{@link SynthesizedAnnotationPostProcessor}需要一起执行时,将按照{@link #order()}的返回值进行排序, * 该值更小的处理器将被优先执行。 @@ -25,6 +25,21 @@ import java.util.Comparator; */ public interface SynthesizedAnnotationPostProcessor extends Comparable { + /** + * 属性上带有{@link Alias}的注解对象的后置处理器 + */ + AliasAnnotationPostProcessor ALIAS_ANNOTATION_POST_PROCESSOR = new AliasAnnotationPostProcessor(); + + /** + * 属性上带有{@link Link},且与其他注解的属性存在镜像关系的注解对象的后置处理器 + */ + MirrorLinkAnnotationPostProcessor MIRROR_LINK_ANNOTATION_POST_PROCESSOR = new MirrorLinkAnnotationPostProcessor(); + + /** + * 属性上带有{@link Link},且与其他注解的属性存在别名关系的注解对象的后置处理器 + */ + AliasLinkAnnotationPostProcessor ALIAS_LINK_ANNOTATION_POST_PROCESSOR = new AliasLinkAnnotationPostProcessor(); + /** * 在一组后置处理器中被调用的顺序,越小越靠前 * @@ -51,6 +66,6 @@ public interface SynthesizedAnnotationPostProcessor extends Comparable - * 该接口用于在{@link SynthesizedAnnotationAggregator}中用于从一批相同的注解对象中筛选最终用于合成注解对象。 + * 该接口用于在{@link SynthesizedAggregateAnnotation}中用于从一批相同的注解对象中筛选最终用于合成注解对象。 * * @author huangchengxing */ diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedMetaAnnotationAggregator.java b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedMetaAggregateAnnotation.java similarity index 89% rename from hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedMetaAnnotationAggregator.java rename to hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedMetaAggregateAnnotation.java index d6d96044f..4c9106c46 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedMetaAnnotationAggregator.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedMetaAggregateAnnotation.java @@ -10,10 +10,10 @@ import java.lang.reflect.AnnotatedElement; import java.util.*; /** - * {@link SynthesizedAnnotationAggregator}的基本实现,表示一个根注解与根注解上的多层元注解的聚合状态 + * {@link SynthesizedAggregateAnnotation}的基本实现,表示一个根注解与根注解上的多层元注解的聚合得到的注解 * *

    假设现有注解A,A上存在元注解B,B上存在元注解C,则对注解A进行解析, - * 将得到包含根注解A,以及其元注解B、C在内的合成元注解聚合{@link SynthesizedMetaAnnotationAggregator}。 + * 将得到包含根注解A,以及其元注解B、C在内的合成元注解聚合{@link SynthesizedMetaAggregateAnnotation}。 * 从{@link AnnotatedElement}的角度来说,得到的合成注解是一个同时承载有ABC三个注解对象的被注解元素, * 因此通过调用{@link AnnotatedElement}的相关方法将返回对应符合语义的注解对象。 * @@ -34,7 +34,7 @@ import java.util.*; *

* 若用户需要自行扩展,则需要保证上述三个处理器被正确注入当前实例。 * - *

{@link SynthesizedMetaAnnotationAggregator}支持通过{@link #getAttribute(String, Class)}, + *

{@link SynthesizedMetaAggregateAnnotation}支持通过{@link #getAttribute(String, Class)}, * 或通过{@link #synthesize(Class)}获得注解代理对象后获取指定类型的注解属性值, * 返回的属性值将根据合成注解中对应原始注解属性上的{@link Alias}与{@link Link}注解而有所变化。 * 通过当前实例获取属性值时,将经过{@link SynthesizedAnnotationAttributeProcessor}的处理。
@@ -45,7 +45,7 @@ import java.util.*; * @see AnnotationUtil * @see SynthesizedAnnotationSelector */ -public class SynthesizedMetaAnnotationAggregator implements SynthesizedAnnotationAggregator { +public class SynthesizedMetaAggregateAnnotation implements SynthesizedAggregateAnnotation { /** * 根注解,即当前查找的注解 @@ -79,14 +79,14 @@ public class SynthesizedMetaAnnotationAggregator implements SynthesizedAnnotatio * * @param source 源注解 */ - public SynthesizedMetaAnnotationAggregator(Annotation source) { + public SynthesizedMetaAggregateAnnotation(Annotation source) { this( source, SynthesizedAnnotationSelector.NEAREST_AND_OLDEST_PRIORITY, new CacheableSynthesizedAnnotationAttributeProcessor(), Arrays.asList( - new AliasAnnotationPostProcessor(), - new MirrorLinkAnnotationPostProcessor(), - new AliasLinkAnnotationPostProcessor() + SynthesizedAnnotationPostProcessor.ALIAS_ANNOTATION_POST_PROCESSOR, + SynthesizedAnnotationPostProcessor.MIRROR_LINK_ANNOTATION_POST_PROCESSOR, + SynthesizedAnnotationPostProcessor.ALIAS_LINK_ANNOTATION_POST_PROCESSOR ) ); } @@ -98,7 +98,7 @@ public class SynthesizedMetaAnnotationAggregator implements SynthesizedAnnotatio * @param annotationSelector 合成注解选择器 * @param attributeProcessor 注解属性处理器 */ - public SynthesizedMetaAnnotationAggregator( + public SynthesizedMetaAggregateAnnotation( Annotation annotation, SynthesizedAnnotationSelector annotationSelector, SynthesizedAnnotationAttributeProcessor attributeProcessor, @@ -240,23 +240,13 @@ public class SynthesizedMetaAnnotationAggregator implements SynthesizedAnnotatio * * @param annotationType 注解类型 * @return 合成注解对象 - * @see SyntheticAnnotationProxy#create(Class, SynthesizedAnnotationAggregator) + * @see SyntheticAnnotationProxy#create(Class, SynthesizedAggregateAnnotation) */ @Override public T synthesize(Class annotationType) { return SyntheticAnnotationProxy.create(annotationType, this); } - /** - * 获取根注解直接声明的注解,该方法正常情况下当只返回原注解 - * - * @return 直接声明注解 - */ - @Override - public Annotation[] getDeclaredAnnotations() { - return new Annotation[]{getSource()}; - } - /** * 广度优先遍历并缓存该根注解上的全部元注解 */ @@ -294,7 +284,7 @@ public class SynthesizedMetaAnnotationAggregator implements SynthesizedAnnotatio * @param verticalDistance 距离根对象的水平距离 * @param horizontalDistance 距离根对象的垂直距离 */ - protected MetaAnnotation(SynthesizedAnnotationAggregator owner, Annotation root, Annotation annotation, int verticalDistance, int horizontalDistance) { + protected MetaAnnotation(SynthesizedAggregateAnnotation owner, Annotation root, Annotation annotation, int verticalDistance, int horizontalDistance) { super(owner, root, annotation, verticalDistance, horizontalDistance); } 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 f33557d7f..f107c290e 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 @@ -38,7 +38,7 @@ class SyntheticAnnotationProxy implements InvocationHandler { /** * 创建一个代理注解,生成的代理对象将是{@link SyntheticProxyAnnotation}与指定的注解类的子类。 *

    - *
  • 当作为{@code annotationType}所指定的类型使用时,其属性将通过合成它的{@link SynthesizedAnnotationAggregator}获取;
  • + *
  • 当作为{@code annotationType}所指定的类型使用时,其属性将通过合成它的{@link SynthesizedAggregateAnnotation}获取;
  • *
  • 当作为{@link SyntheticProxyAnnotation}或{@link SynthesizedAnnotation}使用时,将可以获得原始注解实例的相关信息;
  • *
* @@ -48,7 +48,7 @@ class SyntheticAnnotationProxy implements InvocationHandler { */ @SuppressWarnings("unchecked") static T create( - Class annotationType, SynthesizedAnnotationAggregator synthesizedAnnotationAggregator) { + Class annotationType, SynthesizedAggregateAnnotation synthesizedAnnotationAggregator) { final SynthesizedAnnotation annotation = synthesizedAnnotationAggregator.getSynthesizedAnnotation(annotationType); if (ObjectUtil.isNull(annotation)) { return null; @@ -93,13 +93,13 @@ class SyntheticAnnotationProxy implements InvocationHandler { 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()) { + for (final Method declaredMethod : ClassUtil.getDeclaredMethods(annotation.getAnnotation().annotationType())) { methods.put(declaredMethod.getName(), (method, args) -> proxyAttributeValue(method)); } } private String proxyToString() { - final String attributes = Stream.of(annotation.annotationType().getDeclaredMethods()) + final String attributes = Stream.of(ClassUtil.getDeclaredMethods(annotation.getAnnotation().annotationType())) .filter(AnnotationUtil::isAttributeMethod) .map(method -> StrUtil.format("{}={}", method.getName(), annotation.getOwner().getAttribute(method.getName(), method.getReturnType()))) .collect(Collectors.joining(", ")); @@ -134,7 +134,7 @@ class SyntheticAnnotationProxy implements InvocationHandler { * * @return 合成注解 */ - SynthesizedAnnotationAggregator getSynthesizedAnnotationAggregator(); + SynthesizedAggregateAnnotation getSynthesizedAnnotationAggregator(); /** * 获取该代理注解对应的已合成注解 diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/MethodAnnotationScanner.java b/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/MethodAnnotationScanner.java index ac054f88f..18be3ea1b 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/MethodAnnotationScanner.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/MethodAnnotationScanner.java @@ -80,7 +80,7 @@ public class MethodAnnotationScanner extends AbstractTypeAnnotationScanner targetClass) { final Method sourceMethod = (Method) source; - return Stream.of(targetClass.getDeclaredMethods()) + return Stream.of(ClassUtil.getDeclaredMethods(targetClass)) .filter(superMethod -> !superMethod.isBridge()) .filter(superMethod -> hasSameSignature(sourceMethod, superMethod)) .map(AnnotatedElement::getAnnotations) diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/AliasAnnotationPostProcessorTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/AliasAnnotationPostProcessorTest.java index 8c792a1c9..db9558fd4 100644 --- a/hutool-core/src/test/java/cn/hutool/core/annotation/AliasAnnotationPostProcessorTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/AliasAnnotationPostProcessorTest.java @@ -19,7 +19,7 @@ public class AliasAnnotationPostProcessorTest { AliasAnnotationPostProcessor processor = new AliasAnnotationPostProcessor(); Map, SynthesizedAnnotation> annotationMap = new HashMap<>(); - SynthesizedAnnotationAggregator synthesizedAnnotationAggregator = new TestSynthesizedAnnotationAggregator(annotationMap); + SynthesizedAggregateAnnotation synthesizedAnnotationAggregator = new TestSynthesizedAggregateAnnotation(annotationMap); AnnotationForTest annotation = ClassForTest.class.getAnnotation(AnnotationForTest.class); SynthesizedAnnotation synthesizedAnnotation = new TestSynthesizedAnnotation(synthesizedAnnotationAggregator, annotation); annotationMap.put(annotation.annotationType(), synthesizedAnnotation); @@ -49,11 +49,11 @@ public class AliasAnnotationPostProcessorTest { String name() default ""; } - static class TestSynthesizedAnnotationAggregator implements SynthesizedAnnotationAggregator { + static class TestSynthesizedAggregateAnnotation implements SynthesizedAggregateAnnotation { private final Map, SynthesizedAnnotation> annotationMap; - public TestSynthesizedAnnotationAggregator(Map, SynthesizedAnnotation> annotationMap) { + public TestSynthesizedAggregateAnnotation(Map, SynthesizedAnnotation> annotationMap) { this.annotationMap = annotationMap; } @@ -106,20 +106,15 @@ public class AliasAnnotationPostProcessorTest { public Object getAttribute(String attributeName, Class attributeType) { return null; } - - @Override - public Annotation[] getDeclaredAnnotations() { - return new Annotation[0]; - } } static class TestSynthesizedAnnotation implements SynthesizedAnnotation { private final Annotation annotation; - private final SynthesizedAnnotationAggregator owner; + private final SynthesizedAggregateAnnotation owner; private final Map attributeMap; - public TestSynthesizedAnnotation(SynthesizedAnnotationAggregator owner, Annotation annotation) { + public TestSynthesizedAnnotation(SynthesizedAggregateAnnotation owner, Annotation annotation) { this.owner = owner; this.attributeMap = new HashMap<>(); this.annotation = annotation; @@ -129,7 +124,7 @@ public class AliasAnnotationPostProcessorTest { } @Override - public SynthesizedAnnotationAggregator getOwner() { + public SynthesizedAggregateAnnotation getOwner() { return owner; } diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/AliasLinkAnnotationPostProcessorTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/AliasLinkAnnotationPostProcessorTest.java index 339b012d5..1ca645d53 100644 --- a/hutool-core/src/test/java/cn/hutool/core/annotation/AliasLinkAnnotationPostProcessorTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/AliasLinkAnnotationPostProcessorTest.java @@ -19,7 +19,7 @@ public class AliasLinkAnnotationPostProcessorTest { AliasLinkAnnotationPostProcessor processor = new AliasLinkAnnotationPostProcessor(); Map, SynthesizedAnnotation> annotationMap = new HashMap<>(); - SynthesizedAnnotationAggregator synthesizedAnnotationAggregator = new TestSynthesizedAnnotationAggregator(annotationMap); + SynthesizedAggregateAnnotation synthesizedAnnotationAggregator = new TestSynthesizedAggregateAnnotation(annotationMap); AnnotationForTest annotation = ClassForTest.class.getAnnotation(AnnotationForTest.class); SynthesizedAnnotation synthesizedAnnotation = new TestSynthesizedAnnotation(synthesizedAnnotationAggregator, annotation); annotationMap.put(annotation.annotationType(), synthesizedAnnotation); @@ -43,7 +43,7 @@ public class AliasLinkAnnotationPostProcessorTest { AliasLinkAnnotationPostProcessor processor = new AliasLinkAnnotationPostProcessor(); Map, SynthesizedAnnotation> annotationMap = new HashMap<>(); - SynthesizedAnnotationAggregator synthesizedAnnotationAggregator = new TestSynthesizedAnnotationAggregator(annotationMap); + SynthesizedAggregateAnnotation synthesizedAnnotationAggregator = new TestSynthesizedAggregateAnnotation(annotationMap); AnnotationForTest annotation = ClassForTest.class.getAnnotation(AnnotationForTest.class); SynthesizedAnnotation synthesizedAnnotation = new TestSynthesizedAnnotation(synthesizedAnnotationAggregator, annotation); annotationMap.put(annotation.annotationType(), synthesizedAnnotation); @@ -77,11 +77,11 @@ public class AliasLinkAnnotationPostProcessorTest { String name2() default ""; } - static class TestSynthesizedAnnotationAggregator implements SynthesizedAnnotationAggregator { + static class TestSynthesizedAggregateAnnotation implements SynthesizedAggregateAnnotation { private final Map, SynthesizedAnnotation> annotationMap; - public TestSynthesizedAnnotationAggregator(Map, SynthesizedAnnotation> annotationMap) { + public TestSynthesizedAggregateAnnotation(Map, SynthesizedAnnotation> annotationMap) { this.annotationMap = annotationMap; } @@ -134,20 +134,15 @@ public class AliasLinkAnnotationPostProcessorTest { public Object getAttribute(String attributeName, Class attributeType) { return null; } - - @Override - public Annotation[] getDeclaredAnnotations() { - return new Annotation[0]; - } } static class TestSynthesizedAnnotation implements SynthesizedAnnotation { private final Annotation annotation; - private final SynthesizedAnnotationAggregator owner; + private final SynthesizedAggregateAnnotation owner; private final Map attributeMap; - public TestSynthesizedAnnotation(SynthesizedAnnotationAggregator owner, Annotation annotation) { + public TestSynthesizedAnnotation(SynthesizedAggregateAnnotation owner, Annotation annotation) { this.owner = owner; this.attributeMap = new HashMap<>(); this.annotation = annotation; @@ -157,7 +152,7 @@ public class AliasLinkAnnotationPostProcessorTest { } @Override - public SynthesizedAnnotationAggregator getOwner() { + public SynthesizedAggregateAnnotation getOwner() { return owner; } diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/CacheableSynthesizedAnnotationAttributeProcessorTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/CacheableSynthesizedAnnotationAttributeProcessorTest.java index c4db00adf..3a9717134 100644 --- a/hutool-core/src/test/java/cn/hutool/core/annotation/CacheableSynthesizedAnnotationAttributeProcessorTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/CacheableSynthesizedAnnotationAttributeProcessorTest.java @@ -39,7 +39,7 @@ public class CacheableSynthesizedAnnotationAttributeProcessorTest { } @Override - public SynthesizedAnnotationAggregator getOwner() { + public SynthesizedAggregateAnnotation getOwner() { return null; } diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessorTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessorTest.java index 2c741e32d..5f18f9574 100644 --- a/hutool-core/src/test/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessorTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessorTest.java @@ -19,7 +19,7 @@ public class MirrorLinkAnnotationPostProcessorTest { MirrorLinkAnnotationPostProcessor processor = new MirrorLinkAnnotationPostProcessor(); Map, SynthesizedAnnotation> annotationMap = new HashMap<>(); - SynthesizedAnnotationAggregator synthesizedAnnotationAggregator = new TestSynthesizedAnnotationAggregator(annotationMap); + SynthesizedAggregateAnnotation synthesizedAnnotationAggregator = new TestSynthesizedAggregateAnnotation(annotationMap); AnnotationForTest annotation = ClassForTest.class.getAnnotation(AnnotationForTest.class); SynthesizedAnnotation synthesizedAnnotation = new TestSynthesizedAnnotation(synthesizedAnnotationAggregator, annotation); annotationMap.put(annotation.annotationType(), synthesizedAnnotation); @@ -51,11 +51,11 @@ public class MirrorLinkAnnotationPostProcessorTest { String name() default ""; } - static class TestSynthesizedAnnotationAggregator implements SynthesizedAnnotationAggregator { + static class TestSynthesizedAggregateAnnotation implements SynthesizedAggregateAnnotation { private final Map, SynthesizedAnnotation> annotationMap; - public TestSynthesizedAnnotationAggregator(Map, SynthesizedAnnotation> annotationMap) { + public TestSynthesizedAggregateAnnotation(Map, SynthesizedAnnotation> annotationMap) { this.annotationMap = annotationMap; } @@ -109,19 +109,15 @@ public class MirrorLinkAnnotationPostProcessorTest { return null; } - @Override - public Annotation[] getDeclaredAnnotations() { - return new Annotation[0]; - } } static class TestSynthesizedAnnotation implements SynthesizedAnnotation { private final Annotation annotation; - private final SynthesizedAnnotationAggregator owner; + private final SynthesizedAggregateAnnotation owner; private final Map attributeMap; - public TestSynthesizedAnnotation(SynthesizedAnnotationAggregator owner, Annotation annotation) { + public TestSynthesizedAnnotation(SynthesizedAggregateAnnotation owner, Annotation annotation) { this.owner = owner; this.attributeMap = new HashMap<>(); this.annotation = annotation; @@ -131,7 +127,7 @@ public class MirrorLinkAnnotationPostProcessorTest { } @Override - public SynthesizedAnnotationAggregator getOwner() { + public SynthesizedAggregateAnnotation getOwner() { return owner; } diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/SynthesizedAnnotationSelectorTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/SynthesizedAnnotationSelectorTest.java index cadb3d6f9..0436d5c4c 100644 --- a/hutool-core/src/test/java/cn/hutool/core/annotation/SynthesizedAnnotationSelectorTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/SynthesizedAnnotationSelectorTest.java @@ -88,7 +88,7 @@ public class SynthesizedAnnotationSelectorTest { } @Override - public SynthesizedAnnotationAggregator getOwner() { + public SynthesizedAggregateAnnotation getOwner() { return null; } 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 b34f2d4dc..a0a279641 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 @@ -4,12 +4,15 @@ import cn.hutool.core.util.ReflectUtil; import org.junit.Assert; import org.junit.Test; -import java.lang.annotation.*; +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; import java.util.Arrays; /** - * 合成注解{@link SynthesizedMetaAnnotationAggregator}的测试用例 + * 合成注解{@link SynthesizedMetaAggregateAnnotation}的测试用例 * * @author huangchengxing */ @@ -22,10 +25,10 @@ public class SyntheticMetaAnnotationTest { final GrandParentAnnotation grandParentAnnotation = ChildAnnotation.class.getAnnotation(GrandParentAnnotation.class); final ParentAnnotation parentAnnotation = ChildAnnotation.class.getAnnotation(ParentAnnotation.class); final ChildAnnotation childAnnotation = AnnotatedClass.class.getAnnotation(ChildAnnotation.class); - final SynthesizedMetaAnnotationAggregator syntheticMetaAnnotation = new SynthesizedMetaAnnotationAggregator(childAnnotation); + final SynthesizedMetaAggregateAnnotation syntheticMetaAnnotation = new SynthesizedMetaAggregateAnnotation(childAnnotation); // Annotation & AnnotatedElement - Assert.assertEquals(SynthesizedMetaAnnotationAggregator.class, syntheticMetaAnnotation.annotationType()); + Assert.assertEquals(SynthesizedMetaAggregateAnnotation.class, syntheticMetaAnnotation.annotationType()); Assert.assertTrue(syntheticMetaAnnotation.isAnnotationPresent(GrandParentAnnotation.class)); Assert.assertTrue(syntheticMetaAnnotation.isAnnotationPresent(ParentAnnotation.class)); Assert.assertTrue(syntheticMetaAnnotation.isAnnotationPresent(ChildAnnotation.class)); @@ -36,7 +39,6 @@ public class SyntheticMetaAnnotationTest { Arrays.asList(childAnnotation, grandParentAnnotation, parentAnnotation), Arrays.asList(syntheticMetaAnnotation.getAnnotations()) ); - Assert.assertArrayEquals(new Annotation[]{ childAnnotation }, syntheticMetaAnnotation.getDeclaredAnnotations()); // 扩展方法 Assert.assertNotNull(syntheticMetaAnnotation.getSynthesizedAnnotation(GrandParentAnnotation.class)); @@ -53,11 +55,9 @@ public class SyntheticMetaAnnotationTest { @Test public void synthesisAnnotationAttributeTest() { final ChildAnnotation rootAnnotation = AnnotatedClass.class.getAnnotation(ChildAnnotation.class); - SynthesizedMetaAnnotationAggregator syntheticMetaAnnotation = new SynthesizedMetaAnnotationAggregator(rootAnnotation); + SynthesizedMetaAggregateAnnotation syntheticMetaAnnotation = new SynthesizedMetaAggregateAnnotation(rootAnnotation); Assert.assertEquals(syntheticMetaAnnotation.getSource(), rootAnnotation); - Assert.assertEquals(syntheticMetaAnnotation.annotationType(), SynthesizedMetaAnnotationAggregator.class); - Assert.assertEquals(1, syntheticMetaAnnotation.getDeclaredAnnotations().length); - Assert.assertEquals(syntheticMetaAnnotation.getDeclaredAnnotations()[0], rootAnnotation); + Assert.assertEquals(syntheticMetaAnnotation.annotationType(), SynthesizedMetaAggregateAnnotation.class); Assert.assertEquals(3, syntheticMetaAnnotation.getAnnotations().length); Assert.assertEquals("Child!", syntheticMetaAnnotation.getAttribute("childValue", String.class)); @@ -69,7 +69,7 @@ public class SyntheticMetaAnnotationTest { @Test public void syntheticAnnotationTest() { final ChildAnnotation rootAnnotation = AnnotatedClass.class.getAnnotation(ChildAnnotation.class); - SynthesizedMetaAnnotationAggregator syntheticMetaAnnotation = new SynthesizedMetaAnnotationAggregator(rootAnnotation); + SynthesizedMetaAggregateAnnotation syntheticMetaAnnotation = new SynthesizedMetaAggregateAnnotation(rootAnnotation); final ChildAnnotation childAnnotation = syntheticMetaAnnotation.synthesize(ChildAnnotation.class); SynthesizedAnnotation childSyntheticAnnotation = syntheticMetaAnnotation.getSynthesizedAnnotation(ChildAnnotation.class); @@ -82,7 +82,7 @@ public class SyntheticMetaAnnotationTest { Assert.assertEquals("Child!", childAnnotation.childValue()); Assert.assertEquals("Child!", childAnnotation.childValueAlias()); Assert.assertEquals(childAnnotation.grandParentType(), Integer.class); - Assert.assertThrows(IllegalArgumentException.class, () -> new SynthesizedMetaAnnotationAggregator(childAnnotation)); + Assert.assertThrows(IllegalArgumentException.class, () -> new SynthesizedMetaAggregateAnnotation(childAnnotation)); final ParentAnnotation parentAnnotation = syntheticMetaAnnotation.synthesize(ParentAnnotation.class); SynthesizedAnnotation parentSyntheticAnnotation = syntheticMetaAnnotation.getSynthesizedAnnotation(ParentAnnotation.class); @@ -93,7 +93,7 @@ public class SyntheticMetaAnnotationTest { Assert.assertNotNull(parentAnnotation); Assert.assertEquals("Child's Parent!", parentAnnotation.parentValue()); Assert.assertEquals("java.lang.Void", parentAnnotation.grandParentType()); - Assert.assertThrows(IllegalArgumentException.class, () -> new SynthesizedMetaAnnotationAggregator(parentAnnotation)); + Assert.assertThrows(IllegalArgumentException.class, () -> new SynthesizedMetaAggregateAnnotation(parentAnnotation)); final GrandParentAnnotation grandParentAnnotation = syntheticMetaAnnotation.synthesize(GrandParentAnnotation.class); SynthesizedAnnotation grandParentSyntheticAnnotation = syntheticMetaAnnotation.getSynthesizedAnnotation(GrandParentAnnotation.class); @@ -105,13 +105,13 @@ public class SyntheticMetaAnnotationTest { Assert.assertNotNull(grandParentAnnotation); Assert.assertEquals("Child's GrandParent!", grandParentAnnotation.grandParentValue()); Assert.assertEquals(grandParentAnnotation.grandParentType(), Integer.class); - Assert.assertThrows(IllegalArgumentException.class, () -> new SynthesizedMetaAnnotationAggregator(grandParentAnnotation)); + Assert.assertThrows(IllegalArgumentException.class, () -> new SynthesizedMetaAggregateAnnotation(grandParentAnnotation)); } @Test public void linkTest() { final Method method = ReflectUtil.getMethod(AnnotationForLinkTest.class, "value"); - final SynthesizedAnnotationAggregator synthesizedAnnotationAggregator = new SynthesizedMetaAnnotationAggregator(method.getAnnotation(AliasFor.class)); + final SynthesizedAggregateAnnotation synthesizedAnnotationAggregator = new SynthesizedMetaAggregateAnnotation(method.getAnnotation(AliasFor.class)); final Link link = synthesizedAnnotationAggregator.synthesize(Link.class); Assert.assertEquals(AnnotationForLinkTest.class, link.annotation()); Assert.assertEquals("name", link.attribute()); @@ -120,19 +120,19 @@ public class SyntheticMetaAnnotationTest { @Test public void mirrorAttributeTest() { AnnotationForMirrorTest annotation = ClassForMirrorTest.class.getAnnotation(AnnotationForMirrorTest.class); - SynthesizedAnnotationAggregator synthetic = new SynthesizedMetaAnnotationAggregator(annotation); + SynthesizedAggregateAnnotation synthetic = new SynthesizedMetaAggregateAnnotation(annotation); AnnotationForMirrorTest syntheticAnnotation = synthetic.synthesize(AnnotationForMirrorTest.class); Assert.assertEquals("Foo", syntheticAnnotation.name()); Assert.assertEquals("Foo", syntheticAnnotation.value()); annotation = ClassForMirrorTest2.class.getAnnotation(AnnotationForMirrorTest.class); - synthetic = new SynthesizedMetaAnnotationAggregator(annotation); + synthetic = new SynthesizedMetaAggregateAnnotation(annotation); syntheticAnnotation = synthetic.synthesize(AnnotationForMirrorTest.class); Assert.assertEquals("Foo", syntheticAnnotation.name()); Assert.assertEquals("Foo", syntheticAnnotation.value()); annotation = ClassForMirrorTest3.class.getAnnotation(AnnotationForMirrorTest.class); - synthetic = new SynthesizedMetaAnnotationAggregator(annotation); + synthetic = new SynthesizedMetaAggregateAnnotation(annotation); syntheticAnnotation = synthetic.synthesize(AnnotationForMirrorTest.class); AnnotationForMirrorTest finalSyntheticAnnotation = syntheticAnnotation; Assert.assertThrows(IllegalArgumentException.class, finalSyntheticAnnotation::name); @@ -141,14 +141,14 @@ public class SyntheticMetaAnnotationTest { @Test public void aliasForTest() { AnnotationForAliasForTest annotation = ClassForAliasForTest.class.getAnnotation(AnnotationForAliasForTest.class); - SynthesizedAnnotationAggregator synthetic = new SynthesizedMetaAnnotationAggregator(annotation); + SynthesizedAggregateAnnotation synthetic = new SynthesizedMetaAggregateAnnotation(annotation); MetaAnnotationForAliasForTest metaAnnotation = synthetic.synthesize(MetaAnnotationForAliasForTest.class); Assert.assertEquals("Meta", metaAnnotation.name()); AnnotationForAliasForTest childAnnotation = synthetic.synthesize(AnnotationForAliasForTest.class); Assert.assertEquals("", childAnnotation.value()); annotation = ClassForAliasForTest2.class.getAnnotation(AnnotationForAliasForTest.class); - synthetic = new SynthesizedMetaAnnotationAggregator(annotation); + synthetic = new SynthesizedMetaAggregateAnnotation(annotation); metaAnnotation = synthetic.synthesize(MetaAnnotationForAliasForTest.class); Assert.assertEquals("Foo", metaAnnotation.name()); childAnnotation = synthetic.synthesize(AnnotationForAliasForTest.class); @@ -158,14 +158,14 @@ public class SyntheticMetaAnnotationTest { @Test public void forceAliasForTest() { AnnotationForceForAliasForTest annotation = ClassForForceAliasForTest.class.getAnnotation(AnnotationForceForAliasForTest.class); - SynthesizedAnnotationAggregator synthetic = new SynthesizedMetaAnnotationAggregator(annotation); + SynthesizedAggregateAnnotation synthetic = new SynthesizedMetaAggregateAnnotation(annotation); MetaAnnotationForForceAliasForTest metaAnnotation = synthetic.synthesize(MetaAnnotationForForceAliasForTest.class); Assert.assertEquals("", metaAnnotation.name()); AnnotationForceForAliasForTest childAnnotation = synthetic.synthesize(AnnotationForceForAliasForTest.class); Assert.assertEquals("", childAnnotation.value()); annotation = ClassForForceAliasForTest2.class.getAnnotation(AnnotationForceForAliasForTest.class); - synthetic = new SynthesizedMetaAnnotationAggregator(annotation); + synthetic = new SynthesizedMetaAggregateAnnotation(annotation); metaAnnotation = synthetic.synthesize(MetaAnnotationForForceAliasForTest.class); Assert.assertEquals("Foo", metaAnnotation.name()); childAnnotation = synthetic.synthesize(AnnotationForceForAliasForTest.class); @@ -175,7 +175,7 @@ public class SyntheticMetaAnnotationTest { @Test public void aliasForAndMirrorTest() { AnnotationForMirrorThenAliasForTest annotation = ClassForAliasForAndMirrorTest.class.getAnnotation(AnnotationForMirrorThenAliasForTest.class); - SynthesizedAnnotationAggregator synthetic = new SynthesizedMetaAnnotationAggregator(annotation); + SynthesizedAggregateAnnotation synthetic = new SynthesizedMetaAggregateAnnotation(annotation); MetaAnnotationForMirrorThenAliasForTest metaAnnotation = synthetic.synthesize(MetaAnnotationForMirrorThenAliasForTest.class); Assert.assertEquals("test", metaAnnotation.name()); Assert.assertEquals("test", metaAnnotation.value()); @@ -186,7 +186,7 @@ public class SyntheticMetaAnnotationTest { @Test public void multiAliasForTest() { final AnnotationForMultiAliasForTest annotation = ClassForMultiAliasForTest.class.getAnnotation(AnnotationForMultiAliasForTest.class); - final SynthesizedAnnotationAggregator synthetic = new SynthesizedMetaAnnotationAggregator(annotation); + final SynthesizedAggregateAnnotation synthetic = new SynthesizedMetaAggregateAnnotation(annotation); final MetaAnnotationForMultiAliasForTest1 metaAnnotation1 = synthetic.synthesize(MetaAnnotationForMultiAliasForTest1.class); Assert.assertEquals("test", metaAnnotation1.name()); @@ -200,7 +200,7 @@ public class SyntheticMetaAnnotationTest { @Test public void implicitAliasTest() { final AnnotationForImplicitAliasTest annotation = ClassForImplicitAliasTest.class.getAnnotation(AnnotationForImplicitAliasTest.class); - final SynthesizedAnnotationAggregator synthetic = new SynthesizedMetaAnnotationAggregator(annotation); + final SynthesizedAggregateAnnotation synthetic = new SynthesizedMetaAggregateAnnotation(annotation); final MetaAnnotationForImplicitAliasTest metaAnnotation = synthetic.synthesize(MetaAnnotationForImplicitAliasTest.class); Assert.assertEquals("Meta", metaAnnotation.name()); From cf08a92f3454d8cf33c28c7ac037564cffc51703 Mon Sep 17 00:00:00 2001 From: huangchengxing <841396397@qq.com> Date: Thu, 14 Jul 2022 16:16:14 +0800 Subject: [PATCH 11/15] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=B1=82=E7=BA=A7?= =?UTF-8?q?=E5=AF=B9=E8=B1=A1=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...nthesizedAnnotationAttributeProcessor.java | 6 +- .../hutool/core/annotation/Hierarchical.java | 155 ++++++++++++++++++ .../SynthesizedAggregateAnnotation.java | 28 +++- .../annotation/SynthesizedAnnotation.java | 32 +--- .../SynthesizedAnnotationSelector.java | 8 +- .../SynthesizedMetaAggregateAnnotation.java | 80 ++++++++- .../AliasAnnotationPostProcessorTest.java | 5 + .../AliasLinkAnnotationPostProcessorTest.java | 5 + ...MirrorLinkAnnotationPostProcessorTest.java | 5 + 9 files changed, 284 insertions(+), 40 deletions(-) create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/Hierarchical.java 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 e4596446b..ab7ae9ad6 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 @@ -22,14 +22,14 @@ import java.util.Comparator; public class CacheableSynthesizedAnnotationAttributeProcessor implements SynthesizedAnnotationAttributeProcessor { private final Table, Object> valueCaches = new RowKeyTable<>(); - private final Comparator annotationComparator; + private final Comparator annotationComparator; /** * 创建一个带缓存的注解值选择器 * * @param annotationComparator 注解比较器,排序更靠前的注解将被优先用于获取值 */ - public CacheableSynthesizedAnnotationAttributeProcessor(Comparator annotationComparator) { + public CacheableSynthesizedAnnotationAttributeProcessor(Comparator annotationComparator) { Assert.notNull(annotationComparator, "annotationComparator must not null"); this.annotationComparator = annotationComparator; } @@ -40,7 +40,7 @@ public class CacheableSynthesizedAnnotationAttributeProcessor implements Synthes * 越靠前的越优先被取值。 */ public CacheableSynthesizedAnnotationAttributeProcessor() { - this(SynthesizedAnnotation.DEFAULT_CHILD_PRIORITY_COMPARATOR); + this(Hierarchical.DEFAULT_HIERARCHICAL_COMPARATOR); } @SuppressWarnings("unchecked") diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/Hierarchical.java b/hutool-core/src/main/java/cn/hutool/core/annotation/Hierarchical.java new file mode 100644 index 000000000..00c993822 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/Hierarchical.java @@ -0,0 +1,155 @@ +package cn.hutool.core.annotation; + + +import java.util.Comparator; + +/** + *

描述以一个参照物为对象,存在于该参照物的层级结构中的对象。 + * + *

该对象可通过{@link #getVerticalDistance()}与{@link #getHorizontalDistance()} + * 描述其在以参照物为基点的坐标坐标系中的位置。
+ * 在需要对该接口的实现类进行按优先级排序时,距离{@link #getRoot()}对象越近,则该实现类的优先级越高。 + * 默认提供了{@link #DEFAULT_HIERARCHICAL_COMPARATOR}用于实现该比较规则。
+ * 一般情况下,{@link #getRoot()}返回值相同的对象之间的比较才有意义。 + * + *

此外,还提供了{@link Selector}接口用于根据一定的规则从两个{@link Hierarchical}实现类中选择并返回一个最合适的对象, + * 默认提供了四个实现类: + *

    + *
  • {@link Selector#NEAREST_AND_OLDEST_PRIORITY}: 返回距离根对象更近的对象,当距离一样时优先返回旧对象;
  • + *
  • {@link Selector#NEAREST_AND_NEWEST_PRIORITY}: 返回距离根对象更近的对象,当距离一样时优先返回新对象;
  • + *
  • {@link Selector#FARTHEST_AND_OLDEST_PRIORITY}: 返回距离根对象更远的对象,当距离一样时优先返回旧对象;
  • + *
  • {@link Selector#FARTHEST_AND_NEWEST_PRIORITY}: 返回距离根对象更远的对象,当距离一样时优先返回新对象;
  • + *
+ * + * @author huangchengxing + */ +public interface Hierarchical extends Comparable { + + // ====================== compare ====================== + + /** + * 默认{@link #getHorizontalDistance()}与{@link #getVerticalDistance()}排序的比较器 + */ + Comparator DEFAULT_HIERARCHICAL_COMPARATOR = Comparator + .comparing(Hierarchical::getVerticalDistance) + .thenComparing(Hierarchical::getHorizontalDistance); + + /** + * 按{@link #getVerticalDistance()}和{@link #getHorizontalDistance()}排序 + * + * @param o {@link SynthesizedAnnotation}对象 + * @return 比较值 + */ + @Override + default int compareTo(Hierarchical o) { + return DEFAULT_HIERARCHICAL_COMPARATOR.compare(this, o); + } + + // ====================== hierarchical ====================== + + /** + * 参照物,即坐标为{@code (0, 0)}的对象。 + * 当对象本身即为参照物时,该方法应当返回其本身 + * + * @return 参照物 + */ + Object getRoot(); + + /** + * 获取该对象与参照物的垂直距离。 + * 默认情况下,该距离即为当前对象与参照物之间相隔的层级数。 + * + * @return 合成注解与根对象的垂直距离 + */ + int getVerticalDistance(); + + /** + * 获取该对象与参照物的水平距离。 + * 默认情况下,该距离即为当前对象在与参照物{@link #getVerticalDistance()}相同的情况下条, + * 该对象被扫描到的顺序。 + * + * @return 合成注解与根对象的水平距离 + */ + int getHorizontalDistance(); + + // ====================== selector ====================== + + /** + * {@link Hierarchical}选择器,用于根据一定的规则从两个{@link Hierarchical}实现类中选择并返回一个最合适的对象 + */ + @FunctionalInterface + interface Selector { + + /** + * 返回距离根对象更近的对象,当距离一样时优先返回旧对象 + */ + Selector NEAREST_AND_OLDEST_PRIORITY = new NearestAndOldestPrioritySelector(); + + /** + * 返回距离根对象更近的对象,当距离一样时优先返回新对象 + */ + Selector NEAREST_AND_NEWEST_PRIORITY = new NearestAndNewestPrioritySelector(); + + /** + * 返回距离根对象更远的对象,当距离一样时优先返回旧对象 + */ + Selector FARTHEST_AND_OLDEST_PRIORITY = new FarthestAndOldestPrioritySelector(); + + /** + * 返回距离根对象更远的对象,当距离一样时优先返回新对象 + */ + Selector FARTHEST_AND_NEWEST_PRIORITY = new FarthestAndNewestPrioritySelector(); + + /** + * 比较两个被合成的对象,选择其中的一个并返回 + * + * @param 复合注解类型 + * @param prev 上一对象,该参数不允许为空 + * @param next 下一对象,该参数不允许为空 + * @return 对象 + */ + T choose(T prev, T next); + + /** + * 返回距离根对象更近的注解,当距离一样时优先返回旧注解 + */ + class NearestAndOldestPrioritySelector implements Selector { + @Override + public T choose(T oldAnnotation, T newAnnotation) { + return newAnnotation.getVerticalDistance() < oldAnnotation.getVerticalDistance() ? newAnnotation : oldAnnotation; + } + } + + /** + * 返回距离根对象更近的注解,当距离一样时优先返回新注解 + */ + class NearestAndNewestPrioritySelector implements Selector { + @Override + public T choose(T oldAnnotation, T newAnnotation) { + return newAnnotation.getVerticalDistance() <= oldAnnotation.getVerticalDistance() ? newAnnotation : oldAnnotation; + } + } + + /** + * 返回距离根对象更远的注解,当距离一样时优先返回旧注解 + */ + class FarthestAndOldestPrioritySelector implements Selector { + @Override + public T choose(T oldAnnotation, T newAnnotation) { + return newAnnotation.getVerticalDistance() > oldAnnotation.getVerticalDistance() ? newAnnotation : oldAnnotation; + } + } + + /** + * 返回距离根对象更远的注解,当距离一样时优先返回新注解 + */ + class FarthestAndNewestPrioritySelector implements Selector { + @Override + public T choose(T oldAnnotation, T newAnnotation) { + return newAnnotation.getVerticalDistance() >= oldAnnotation.getVerticalDistance() ? newAnnotation : oldAnnotation; + } + } + + } + +} diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAggregateAnnotation.java b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAggregateAnnotation.java index fe85356a1..b2d047bb6 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAggregateAnnotation.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAggregateAnnotation.java @@ -34,7 +34,33 @@ import java.util.Collection; * @see SynthesizedAnnotationPostProcessor * @see SynthesizedMetaAggregateAnnotation */ -public interface SynthesizedAggregateAnnotation extends Annotation, AggregateAnnotation { +public interface SynthesizedAggregateAnnotation extends Annotation, AggregateAnnotation, Hierarchical { + + // ================== hierarchical ================== + + /** + * 距离{@link #getRoot()}返回值的垂直距离, + * 默认聚合注解即为根对象,因此返回0 + * + * @return 距离{@link #getRoot()}返回值的水平距离, + */ + @Override + default int getVerticalDistance() { + return 0; + } + + /** + * 距离{@link #getRoot()}返回值的水平距离, + * 默认聚合注解即为根对象,因此返回0 + * + * @return 距离{@link #getRoot()}返回值的水平距离, + */ + @Override + default int getHorizontalDistance() { + return 0; + } + + // ================== synthesize ================== /** * 获取在聚合中存在的指定注解对象 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 40bd70499..b65370ba2 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 @@ -3,7 +3,6 @@ package cn.hutool.core.annotation; import cn.hutool.core.collection.CollUtil; import java.lang.annotation.Annotation; -import java.util.Comparator; import java.util.Map; import java.util.function.UnaryOperator; @@ -16,16 +15,7 @@ import java.util.function.UnaryOperator; * @author huangchengxing * @see SynthesizedAggregateAnnotation */ -public interface SynthesizedAnnotation extends Annotation, Comparable { - - /** - * {@link SynthesizedAnnotation}使用的默认的比较器, - * 按照按{@link #getVerticalDistance()}和{@link #getHorizontalDistance()}的返回值进行排序。
- * 一般情况下,排序越小的合成注解应当被优先处理。 - */ - Comparator DEFAULT_CHILD_PRIORITY_COMPARATOR = Comparator - .comparing(SynthesizedAnnotation::getVerticalDistance) - .thenComparing(SynthesizedAnnotation::getHorizontalDistance); +public interface SynthesizedAnnotation extends Annotation, Hierarchical { /** * 获取所属的合成注解聚合器 @@ -34,13 +24,6 @@ public interface SynthesizedAnnotation extends Annotation, Comparable T choose(T oldAnnotation, T newAnnotation) { - return newAnnotation.getVerticalDistance() < oldAnnotation.getVerticalDistance() ? newAnnotation : oldAnnotation; + return Hierarchical.Selector.NEAREST_AND_OLDEST_PRIORITY.choose(oldAnnotation, newAnnotation); } } @@ -55,7 +55,7 @@ public interface SynthesizedAnnotationSelector { class NearestAndNewestPrioritySelector implements SynthesizedAnnotationSelector { @Override public T choose(T oldAnnotation, T newAnnotation) { - return newAnnotation.getVerticalDistance() <= oldAnnotation.getVerticalDistance() ? newAnnotation : oldAnnotation; + return Hierarchical.Selector.NEAREST_AND_NEWEST_PRIORITY.choose(oldAnnotation, newAnnotation); } } @@ -65,7 +65,7 @@ public interface SynthesizedAnnotationSelector { class FarthestAndOldestPrioritySelector implements SynthesizedAnnotationSelector { @Override public T choose(T oldAnnotation, T newAnnotation) { - return newAnnotation.getVerticalDistance() > oldAnnotation.getVerticalDistance() ? newAnnotation : oldAnnotation; + return Hierarchical.Selector.FARTHEST_AND_OLDEST_PRIORITY.choose(oldAnnotation, newAnnotation); } } @@ -75,7 +75,7 @@ public interface SynthesizedAnnotationSelector { class FarthestAndNewestPrioritySelector implements SynthesizedAnnotationSelector { @Override public T choose(T oldAnnotation, T newAnnotation) { - return newAnnotation.getVerticalDistance() >= oldAnnotation.getVerticalDistance() ? newAnnotation : oldAnnotation; + return Hierarchical.Selector.FARTHEST_AND_NEWEST_PRIORITY.choose(oldAnnotation, newAnnotation); } } diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedMetaAggregateAnnotation.java b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedMetaAggregateAnnotation.java index 4c9106c46..9a06f37bf 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedMetaAggregateAnnotation.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedMetaAggregateAnnotation.java @@ -52,6 +52,21 @@ public class SynthesizedMetaAggregateAnnotation implements SynthesizedAggregateA */ private final Annotation source; + /** + * 根对象 + */ + private final Object root; + + /** + * 距离根对象的垂直距离 + */ + private final int verticalDistance; + + /** + * 距离根对象的水平距离 + */ + private final int horizontalDistance; + /** * 包含根注解以及其元注解在内的全部注解实例 */ @@ -94,11 +109,35 @@ public class SynthesizedMetaAggregateAnnotation implements SynthesizedAggregateA /** * 基于指定根注解,为其层级结构中的全部注解构造一个合成注解 * - * @param annotation 当前查找的注解对象 - * @param annotationSelector 合成注解选择器 - * @param attributeProcessor 注解属性处理器 + * @param annotation 当前查找的注解对象 + * @param annotationSelector 合成注解选择器 + * @param attributeProcessor 注解属性处理器 + * @param annotationPostProcessors 注解后置处理器 */ public SynthesizedMetaAggregateAnnotation( + Annotation annotation, + SynthesizedAnnotationSelector annotationSelector, + SynthesizedAnnotationAttributeProcessor attributeProcessor, + Collection annotationPostProcessors) { + this( + null, 0, 0, + annotation, annotationSelector, attributeProcessor, annotationPostProcessors + ); + } + + /** + * 基于指定根注解,为其层级结构中的全部注解构造一个合成注解 + * + * @param root 根对象 + * @param verticalDistance 距离根对象的水平距离 + * @param horizontalDistance 距离根对象的垂直距离 + * @param annotation 当前查找的注解对象 + * @param annotationSelector 合成注解选择器 + * @param attributeProcessor 注解属性处理器 + * @param annotationPostProcessors 注解后置处理器 + */ + SynthesizedMetaAggregateAnnotation( + Object root, int verticalDistance, int horizontalDistance, Annotation annotation, SynthesizedAnnotationSelector annotationSelector, SynthesizedAnnotationAttributeProcessor attributeProcessor, @@ -108,6 +147,11 @@ public class SynthesizedMetaAggregateAnnotation implements SynthesizedAggregateA Assert.notNull(attributeProcessor, "attributeProcessor must not null"); Assert.notNull(annotationPostProcessors, "attributePostProcessors must not null"); + // 初始化坐标 + this.root = ObjectUtil.defaultIfNull(root, this); + this.verticalDistance = verticalDistance; + this.horizontalDistance = horizontalDistance; + // 初始化属性 this.source = annotation; this.annotationSelector = annotationSelector; @@ -123,6 +167,36 @@ public class SynthesizedMetaAggregateAnnotation implements SynthesizedAggregateA ); } + /** + * 获取根对象 + * + * @return 根对象 + */ + @Override + public Object getRoot() { + return root; + } + + /** + * 获取与根对象的垂直距离 + * + * @return 与根对象的垂直距离 + */ + @Override + public int getVerticalDistance() { + return verticalDistance; + } + + /** + * 获取与根对象的水平距离 + * + * @return 获取与根对象的水平距离 + */ + @Override + public int getHorizontalDistance() { + return horizontalDistance; + } + /** * 获取根注解 * diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/AliasAnnotationPostProcessorTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/AliasAnnotationPostProcessorTest.java index db9558fd4..922c3cc6b 100644 --- a/hutool-core/src/test/java/cn/hutool/core/annotation/AliasAnnotationPostProcessorTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/AliasAnnotationPostProcessorTest.java @@ -106,6 +106,11 @@ public class AliasAnnotationPostProcessorTest { public Object getAttribute(String attributeName, Class attributeType) { return null; } + + @Override + public Object getRoot() { + return null; + } } static class TestSynthesizedAnnotation implements SynthesizedAnnotation { diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/AliasLinkAnnotationPostProcessorTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/AliasLinkAnnotationPostProcessorTest.java index 1ca645d53..877f8afb2 100644 --- a/hutool-core/src/test/java/cn/hutool/core/annotation/AliasLinkAnnotationPostProcessorTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/AliasLinkAnnotationPostProcessorTest.java @@ -134,6 +134,11 @@ public class AliasLinkAnnotationPostProcessorTest { public Object getAttribute(String attributeName, Class attributeType) { return null; } + + @Override + public Object getRoot() { + return null; + } } static class TestSynthesizedAnnotation implements SynthesizedAnnotation { diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessorTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessorTest.java index 5f18f9574..d14734229 100644 --- a/hutool-core/src/test/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessorTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessorTest.java @@ -109,6 +109,11 @@ public class MirrorLinkAnnotationPostProcessorTest { return null; } + @Override + public Object getRoot() { + return null; + } + } static class TestSynthesizedAnnotation implements SynthesizedAnnotation { From 931965301b28f19ece802c45a178e3bf772c796e Mon Sep 17 00:00:00 2001 From: huangchengxing <841396397@qq.com> Date: Sat, 16 Jul 2022 13:18:55 +0800 Subject: [PATCH 12/15] =?UTF-8?q?=E6=8F=90=E5=8F=96=E6=B3=A8=E8=A7=A3?= =?UTF-8?q?=E5=90=88=E6=88=90=E7=9B=B8=E5=85=B3=E9=80=BB=E8=BE=91=E8=87=B3?= =?UTF-8?q?=E6=B3=A8=E8=A7=A3=E5=90=88=E6=88=90=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AbstractAnnotationSynthesizer.java | 148 +++++++++++++++++ .../AbstractLinkAnnotationPostProcessor.java | 20 +-- .../AbstractSynthesizedAnnotation.java | 15 +- .../AliasAnnotationPostProcessor.java | 2 +- .../AliasLinkAnnotationPostProcessor.java | 18 +- .../annotation/AnnotationSynthesizer.java | 77 +++++++++ .../MirrorLinkAnnotationPostProcessor.java | 12 +- .../SynthesizedAggregateAnnotation.java | 45 +---- .../annotation/SynthesizedAnnotation.java | 9 +- .../SynthesizedAnnotationPostProcessor.java | 4 +- .../SynthesizedMetaAggregateAnnotation.java | 156 +++++------------- .../annotation/SyntheticAnnotationProxy.java | 25 +-- .../AliasAnnotationPostProcessorTest.java | 14 +- .../AliasLinkAnnotationPostProcessorTest.java | 14 +- ...sizedAnnotationAttributeProcessorTest.java | 5 - ...MirrorLinkAnnotationPostProcessorTest.java | 14 +- .../SynthesizedAnnotationSelectorTest.java | 5 - .../SyntheticMetaAnnotationTest.java | 2 +- 18 files changed, 331 insertions(+), 254 deletions(-) create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/AbstractAnnotationSynthesizer.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationSynthesizer.java diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractAnnotationSynthesizer.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractAnnotationSynthesizer.java new file mode 100644 index 000000000..8f7ec7d76 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractAnnotationSynthesizer.java @@ -0,0 +1,148 @@ +package cn.hutool.core.annotation; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; + +import java.lang.annotation.Annotation; +import java.util.Collection; +import java.util.Comparator; +import java.util.Map; + +/** + * {@link AnnotationSynthesizer}的基本实现 + * + * @author huangchengxing + */ +public abstract class AbstractAnnotationSynthesizer implements AnnotationSynthesizer { + + /** + * 合成注解来源最初来源 + */ + protected final T source; + + /** + * 包含根注解以及其元注解在内的全部注解实例 + */ + protected final Map, SynthesizedAnnotation> synthesizedAnnotationMap; + + /** + * 合成注解选择器 + */ + protected final SynthesizedAnnotationSelector annotationSelector; + + /** + * 合成注解属性处理器 + */ + protected final Collection postProcessors; + + /** + * 基于指定根注解,为其层级结构中的全部注解构造一个合成注解 + * + * @param source 当前查找的注解对象 + * @param annotationSelector 合成注解选择器 + * @param annotationPostProcessors 注解后置处理器 + */ + protected AbstractAnnotationSynthesizer( + T source, SynthesizedAnnotationSelector annotationSelector, Collection annotationPostProcessors) { + Assert.notNull(source, "source must not null"); + Assert.notNull(annotationSelector, "annotationSelector must not null"); + Assert.notNull(annotationPostProcessors, "annotationPostProcessors must not null"); + + this.source = source; + this.annotationSelector = annotationSelector; + this.postProcessors = CollUtil.unmodifiable( + CollUtil.sort(annotationPostProcessors, Comparator.comparing(SynthesizedAnnotationPostProcessor::order)) + ); + this.synthesizedAnnotationMap = MapUtil.unmodifiable(loadAnnotations()); + annotationPostProcessors.forEach(processor -> + synthesizedAnnotationMap.values().forEach(synthesized -> processor.process(synthesized, this)) + ); + } + + /** + * 加载合成注解的必要属性 + * + * @return 合成注解 + */ + protected abstract Map, SynthesizedAnnotation> loadAnnotations(); + + /** + * 根据指定的注解类型和对应注解对象,合成最终所需的合成注解 + * + * @param annotationType 注解类型 + * @param annotation 合成注解对象 + * @param 注解类型 + * @return 最终所需的合成注解 + */ + protected abstract A synthesize(Class annotationType, SynthesizedAnnotation annotation); + + /** + * 获取合成注解来源最初来源 + * + * @return 合成注解来源最初来源 + */ + @Override + public T getSource() { + return source; + } + + /** + * 合成注解选择器 + * + * @return 注解选择器 + */ + @Override + public SynthesizedAnnotationSelector getAnnotationSelector() { + return annotationSelector; + } + + /** + * 获取合成注解后置处理器 + * + * @return 合成注解后置处理器 + */ + @Override + public Collection getAnnotationPostProcessors() { + return postProcessors; + } + + /** + * 获取已合成的注解 + * + * @param annotationType 注解类型 + * @return 已合成的注解 + */ + @Override + public SynthesizedAnnotation getSynthesizedAnnotation(Class annotationType) { + return synthesizedAnnotationMap.get(annotationType); + } + + /** + * 获取全部的合成注解 + * + * @return 合成注解 + */ + @Override + public Map, SynthesizedAnnotation> getAllSynthesizedAnnotation() { + return synthesizedAnnotationMap; + } + + /** + * 获取合成注解 + * + * @param annotationType 注解类型 + * @param 注解类型 + * @return 类型 + */ + @Override + public A synthesize(Class annotationType) { + SynthesizedAnnotation synthesizedAnnotation = synthesizedAnnotationMap.get(annotationType); + if (ObjectUtil.isNull(synthesizedAnnotation)) { + return null; + } + return synthesize(annotationType, synthesizedAnnotation); + } + +} diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractLinkAnnotationPostProcessor.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractLinkAnnotationPostProcessor.java index d510176ad..578bb6fde 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractLinkAnnotationPostProcessor.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractLinkAnnotationPostProcessor.java @@ -26,10 +26,10 @@ public abstract class AbstractLinkAnnotationPostProcessor implements Synthesized * {@link #processLinkedAttribute}处理。 * * @param synthesizedAnnotation 合成的注解 - * @param aggregator 合成注解聚合器 + * @param synthesizer 合成注解聚合器 */ @Override - public void process(SynthesizedAnnotation synthesizedAnnotation, SynthesizedAggregateAnnotation aggregator) { + public void process(SynthesizedAnnotation synthesizedAnnotation, AnnotationSynthesizer synthesizer) { final Map attributeMap = new HashMap<>(synthesizedAnnotation.getAttributes()); attributeMap.forEach((originalAttributeName, originalAttribute) -> { // 获取注解 @@ -38,14 +38,14 @@ public abstract class AbstractLinkAnnotationPostProcessor implements Synthesized return; } // 获取注解属性 - final SynthesizedAnnotation linkedAnnotation = getLinkedAnnotation(link, aggregator, synthesizedAnnotation.annotationType()); + final SynthesizedAnnotation linkedAnnotation = getLinkedAnnotation(link, synthesizer, synthesizedAnnotation.annotationType()); if (ObjectUtil.isNull(linkedAnnotation)) { return; } final AnnotationAttribute linkedAttribute = linkedAnnotation.getAttributes().get(link.attribute()); // 处理 processLinkedAttribute( - aggregator, link, + synthesizer, link, synthesizedAnnotation, synthesizedAnnotation.getAttributes().get(originalAttributeName), linkedAnnotation, linkedAttribute ); @@ -64,7 +64,7 @@ public abstract class AbstractLinkAnnotationPostProcessor implements Synthesized /** * 对关联的合成注解对象及其关联属性的处理 * - * @param aggregator 合成注解聚合器 + * @param synthesizer 注解合成器 * @param annotation {@code originalAttribute}上的{@link Link}注解对象 * @param originalAnnotation 当前正在处理的{@link SynthesizedAnnotation}对象 * @param originalAttribute {@code originalAnnotation}上的待处理的属性 @@ -72,7 +72,7 @@ public abstract class AbstractLinkAnnotationPostProcessor implements Synthesized * @param linkedAttribute {@link Link}指向的{@code originalAnnotation}中的关联属性,该参数可能为空 */ protected abstract void processLinkedAttribute( - SynthesizedAggregateAnnotation aggregator, Link annotation, + AnnotationSynthesizer synthesizer, Link annotation, SynthesizedAnnotation originalAnnotation, AnnotationAttribute originalAttribute, SynthesizedAnnotation linkedAnnotation, AnnotationAttribute linkedAttribute ); @@ -96,13 +96,13 @@ public abstract class AbstractLinkAnnotationPostProcessor implements Synthesized /** * 从合成注解中获取{@link Link#type()}指定的注解对象 * - * @param annotation {@link Link}注解 - * @param synthesizedAnnotationAggregator 合成注解 + * @param annotation {@link Link}注解 + * @param synthesizer 注解合成器 */ protected SynthesizedAnnotation getLinkedAnnotation( - Link annotation, SynthesizedAggregateAnnotation synthesizedAnnotationAggregator, Class defaultType) { + Link annotation, AnnotationSynthesizer synthesizer, Class defaultType) { final Class targetAnnotationType = getLinkedAnnotationType(annotation, defaultType); - return synthesizedAnnotationAggregator.getSynthesizedAnnotation(targetAnnotationType); + return synthesizer.getSynthesizedAnnotation(targetAnnotationType); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractSynthesizedAnnotation.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractSynthesizedAnnotation.java index c8b906cc5..a8a19c4cc 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractSynthesizedAnnotation.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractSynthesizedAnnotation.java @@ -20,7 +20,6 @@ import java.util.stream.Stream; */ public abstract class AbstractSynthesizedAnnotation implements Annotation, SynthesizedAnnotation { - private final SynthesizedAggregateAnnotation owner; private final R root; private final Annotation annotation; private final Map attributeMethodCaches; @@ -30,15 +29,13 @@ public abstract class AbstractSynthesizedAnnotation implements Annotation, Sy /** * 创建一个合成注解 * - * @param owner 合成注解所属的合成注解聚合器 * @param root 根对象 * @param annotation 被合成的注解对象 * @param verticalDistance 距离根对象的水平距离 * @param horizontalDistance 距离根对象的垂直距离 */ protected AbstractSynthesizedAnnotation( - SynthesizedAggregateAnnotation owner, R root, Annotation annotation, int verticalDistance, int horizontalDistance) { - this.owner = owner; + R root, Annotation annotation, int verticalDistance, int horizontalDistance) { this.root = root; this.annotation = annotation; this.verticalDistance = verticalDistance; @@ -130,16 +127,6 @@ public abstract class AbstractSynthesizedAnnotation implements Annotation, Sy .get(); } - /** - * 获取所属的合成注解集合器 - * - * @return 合成注解 - */ - @Override - public SynthesizedAggregateAnnotation getOwner() { - return owner; - } - /** * 获取该合成注解对应的根节点 * diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AliasAnnotationPostProcessor.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AliasAnnotationPostProcessor.java index fe8aa890d..ac5481a27 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/AliasAnnotationPostProcessor.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AliasAnnotationPostProcessor.java @@ -27,7 +27,7 @@ public class AliasAnnotationPostProcessor implements SynthesizedAnnotationPostPr } @Override - public void process(SynthesizedAnnotation synthesizedAnnotation, SynthesizedAggregateAnnotation aggregator) { + public void process(SynthesizedAnnotation synthesizedAnnotation, AnnotationSynthesizer synthesizer) { final Map attributeMap = synthesizedAnnotation.getAttributes(); // 记录别名与属性的关系 diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AliasLinkAnnotationPostProcessor.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AliasLinkAnnotationPostProcessor.java index d5fe28f18..0cf5b2255 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/AliasLinkAnnotationPostProcessor.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AliasLinkAnnotationPostProcessor.java @@ -43,7 +43,7 @@ public class AliasLinkAnnotationPostProcessor extends AbstractLinkAnnotationPost * 将目标注解属性包装为{@link AliasedAnnotationAttribute}或{@link ForceAliasedAnnotationAttribute}, * 然后用包装后注解属性在对应的合成注解中替换原始的目标注解属性 * - * @param aggregator 合成注解聚合器 + * @param synthesizer 注解合成器 * @param annotation {@code originalAttribute}上的{@link Link}注解对象 * @param originalAnnotation 当前正在处理的{@link SynthesizedAnnotation}对象 * @param originalAttribute {@code originalAnnotation}上的待处理的属性 @@ -52,34 +52,34 @@ public class AliasLinkAnnotationPostProcessor extends AbstractLinkAnnotationPost */ @Override protected void processLinkedAttribute( - SynthesizedAggregateAnnotation aggregator, Link annotation, + AnnotationSynthesizer synthesizer, Link annotation, SynthesizedAnnotation originalAnnotation, AnnotationAttribute originalAttribute, SynthesizedAnnotation linkedAnnotation, AnnotationAttribute linkedAttribute) { // 校验别名关系 checkAliasRelation(annotation, originalAttribute, linkedAttribute); // 处理aliasFor类型的关系 if (RelationType.ALIAS_FOR.equals(annotation.type())) { - wrappingLinkedAttribute(aggregator, originalAttribute, linkedAttribute, AliasedAnnotationAttribute::new); + wrappingLinkedAttribute(synthesizer, originalAttribute, linkedAttribute, AliasedAnnotationAttribute::new); return; } // 处理forceAliasFor类型的关系 - wrappingLinkedAttribute(aggregator, originalAttribute, linkedAttribute, ForceAliasedAnnotationAttribute::new); + wrappingLinkedAttribute(synthesizer, originalAttribute, linkedAttribute, ForceAliasedAnnotationAttribute::new); } /** * 对指定注解属性进行包装,若该属性已被包装过,则递归以其为根节点的树结构,对树上全部的叶子节点进行包装 */ private void wrappingLinkedAttribute( - SynthesizedAggregateAnnotation synthesizedAnnotationAggregator, AnnotationAttribute originalAttribute, AnnotationAttribute aliasAttribute, BinaryOperator wrapping) { + AnnotationSynthesizer synthesizer, AnnotationAttribute originalAttribute, AnnotationAttribute aliasAttribute, BinaryOperator wrapping) { // 不是包装属性 if (!aliasAttribute.isWrapped()) { - processAttribute(synthesizedAnnotationAggregator, originalAttribute, aliasAttribute, wrapping); + processAttribute(synthesizer, originalAttribute, aliasAttribute, wrapping); return; } // 是包装属性 final AbstractWrappedAnnotationAttribute wrapper = (AbstractWrappedAnnotationAttribute)aliasAttribute; wrapper.getAllLinkedNonWrappedAttributes().forEach( - t -> processAttribute(synthesizedAnnotationAggregator, originalAttribute, t, wrapping) + t -> processAttribute(synthesizer, originalAttribute, t, wrapping) ); } @@ -87,10 +87,10 @@ public class AliasLinkAnnotationPostProcessor extends AbstractLinkAnnotationPost * 获取指定注解属性,然后将其再进行一层包装 */ private void processAttribute( - SynthesizedAggregateAnnotation synthesizedAnnotationAggregator, AnnotationAttribute originalAttribute, + AnnotationSynthesizer synthesizer, AnnotationAttribute originalAttribute, AnnotationAttribute target, BinaryOperator wrapping) { Opt.ofNullable(target.getAnnotationType()) - .map(synthesizedAnnotationAggregator::getSynthesizedAnnotation) + .map(synthesizer::getSynthesizedAnnotation) .ifPresent(t -> t.replaceAttribute(target.getAttributeName(), old -> wrapping.apply(old, originalAttribute))); } diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationSynthesizer.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationSynthesizer.java new file mode 100644 index 000000000..298a33dbc --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationSynthesizer.java @@ -0,0 +1,77 @@ +package cn.hutool.core.annotation; + +import java.lang.annotation.Annotation; +import java.util.Collection; +import java.util.Map; + +/** + *

注解合成器,用于处理一组给定的与{@link #getSource()}具有直接或间接联系的注解对象, + * 并返回与原始注解对象具有不同属性的“合成”注解。 + * + *

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

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

使用{@link #synthesize(Class)}用于获取“合成”后的注解, + * 该注解对象的属性可能会与原始的对象属性不同。 + * + * @author huangchengxing + */ +public interface AnnotationSynthesizer { + + /** + * 获取合成注解来源最初来源 + * + * @return 合成注解来源最初来源 + */ + Object getSource(); + + /** + * 合成注解选择器 + * + * @return 注解选择器 + */ + SynthesizedAnnotationSelector getAnnotationSelector(); + + /** + * 获取合成注解后置处理器 + * + * @return 合成注解后置处理器 + */ + Collection getAnnotationPostProcessors(); + + /** + * 获取已合成的注解 + * + * @param annotationType 注解类型 + * @return 已合成的注解 + */ + SynthesizedAnnotation getSynthesizedAnnotation(Class annotationType); + + /** + * 获取全部的合成注解 + * + * @return 合成注解 + */ + Map, SynthesizedAnnotation> getAllSynthesizedAnnotation(); + + /** + * 获取合成注解 + * + * @param annotationType 注解类型 + * @param 注解类型 + * @return 类型 + */ + T synthesize(Class annotationType); + +} diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessor.java b/hutool-core/src/main/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessor.java index f523473e8..1fc0a203b 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessor.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessor.java @@ -1,8 +1,8 @@ package cn.hutool.core.annotation; import cn.hutool.core.lang.Assert; +import cn.hutool.core.text.CharSequenceUtil; import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.StrUtil; /** *

用于处理注解对象中带有{@link Link}注解,且{@link Link#type()}为{@link RelationType#MIRROR_FOR}的属性。
@@ -36,7 +36,7 @@ public class MirrorLinkAnnotationPostProcessor extends AbstractLinkAnnotationPos * 将存在镜像关系的合成注解属性分别包装为{@link MirroredAnnotationAttribute}对象, * 并使用包装后{@link MirroredAnnotationAttribute}替换在它们对应合成注解实例中的{@link AnnotationAttribute} * - * @param aggregator 合成注解聚合器 + * @param synthesizer 注解合成器 * @param annotation {@code originalAttribute}上的{@link Link}注解对象 * @param originalAnnotation 当前正在处理的{@link SynthesizedAnnotation}对象 * @param originalAttribute {@code originalAnnotation}上的待处理的属性 @@ -45,7 +45,7 @@ public class MirrorLinkAnnotationPostProcessor extends AbstractLinkAnnotationPos */ @Override protected void processLinkedAttribute( - SynthesizedAggregateAnnotation aggregator, Link annotation, + AnnotationSynthesizer synthesizer, Link annotation, SynthesizedAnnotation originalAnnotation, AnnotationAttribute originalAttribute, SynthesizedAnnotation linkedAnnotation, AnnotationAttribute linkedAttribute) { @@ -86,21 +86,21 @@ public class MirrorLinkAnnotationPostProcessor extends AbstractLinkAnnotationPos String errorMsg; // 原始字段已经跟其他字段形成镜像 if (originalAttributeMirrored && !mirrorAttributeMirrored) { - errorMsg = StrUtil.format( + errorMsg = CharSequenceUtil.format( "attribute [{}] cannot mirror for [{}], because it's already mirrored for [{}]", original.getAttribute(), mirror.getAttribute(), ((MirroredAnnotationAttribute)original).getLinked() ); } // 镜像字段已经跟其他字段形成镜像 else if (!originalAttributeMirrored && mirrorAttributeMirrored) { - errorMsg = StrUtil.format( + errorMsg = CharSequenceUtil.format( "attribute [{}] cannot mirror for [{}], because it's already mirrored for [{}]", mirror.getAttribute(), original.getAttribute(), ((MirroredAnnotationAttribute)mirror).getLinked() ); } // 两者都形成了镜像,但是都未指向对方,理论上不会存在该情况 else { - errorMsg = StrUtil.format( + errorMsg = CharSequenceUtil.format( "attribute [{}] cannot mirror for [{}], because [{}] already mirrored for [{}] and [{}] already mirrored for [{}]", mirror.getAttribute(), original.getAttribute(), mirror.getAttribute(), ((MirroredAnnotationAttribute)mirror).getLinked(), diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAggregateAnnotation.java b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAggregateAnnotation.java index b2d047bb6..50ffadf7b 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAggregateAnnotation.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAggregateAnnotation.java @@ -1,10 +1,10 @@ package cn.hutool.core.annotation; import java.lang.annotation.Annotation; -import java.util.Collection; /** - * 表示基于特定规则聚合,将一组注解聚合而来的注解对象 + *

表示基于特定规则聚合,将一组注解聚合而来的注解对象, + * 该注解对象允许根据一定规则“合成”一些跟原始注解属性不一样合成注解。 * *

合成注解一般被用于处理类层级结果中具有直接或间接关联的注解对象, * 当实例被创建时,会获取到这些注解对象,并使用{@link SynthesizedAnnotationSelector}对类型相同的注解进行过滤, @@ -28,13 +28,14 @@ import java.util.Collection; * 自定义属性处理器以拦截合成注解的取值过程。 * * @author huangchengxing + * @see AnnotationSynthesizer * @see SynthesizedAnnotation * @see SynthesizedAnnotationSelector * @see SynthesizedAnnotationAttributeProcessor * @see SynthesizedAnnotationPostProcessor * @see SynthesizedMetaAggregateAnnotation */ -public interface SynthesizedAggregateAnnotation extends Annotation, AggregateAnnotation, Hierarchical { +public interface SynthesizedAggregateAnnotation extends AggregateAnnotation, Hierarchical, AnnotationSynthesizer { // ================== hierarchical ================== @@ -71,13 +72,6 @@ public interface SynthesizedAggregateAnnotation extends Annotation, AggregateAnn */ T getAnnotation(Class annotationType); - /** - * 获取合成注解选择器 - * - * @return 合成注解选择器 - */ - SynthesizedAnnotationSelector getAnnotationSelector(); - /** * 获取合成注解属性处理器 * @@ -85,28 +79,6 @@ public interface SynthesizedAggregateAnnotation extends Annotation, AggregateAnn */ SynthesizedAnnotationAttributeProcessor getAnnotationAttributeProcessor(); - /** - * 获取合成注解属性后置处理器 - * - * @return 合成注解属性后置处理器 - */ - Collection getAnnotationAttributePostProcessors(); - - /** - * 获取已合成的注解 - * - * @param annotationType 注解类型 - * @return 已合成的注解 - */ - SynthesizedAnnotation getSynthesizedAnnotation(Class annotationType); - - /** - * 获取全部的合成注解 - * - * @return 合成注解 - */ - Collection getAllSynthesizedAnnotation(); - /** * 获取当前的注解类型 * @@ -126,15 +98,6 @@ public interface SynthesizedAggregateAnnotation extends Annotation, AggregateAnn */ Object getAttribute(String attributeName, Class attributeType); - /** - * 获取合成注解 - * - * @param annotationType 注解类型 - * @param 注解类型 - * @return 类型 - */ - T synthesize(Class annotationType); - /** * 基于指定根注解,构建包括其元注解在内的合成注解 * 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 b65370ba2..b97bfbb7e 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 @@ -8,7 +8,7 @@ import java.util.function.UnaryOperator; /** *

用于在{@link SynthesizedAggregateAnnotation}中表示一个处于合成状态的注解对象。
- * 当对多个合成注解排序时,默认使用{@link #DEFAULT_CHILD_PRIORITY_COMPARATOR}进行排序, + * 当对多个合成注解排序时,默认使用{@link #DEFAULT_HIERARCHICAL_COMPARATOR}进行排序, * 从保证合成注解按{@link #getVerticalDistance()}与{@link #getHorizontalDistance()}的返回值保持有序, * 从而使得距离根元素更接近的注解对象在被处理是具有更高的优先级。 * @@ -17,13 +17,6 @@ import java.util.function.UnaryOperator; */ public interface SynthesizedAnnotation extends Annotation, Hierarchical { - /** - * 获取所属的合成注解聚合器 - * - * @return 合成注解 - */ - SynthesizedAggregateAnnotation getOwner(); - /** * 获取被合成的注解对象 * 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 index cedff1f4d..91e701be8 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationPostProcessor.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationPostProcessor.java @@ -64,8 +64,8 @@ public interface SynthesizedAnnotationPostProcessor extends Comparable implements SynthesizedAggregateAnnotation { /** * 根对象 @@ -67,26 +65,11 @@ public class SynthesizedMetaAggregateAnnotation implements SynthesizedAggregateA */ private final int horizontalDistance; - /** - * 包含根注解以及其元注解在内的全部注解实例 - */ - private final Map, SynthesizedAnnotation> metaAnnotationMap; - - /** - * 合成注解选择器 - */ - private final SynthesizedAnnotationSelector annotationSelector; - /** * 合成注解属性处理器 */ private final SynthesizedAnnotationAttributeProcessor attributeProcessor; - /** - * 合成注解属性处理器 - */ - private final List postProcessors; - /** * 基于指定根注解,为其层级结构中的全部注解构造一个合成注解。 * 当层级结构中出现了相同的注解对象时,将优先选择以距离根注解最近,且优先被扫描的注解对象, @@ -118,7 +101,7 @@ public class SynthesizedMetaAggregateAnnotation implements SynthesizedAggregateA Annotation annotation, SynthesizedAnnotationSelector annotationSelector, SynthesizedAnnotationAttributeProcessor attributeProcessor, - Collection annotationPostProcessors) { + Collection annotationPostProcessors) { this( null, 0, 0, annotation, annotationSelector, attributeProcessor, annotationPostProcessors @@ -131,40 +114,24 @@ public class SynthesizedMetaAggregateAnnotation implements SynthesizedAggregateA * @param root 根对象 * @param verticalDistance 距离根对象的水平距离 * @param horizontalDistance 距离根对象的垂直距离 - * @param annotation 当前查找的注解对象 + * @param source 当前查找的注解对象 * @param annotationSelector 合成注解选择器 * @param attributeProcessor 注解属性处理器 * @param annotationPostProcessors 注解后置处理器 */ SynthesizedMetaAggregateAnnotation( Object root, int verticalDistance, int horizontalDistance, - Annotation annotation, + Annotation source, SynthesizedAnnotationSelector annotationSelector, SynthesizedAnnotationAttributeProcessor attributeProcessor, - Collection annotationPostProcessors) { - Assert.notNull(annotation, "annotation must not null"); - Assert.notNull(annotationSelector, "annotationSelector must not null"); + Collection annotationPostProcessors) { + super(source, annotationSelector, annotationPostProcessors); Assert.notNull(attributeProcessor, "attributeProcessor must not null"); - Assert.notNull(annotationPostProcessors, "attributePostProcessors must not null"); - // 初始化坐标 this.root = ObjectUtil.defaultIfNull(root, this); this.verticalDistance = verticalDistance; this.horizontalDistance = horizontalDistance; - - // 初始化属性 - 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)) - ); } /** @@ -198,22 +165,26 @@ public class SynthesizedMetaAggregateAnnotation implements SynthesizedAggregateA } /** - * 获取根注解 - * - * @return 根注解 - */ - public Annotation getSource() { - return source; - } - - /** - * 获取合成注解选择器 - * - * @return 合成注解选择器 + * 按广度优先扫描{@link #source}上的元注解 */ @Override - public SynthesizedAnnotationSelector getAnnotationSelector() { - return this.annotationSelector; + protected Map, SynthesizedAnnotation> loadAnnotations() { + Assert.isFalse(SyntheticAnnotationProxy.isProxyAnnotation(source.getClass()), "source [{}] has been synthesized"); + Map, SynthesizedAnnotation> annotationMap = new LinkedHashMap<>(); + annotationMap.put(source.annotationType(), new MetaAnnotation(source, source, 0, 0)); + new MetaAnnotationScanner().scan( + (index, annotation) -> { + SynthesizedAnnotation oldAnnotation = annotationMap.get(annotation.annotationType()); + SynthesizedAnnotation newAnnotation = new MetaAnnotation(source, annotation, index, annotationMap.size()); + if (ObjectUtil.isNull(oldAnnotation)) { + annotationMap.put(annotation.annotationType(), newAnnotation); + } else { + annotationMap.put(annotation.annotationType(), annotationSelector.choose(oldAnnotation, newAnnotation)); + } + }, + source.annotationType(), null + ); + return annotationMap; } /** @@ -226,37 +197,6 @@ public class SynthesizedMetaAggregateAnnotation implements SynthesizedAggregateA return this.attributeProcessor; } - /** - * 获取合成注解属性后置处理器 - * - * @return 合成注解属性后置处理器 - */ - @Override - public Collection getAnnotationAttributePostProcessors() { - return this.postProcessors; - } - - /** - * 获取已合成的注解 - * - * @param annotationType 注解类型 - * @return 已合成的注解 - */ - @Override - public SynthesizedAnnotation getSynthesizedAnnotation(Class annotationType) { - return metaAnnotationMap.get(annotationType); - } - - /** - * 获取全部的已合成注解 - * - * @return 合成注解 - */ - @Override - public Collection getAllSynthesizedAnnotation() { - return metaAnnotationMap.values(); - } - /** * 根据指定的属性名与属性类型获取对应的属性值,若存在{@link Alias}则获取{@link Alias#value()}指定的别名属性的值 *

当不同层级的注解之间存在同名同类型属性时,将优先获取更接近根注解的属性 @@ -267,7 +207,7 @@ public class SynthesizedMetaAggregateAnnotation implements SynthesizedAggregateA */ @Override public Object getAttribute(String attributeName, Class attributeType) { - return attributeProcessor.getAttributeValue(attributeName, attributeType, metaAnnotationMap.values()); + return attributeProcessor.getAttributeValue(attributeName, attributeType, synthesizedAnnotationMap.values()); } /** @@ -280,7 +220,7 @@ public class SynthesizedMetaAggregateAnnotation implements SynthesizedAggregateA @Override public T getAnnotation(Class annotationType) { return Opt.ofNullable(annotationType) - .map(metaAnnotationMap::get) + .map(synthesizedAnnotationMap::get) .map(SynthesizedAnnotation::getAnnotation) .map(annotationType::cast) .orElse(null); @@ -294,7 +234,7 @@ public class SynthesizedMetaAggregateAnnotation implements SynthesizedAggregateA */ @Override public boolean isAnnotationPresent(Class annotationType) { - return metaAnnotationMap.containsKey(annotationType); + return synthesizedAnnotationMap.containsKey(annotationType); } /** @@ -304,7 +244,7 @@ public class SynthesizedMetaAggregateAnnotation implements SynthesizedAggregateA */ @Override public Annotation[] getAnnotations() { - return metaAnnotationMap.values().stream() + return synthesizedAnnotationMap.values().stream() .map(SynthesizedAnnotation::getAnnotation) .toArray(Annotation[]::new); } @@ -314,32 +254,11 @@ public class SynthesizedMetaAggregateAnnotation implements SynthesizedAggregateA * * @param annotationType 注解类型 * @return 合成注解对象 - * @see SyntheticAnnotationProxy#create(Class, SynthesizedAggregateAnnotation) + * @see SyntheticAnnotationProxy#create(Class, SynthesizedAggregateAnnotation, SynthesizedAnnotation) */ @Override - public T synthesize(Class annotationType) { - return SyntheticAnnotationProxy.create(annotationType, this); - } - - /** - * 广度优先遍历并缓存该根注解上的全部元注解 - */ - private void loadMetaAnnotations() { - Assert.isFalse(SyntheticAnnotationProxy.isProxyAnnotation(source.getClass()), "source [{}] has been synthesized"); - // 扫描元注解 - 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(this, source, annotation, index, metaAnnotationMap.size()); - if (ObjectUtil.isNull(oldAnnotation)) { - metaAnnotationMap.put(annotation.annotationType(), newAnnotation); - } else { - metaAnnotationMap.put(annotation.annotationType(), annotationSelector.choose(oldAnnotation, newAnnotation)); - } - }, - source.annotationType(), null - ); + public T synthesize(Class annotationType, SynthesizedAnnotation annotation) { + return SyntheticAnnotationProxy.create(annotationType, this, annotation); } /** @@ -352,14 +271,13 @@ public class SynthesizedMetaAggregateAnnotation implements SynthesizedAggregateA /** * 创建一个合成注解 * - * @param owner 合成注解所属的合成注解聚合器 * @param root 根对象 * @param annotation 被合成的注解对象 * @param verticalDistance 距离根对象的水平距离 * @param horizontalDistance 距离根对象的垂直距离 */ - protected MetaAnnotation(SynthesizedAggregateAnnotation owner, Annotation root, Annotation annotation, int verticalDistance, int horizontalDistance) { - super(owner, root, annotation, verticalDistance, horizontalDistance); + protected MetaAnnotation(Annotation root, Annotation annotation, int verticalDistance, int horizontalDistance) { + super(root, annotation, verticalDistance, horizontalDistance); } } 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 f107c290e..e5ef1beb8 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 @@ -2,10 +2,10 @@ package cn.hutool.core.annotation; import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Opt; +import cn.hutool.core.text.CharSequenceUtil; import cn.hutool.core.util.ClassUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ReflectUtil; -import cn.hutool.core.util.StrUtil; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationHandler; @@ -25,11 +25,14 @@ import java.util.stream.Stream; */ class SyntheticAnnotationProxy implements InvocationHandler { + private final SynthesizedAggregateAnnotation aggregateAnnotation; private final SynthesizedAnnotation annotation; private final Map> methods; - SyntheticAnnotationProxy(SynthesizedAnnotation annotation) { + SyntheticAnnotationProxy(SynthesizedAggregateAnnotation aggregateAnnotation, SynthesizedAnnotation annotation) { + Assert.notNull(aggregateAnnotation, "aggregateAnnotation must not null"); Assert.notNull(annotation, "annotation must not null"); + this.aggregateAnnotation = aggregateAnnotation; this.annotation = annotation; this.methods = new HashMap<>(9); loadMethods(); @@ -43,17 +46,16 @@ class SyntheticAnnotationProxy implements InvocationHandler { * * * @param annotationType 注解类型 - * @param synthesizedAnnotationAggregator 合成注解 + * @param aggregateAnnotation 合成注解 * @return 代理注解 */ @SuppressWarnings("unchecked") static T create( - Class annotationType, SynthesizedAggregateAnnotation synthesizedAnnotationAggregator) { - final SynthesizedAnnotation annotation = synthesizedAnnotationAggregator.getSynthesizedAnnotation(annotationType); + Class annotationType, SynthesizedAggregateAnnotation aggregateAnnotation, SynthesizedAnnotation annotation) { if (ObjectUtil.isNull(annotation)) { return null; } - final SyntheticAnnotationProxy proxyHandler = new SyntheticAnnotationProxy(annotation); + final SyntheticAnnotationProxy proxyHandler = new SyntheticAnnotationProxy(aggregateAnnotation, annotation); if (ObjectUtil.isNull(annotation)) { return null; } @@ -91,7 +93,6 @@ class SyntheticAnnotationProxy implements InvocationHandler { 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 : ClassUtil.getDeclaredMethods(annotation.getAnnotation().annotationType())) { methods.put(declaredMethod.getName(), (method, args) -> proxyAttributeValue(method)); @@ -101,17 +102,17 @@ class SyntheticAnnotationProxy implements InvocationHandler { private String proxyToString() { final String attributes = Stream.of(ClassUtil.getDeclaredMethods(annotation.getAnnotation().annotationType())) .filter(AnnotationUtil::isAttributeMethod) - .map(method -> StrUtil.format("{}={}", method.getName(), annotation.getOwner().getAttribute(method.getName(), method.getReturnType()))) + .map(method -> CharSequenceUtil.format("{}={}", method.getName(), aggregateAnnotation.getAttribute(method.getName(), method.getReturnType()))) .collect(Collectors.joining(", ")); - return StrUtil.format("@{}({})", annotation.annotationType().getName(), attributes); + return CharSequenceUtil.format("@{}({})", annotation.annotationType().getName(), attributes); } private int proxyHashCode() { - return Objects.hash(annotation.getOwner(), annotation); + return Objects.hash(aggregateAnnotation, annotation); } private Object proxyGetSynthesizedAnnotationAggregator() { - return annotation.getOwner(); + return aggregateAnnotation; } private Object proxyGetSynthesizedAnnotation() { @@ -119,7 +120,7 @@ class SyntheticAnnotationProxy implements InvocationHandler { } private Object proxyAttributeValue(Method attributeMethod) { - return annotation.getOwner().getAttribute(attributeMethod.getName(), attributeMethod.getReturnType()); + return aggregateAnnotation.getAttribute(attributeMethod.getName(), attributeMethod.getReturnType()); } /** diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/AliasAnnotationPostProcessorTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/AliasAnnotationPostProcessorTest.java index 922c3cc6b..b3af98d16 100644 --- a/hutool-core/src/test/java/cn/hutool/core/annotation/AliasAnnotationPostProcessorTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/AliasAnnotationPostProcessorTest.java @@ -57,6 +57,11 @@ public class AliasAnnotationPostProcessorTest { this.annotationMap = annotationMap; } + @Override + public Object getSource() { + return null; + } + @Override public SynthesizedAnnotationSelector getAnnotationSelector() { return null; @@ -68,7 +73,7 @@ public class AliasAnnotationPostProcessorTest { } @Override - public Collection getAnnotationAttributePostProcessors() { + public Collection getAnnotationPostProcessors() { return null; } @@ -78,7 +83,7 @@ public class AliasAnnotationPostProcessorTest { } @Override - public Collection getAllSynthesizedAnnotation() { + public Map, SynthesizedAnnotation> getAllSynthesizedAnnotation() { return null; } @@ -128,11 +133,6 @@ public class AliasAnnotationPostProcessorTest { } } - @Override - public SynthesizedAggregateAnnotation getOwner() { - return owner; - } - @Override public Object getRoot() { return null; diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/AliasLinkAnnotationPostProcessorTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/AliasLinkAnnotationPostProcessorTest.java index 877f8afb2..2521b2080 100644 --- a/hutool-core/src/test/java/cn/hutool/core/annotation/AliasLinkAnnotationPostProcessorTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/AliasLinkAnnotationPostProcessorTest.java @@ -85,6 +85,11 @@ public class AliasLinkAnnotationPostProcessorTest { this.annotationMap = annotationMap; } + @Override + public Object getSource() { + return null; + } + @Override public SynthesizedAnnotationSelector getAnnotationSelector() { return null; @@ -96,7 +101,7 @@ public class AliasLinkAnnotationPostProcessorTest { } @Override - public Collection getAnnotationAttributePostProcessors() { + public Collection getAnnotationPostProcessors() { return null; } @@ -106,7 +111,7 @@ public class AliasLinkAnnotationPostProcessorTest { } @Override - public Collection getAllSynthesizedAnnotation() { + public Map, SynthesizedAnnotation> getAllSynthesizedAnnotation() { return null; } @@ -156,11 +161,6 @@ public class AliasLinkAnnotationPostProcessorTest { } } - @Override - public SynthesizedAggregateAnnotation getOwner() { - return owner; - } - @Override public Object getRoot() { return null; diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/CacheableSynthesizedAnnotationAttributeProcessorTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/CacheableSynthesizedAnnotationAttributeProcessorTest.java index 3a9717134..4744866f7 100644 --- a/hutool-core/src/test/java/cn/hutool/core/annotation/CacheableSynthesizedAnnotationAttributeProcessorTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/CacheableSynthesizedAnnotationAttributeProcessorTest.java @@ -38,11 +38,6 @@ public class CacheableSynthesizedAnnotationAttributeProcessorTest { this.value = value; } - @Override - public SynthesizedAggregateAnnotation getOwner() { - return null; - } - @Override public Object getRoot() { return null; diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessorTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessorTest.java index d14734229..3b422bb8d 100644 --- a/hutool-core/src/test/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessorTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessorTest.java @@ -59,6 +59,11 @@ public class MirrorLinkAnnotationPostProcessorTest { this.annotationMap = annotationMap; } + @Override + public Object getSource() { + return null; + } + @Override public SynthesizedAnnotationSelector getAnnotationSelector() { return null; @@ -70,7 +75,7 @@ public class MirrorLinkAnnotationPostProcessorTest { } @Override - public Collection getAnnotationAttributePostProcessors() { + public Collection getAnnotationPostProcessors() { return null; } @@ -80,7 +85,7 @@ public class MirrorLinkAnnotationPostProcessorTest { } @Override - public Collection getAllSynthesizedAnnotation() { + public Map, SynthesizedAnnotation> getAllSynthesizedAnnotation() { return null; } @@ -131,11 +136,6 @@ public class MirrorLinkAnnotationPostProcessorTest { } } - @Override - public SynthesizedAggregateAnnotation getOwner() { - return owner; - } - @Override public Object getRoot() { return null; diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/SynthesizedAnnotationSelectorTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/SynthesizedAnnotationSelectorTest.java index 0436d5c4c..0822fdb78 100644 --- a/hutool-core/src/test/java/cn/hutool/core/annotation/SynthesizedAnnotationSelectorTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/SynthesizedAnnotationSelectorTest.java @@ -87,11 +87,6 @@ public class SynthesizedAnnotationSelectorTest { this.horizontalDistance = horizontalDistance; } - @Override - public SynthesizedAggregateAnnotation getOwner() { - return null; - } - @Override public Object getRoot() { return null; 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 a0a279641..4b6d6c387 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 @@ -49,7 +49,7 @@ public class SyntheticMetaAnnotationTest { // 属性 Assert.assertEquals(SynthesizedAnnotationSelector.NEAREST_AND_OLDEST_PRIORITY, syntheticMetaAnnotation.getAnnotationSelector()); Assert.assertEquals(CacheableSynthesizedAnnotationAttributeProcessor.class, syntheticMetaAnnotation.getAnnotationAttributeProcessor().getClass()); - Assert.assertEquals(3, syntheticMetaAnnotation.getAnnotationAttributePostProcessors().size()); + Assert.assertEquals(3, syntheticMetaAnnotation.getAnnotationPostProcessors().size()); } @Test From d873b6e9da7db2799fb48543ae37bfdec2196f5d Mon Sep 17 00:00:00 2001 From: huangchengxing <841396397@qq.com> Date: Sat, 16 Jul 2022 16:19:54 +0800 Subject: [PATCH 13/15] =?UTF-8?q?=E6=8F=90=E5=8F=96=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E6=B3=A8=E8=A7=A3=E5=B1=9E=E6=80=A7=E5=80=BC=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E9=80=BB=E8=BE=91=E8=87=B3=E6=B3=A8=E8=A7=A3=E5=B1=9E=E6=80=A7?= =?UTF-8?q?=E5=80=BC=E6=8F=90=E5=8F=96=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AbstractAnnotationSynthesizer.java | 20 ++++-- .../AnnotationAttributeValueProvider.java | 18 +++++ .../core/annotation/AnnotationUtil.java | 2 +- ...java => GenericSynthesizedAnnotation.java} | 25 +++++-- .../SynthesizedAggregateAnnotation.java | 7 +- .../annotation/SynthesizedAnnotation.java | 2 +- ...y.java => SynthesizedAnnotationProxy.java} | 66 +++++++++---------- .../SynthesizedMetaAggregateAnnotation.java | 12 ++-- .../AliasAnnotationPostProcessorTest.java | 7 +- .../AliasLinkAnnotationPostProcessorTest.java | 7 +- .../core/annotation/AnnotationUtilTest.java | 1 + ...sizedAnnotationAttributeProcessorTest.java | 4 ++ ...MirrorLinkAnnotationPostProcessorTest.java | 7 +- .../SynthesizedAnnotationSelectorTest.java | 5 ++ .../SyntheticMetaAnnotationTest.java | 8 +-- 15 files changed, 128 insertions(+), 63 deletions(-) create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationAttributeValueProvider.java rename hutool-core/src/main/java/cn/hutool/core/annotation/{AbstractSynthesizedAnnotation.java => GenericSynthesizedAnnotation.java} (85%) rename hutool-core/src/main/java/cn/hutool/core/annotation/{SyntheticAnnotationProxy.java => SynthesizedAnnotationProxy.java} (72%) diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractAnnotationSynthesizer.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractAnnotationSynthesizer.java index 8f7ec7d76..5433182e4 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractAnnotationSynthesizer.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractAnnotationSynthesizer.java @@ -8,6 +8,7 @@ import cn.hutool.core.util.ObjectUtil; import java.lang.annotation.Annotation; import java.util.Collection; import java.util.Comparator; +import java.util.LinkedHashMap; import java.util.Map; /** @@ -27,6 +28,11 @@ public abstract class AbstractAnnotationSynthesizer implements AnnotationSynt */ protected final Map, SynthesizedAnnotation> synthesizedAnnotationMap; + /** + * 已经合成过的注解对象 + */ + private final Map, Annotation> synthesizedProxyAnnotations; + /** * 合成注解选择器 */ @@ -38,7 +44,7 @@ public abstract class AbstractAnnotationSynthesizer implements AnnotationSynt protected final Collection postProcessors; /** - * 基于指定根注解,为其层级结构中的全部注解构造一个合成注解 + * 构造一个注解合成器 * * @param source 当前查找的注解对象 * @param annotationSelector 合成注解选择器 @@ -55,6 +61,7 @@ public abstract class AbstractAnnotationSynthesizer implements AnnotationSynt this.postProcessors = CollUtil.unmodifiable( CollUtil.sort(annotationPostProcessors, Comparator.comparing(SynthesizedAnnotationPostProcessor::order)) ); + this.synthesizedProxyAnnotations = new LinkedHashMap<>(); this.synthesizedAnnotationMap = MapUtil.unmodifiable(loadAnnotations()); annotationPostProcessors.forEach(processor -> synthesizedAnnotationMap.values().forEach(synthesized -> processor.process(synthesized, this)) @@ -136,13 +143,14 @@ public abstract class AbstractAnnotationSynthesizer implements AnnotationSynt * @param 注解类型 * @return 类型 */ + @SuppressWarnings("unchecked") @Override public A synthesize(Class annotationType) { - SynthesizedAnnotation synthesizedAnnotation = synthesizedAnnotationMap.get(annotationType); - if (ObjectUtil.isNull(synthesizedAnnotation)) { - return null; - } - return synthesize(annotationType, synthesizedAnnotation); + return (A)synthesizedProxyAnnotations.computeIfAbsent(annotationType, type -> { + final SynthesizedAnnotation synthesizedAnnotation = synthesizedAnnotationMap.get(annotationType); + return ObjectUtil.isNull(synthesizedAnnotation) ? + null : synthesize(annotationType, synthesizedAnnotation); + }); } } diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationAttributeValueProvider.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationAttributeValueProvider.java new file mode 100644 index 000000000..b127d75c4 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationAttributeValueProvider.java @@ -0,0 +1,18 @@ +package cn.hutool.core.annotation; + +/** + * 表示一个可以从当前接口的实现类中,获得特定的属性值 + */ +@FunctionalInterface +public interface AnnotationAttributeValueProvider { + + /** + * 获取注解属性值 + * + * @param attributeName 属性名称 + * @param attributeType 属性类型 + * @return 注解属性值 + */ + Object getAttributeValue(String attributeName, Class attributeType); + +} 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 b2d947111..3193e9546 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 @@ -355,7 +355,7 @@ public class AnnotationUtil { */ public static T getSynthesizedAnnotation(Annotation annotation, Class annotationType) { // TODO 缓存合成注解信息,避免重复解析 - return SynthesizedAggregateAnnotation.of(annotation).synthesize(annotationType); + return SynthesizedAggregateAnnotation.from(annotation).synthesize(annotationType); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractSynthesizedAnnotation.java b/hutool-core/src/main/java/cn/hutool/core/annotation/GenericSynthesizedAnnotation.java similarity index 85% rename from hutool-core/src/main/java/cn/hutool/core/annotation/AbstractSynthesizedAnnotation.java rename to hutool-core/src/main/java/cn/hutool/core/annotation/GenericSynthesizedAnnotation.java index a8a19c4cc..63ad83080 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractSynthesizedAnnotation.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/GenericSynthesizedAnnotation.java @@ -16,12 +16,13 @@ import java.util.stream.Stream; * {@link SynthesizedAnnotation}的基本实现 * * @param 根对象类型 + * @param 注解类型 * @author huangchengxing */ -public abstract class AbstractSynthesizedAnnotation implements Annotation, SynthesizedAnnotation { +public class GenericSynthesizedAnnotation implements Annotation, SynthesizedAnnotation { private final R root; - private final Annotation annotation; + private final T annotation; private final Map attributeMethodCaches; private final int verticalDistance; private final int horizontalDistance; @@ -34,8 +35,8 @@ public abstract class AbstractSynthesizedAnnotation implements Annotation, Sy * @param verticalDistance 距离根对象的水平距离 * @param horizontalDistance 距离根对象的垂直距离 */ - protected AbstractSynthesizedAnnotation( - R root, Annotation annotation, int verticalDistance, int horizontalDistance) { + protected GenericSynthesizedAnnotation( + R root, T annotation, int verticalDistance, int horizontalDistance) { this.root = root; this.annotation = annotation; this.verticalDistance = verticalDistance; @@ -143,7 +144,7 @@ public abstract class AbstractSynthesizedAnnotation implements Annotation, Sy * @return 注解对象 */ @Override - public Annotation getAnnotation() { + public T getAnnotation() { return annotation; } @@ -179,4 +180,18 @@ public abstract class AbstractSynthesizedAnnotation implements Annotation, Sy return annotation.annotationType(); } + /** + * 获取注解属性值 + * + * @param attributeName 属性名称 + * @param attributeType 属性类型 + * @return 注解属性值 + */ + @Override + public Object getAttributeValue(String attributeName, Class attributeType) { + return Opt.ofNullable(attributeMethodCaches.get(attributeName)) + .filter(method -> ClassUtil.isAssignable(attributeType, method.getAttributeType())) + .map(AnnotationAttribute::getValue) + .get(); + } } diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAggregateAnnotation.java b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAggregateAnnotation.java index 50ffadf7b..e6804d31e 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAggregateAnnotation.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAggregateAnnotation.java @@ -35,7 +35,7 @@ import java.lang.annotation.Annotation; * @see SynthesizedAnnotationPostProcessor * @see SynthesizedMetaAggregateAnnotation */ -public interface SynthesizedAggregateAnnotation extends AggregateAnnotation, Hierarchical, AnnotationSynthesizer { +public interface SynthesizedAggregateAnnotation extends AggregateAnnotation, Hierarchical, AnnotationSynthesizer, AnnotationAttributeValueProvider { // ================== hierarchical ================== @@ -96,7 +96,8 @@ public interface SynthesizedAggregateAnnotation extends AggregateAnnotation, Hie * @param attributeType 属性类型 * @return 属性值 */ - Object getAttribute(String attributeName, Class attributeType); + @Override + Object getAttributeValue(String attributeName, Class attributeType); /** * 基于指定根注解,构建包括其元注解在内的合成注解 @@ -105,7 +106,7 @@ public interface SynthesizedAggregateAnnotation extends AggregateAnnotation, Hie * @param 注解类型 * @return 合成注解 */ - static SynthesizedAggregateAnnotation of(T rootAnnotation) { + static SynthesizedAggregateAnnotation from(T rootAnnotation) { return new SynthesizedMetaAggregateAnnotation(rootAnnotation); } 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 b97bfbb7e..418059974 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 @@ -15,7 +15,7 @@ import java.util.function.UnaryOperator; * @author huangchengxing * @see SynthesizedAggregateAnnotation */ -public interface SynthesizedAnnotation extends Annotation, Hierarchical { +public interface SynthesizedAnnotation extends Annotation, Hierarchical, AnnotationAttributeValueProvider { /** * 获取被合成的注解对象 diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticAnnotationProxy.java b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationProxy.java similarity index 72% rename from hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticAnnotationProxy.java rename to hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationProxy.java index e5ef1beb8..60562a15d 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticAnnotationProxy.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationProxy.java @@ -19,25 +19,18 @@ import java.util.stream.Collectors; import java.util.stream.Stream; /** - * 合成注解代理类 + * 合成注解代理类,用于为{@link SynthesizedAnnotation}生成对应的合成注解代理对象 * * @author huangchengxing + * @see SynthesizedAnnotation + * @see AnnotationAttributeValueProvider */ -class SyntheticAnnotationProxy implements InvocationHandler { +public class SynthesizedAnnotationProxy implements InvocationHandler { - private final SynthesizedAggregateAnnotation aggregateAnnotation; + private final AnnotationAttributeValueProvider annotationAttributeValueProvider; private final SynthesizedAnnotation annotation; private final Map> methods; - SyntheticAnnotationProxy(SynthesizedAggregateAnnotation aggregateAnnotation, SynthesizedAnnotation annotation) { - Assert.notNull(aggregateAnnotation, "aggregateAnnotation must not null"); - Assert.notNull(annotation, "annotation must not null"); - this.aggregateAnnotation = aggregateAnnotation; - this.annotation = annotation; - this.methods = new HashMap<>(9); - loadMethods(); - } - /** * 创建一个代理注解,生成的代理对象将是{@link SyntheticProxyAnnotation}与指定的注解类的子类。 *

* - * @param annotationType 注解类型 - * @param aggregateAnnotation 合成注解 + * @param annotationType 注解类型 + * @param annotationAttributeValueProvider 注解属性值获取器 * @return 代理注解 */ @SuppressWarnings("unchecked") - static T create( - Class annotationType, SynthesizedAggregateAnnotation aggregateAnnotation, SynthesizedAnnotation annotation) { + public static T create( + Class annotationType, AnnotationAttributeValueProvider annotationAttributeValueProvider, SynthesizedAnnotation annotation) { if (ObjectUtil.isNull(annotation)) { return null; } - final SyntheticAnnotationProxy proxyHandler = new SyntheticAnnotationProxy(aggregateAnnotation, annotation); + final SynthesizedAnnotationProxy proxyHandler = new SynthesizedAnnotationProxy(annotationAttributeValueProvider, annotation); if (ObjectUtil.isNull(annotation)) { return null; } @@ -66,8 +59,23 @@ class SyntheticAnnotationProxy implements InvocationHandler { ); } - static boolean isProxyAnnotation(Class targetClass) { - return ClassUtil.isAssignable(SyntheticProxyAnnotation.class, targetClass); + /** + * 该类是否为通过{@link SynthesizedAnnotationProxy}生成的代理类 + * + * @param annotationType 注解类型 + * @return 是否 + */ + public static boolean isProxyAnnotation(Class annotationType) { + return ClassUtil.isAssignable(SyntheticProxyAnnotation.class, annotationType); + } + + SynthesizedAnnotationProxy(AnnotationAttributeValueProvider annotationAttributeValueProvider, SynthesizedAnnotation annotation) { + Assert.notNull(annotationAttributeValueProvider, "annotationAttributeValueProvider must not null"); + Assert.notNull(annotation, "annotation must not null"); + this.annotationAttributeValueProvider = annotationAttributeValueProvider; + this.annotation = annotation; + this.methods = new HashMap<>(9); + loadMethods(); } @Override @@ -82,7 +90,6 @@ class SyntheticAnnotationProxy implements InvocationHandler { void loadMethods() { methods.put("toString", (method, args) -> proxyToString()); methods.put("hashCode", (method, args) -> proxyHashCode()); - methods.put("getSynthesizedAnnotationAggregator", (method, args) -> proxyGetSynthesizedAnnotationAggregator()); methods.put("getSynthesizedAnnotation", (method, args) -> proxyGetSynthesizedAnnotation()); methods.put("getRoot", (method, args) -> annotation.getRoot()); methods.put("getVerticalDistance", (method, args) -> annotation.getVerticalDistance()); @@ -102,17 +109,15 @@ class SyntheticAnnotationProxy implements InvocationHandler { private String proxyToString() { final String attributes = Stream.of(ClassUtil.getDeclaredMethods(annotation.getAnnotation().annotationType())) .filter(AnnotationUtil::isAttributeMethod) - .map(method -> CharSequenceUtil.format("{}={}", method.getName(), aggregateAnnotation.getAttribute(method.getName(), method.getReturnType()))) + .map(method -> CharSequenceUtil.format( + "{}={}", method.getName(), proxyAttributeValue(method)) + ) .collect(Collectors.joining(", ")); return CharSequenceUtil.format("@{}({})", annotation.annotationType().getName(), attributes); } private int proxyHashCode() { - return Objects.hash(aggregateAnnotation, annotation); - } - - private Object proxyGetSynthesizedAnnotationAggregator() { - return aggregateAnnotation; + return Objects.hash(annotationAttributeValueProvider, annotation); } private Object proxyGetSynthesizedAnnotation() { @@ -120,7 +125,7 @@ class SyntheticAnnotationProxy implements InvocationHandler { } private Object proxyAttributeValue(Method attributeMethod) { - return aggregateAnnotation.getAttribute(attributeMethod.getName(), attributeMethod.getReturnType()); + return annotationAttributeValueProvider.getAttributeValue(attributeMethod.getName(), attributeMethod.getReturnType()); } /** @@ -130,13 +135,6 @@ class SyntheticAnnotationProxy implements InvocationHandler { */ interface SyntheticProxyAnnotation extends SynthesizedAnnotation { - /** - * 获取该注解所属的合成注解 - * - * @return 合成注解 - */ - SynthesizedAggregateAnnotation getSynthesizedAnnotationAggregator(); - /** * 获取该代理注解对应的已合成注解 * diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedMetaAggregateAnnotation.java b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedMetaAggregateAnnotation.java index cc6d780e4..d86938c21 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedMetaAggregateAnnotation.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedMetaAggregateAnnotation.java @@ -37,7 +37,7 @@ import java.util.Map; * * 若用户需要自行扩展,则需要保证上述三个处理器被正确注入当前实例。 * - *

{@link SynthesizedMetaAggregateAnnotation}支持通过{@link #getAttribute(String, Class)}, + *

{@link SynthesizedMetaAggregateAnnotation}支持通过{@link #getAttributeValue(String, Class)}, * 或通过{@link #synthesize(Class)}获得注解代理对象后获取指定类型的注解属性值, * 返回的属性值将根据合成注解中对应原始注解属性上的{@link Alias}与{@link Link}注解而有所变化。 * 通过当前实例获取属性值时,将经过{@link SynthesizedAnnotationAttributeProcessor}的处理。
@@ -169,7 +169,7 @@ public class SynthesizedMetaAggregateAnnotation extends AbstractAnnotationSynthe */ @Override protected Map, SynthesizedAnnotation> loadAnnotations() { - Assert.isFalse(SyntheticAnnotationProxy.isProxyAnnotation(source.getClass()), "source [{}] has been synthesized"); + Assert.isFalse(SynthesizedAnnotationProxy.isProxyAnnotation(source.getClass()), "source [{}] has been synthesized"); Map, SynthesizedAnnotation> annotationMap = new LinkedHashMap<>(); annotationMap.put(source.annotationType(), new MetaAnnotation(source, source, 0, 0)); new MetaAnnotationScanner().scan( @@ -206,7 +206,7 @@ public class SynthesizedMetaAggregateAnnotation extends AbstractAnnotationSynthe * @return 属性 */ @Override - public Object getAttribute(String attributeName, Class attributeType) { + public Object getAttributeValue(String attributeName, Class attributeType) { return attributeProcessor.getAttributeValue(attributeName, attributeType, synthesizedAnnotationMap.values()); } @@ -254,11 +254,11 @@ public class SynthesizedMetaAggregateAnnotation extends AbstractAnnotationSynthe * * @param annotationType 注解类型 * @return 合成注解对象 - * @see SyntheticAnnotationProxy#create(Class, SynthesizedAggregateAnnotation, SynthesizedAnnotation) + * @see SynthesizedAnnotationProxy#create(Class, AnnotationAttributeValueProvider, SynthesizedAnnotation) */ @Override public T synthesize(Class annotationType, SynthesizedAnnotation annotation) { - return SyntheticAnnotationProxy.create(annotationType, this, annotation); + return SynthesizedAnnotationProxy.create(annotationType, this, annotation); } /** @@ -266,7 +266,7 @@ public class SynthesizedMetaAggregateAnnotation extends AbstractAnnotationSynthe * * @author huangchengxing */ - public static class MetaAnnotation extends AbstractSynthesizedAnnotation { + public static class MetaAnnotation extends GenericSynthesizedAnnotation { /** * 创建一个合成注解 diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/AliasAnnotationPostProcessorTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/AliasAnnotationPostProcessorTest.java index b3af98d16..8675bca82 100644 --- a/hutool-core/src/test/java/cn/hutool/core/annotation/AliasAnnotationPostProcessorTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/AliasAnnotationPostProcessorTest.java @@ -108,7 +108,7 @@ public class AliasAnnotationPostProcessorTest { } @Override - public Object getAttribute(String attributeName, Class attributeType) { + public Object getAttributeValue(String attributeName, Class attributeType) { return null; } @@ -185,6 +185,11 @@ public class AliasAnnotationPostProcessorTest { public Class annotationType() { return annotation.annotationType(); } + + @Override + public Object getAttributeValue(String attributeName, Class attributeType) { + return null; + } } } diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/AliasLinkAnnotationPostProcessorTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/AliasLinkAnnotationPostProcessorTest.java index 2521b2080..040ab8aac 100644 --- a/hutool-core/src/test/java/cn/hutool/core/annotation/AliasLinkAnnotationPostProcessorTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/AliasLinkAnnotationPostProcessorTest.java @@ -136,7 +136,7 @@ public class AliasLinkAnnotationPostProcessorTest { } @Override - public Object getAttribute(String attributeName, Class attributeType) { + public Object getAttributeValue(String attributeName, Class attributeType) { return null; } @@ -213,6 +213,11 @@ public class AliasLinkAnnotationPostProcessorTest { public Class annotationType() { return annotation.annotationType(); } + + @Override + public Object getAttributeValue(String attributeName, Class attributeType) { + return null; + } } } 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 17407fbf5..6deb9ae67 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 @@ -42,6 +42,7 @@ public class AnnotationUtilTest { // 加别名适配 final AnnotationForTest annotation = AnnotationUtil.getAnnotationAlias(ClassWithAnnotation.class, AnnotationForTest.class); Assert.assertEquals("测试", annotation.retry()); + Assert.assertTrue(AnnotationUtil.isSynthesizedAnnotation(annotation)); } @AnnotationForTest("测试") diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/CacheableSynthesizedAnnotationAttributeProcessorTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/CacheableSynthesizedAnnotationAttributeProcessorTest.java index 4744866f7..facdd1263 100644 --- a/hutool-core/src/test/java/cn/hutool/core/annotation/CacheableSynthesizedAnnotationAttributeProcessorTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/CacheableSynthesizedAnnotationAttributeProcessorTest.java @@ -90,6 +90,10 @@ public class CacheableSynthesizedAnnotationAttributeProcessorTest { return null; } + @Override + public Object getAttributeValue(String attributeName, Class attributeType) { + return null; + } } } diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessorTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessorTest.java index 3b422bb8d..e65f4e611 100644 --- a/hutool-core/src/test/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessorTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/MirrorLinkAnnotationPostProcessorTest.java @@ -110,7 +110,7 @@ public class MirrorLinkAnnotationPostProcessorTest { } @Override - public Object getAttribute(String attributeName, Class attributeType) { + public Object getAttributeValue(String attributeName, Class attributeType) { return null; } @@ -188,6 +188,11 @@ public class MirrorLinkAnnotationPostProcessorTest { public Class annotationType() { return annotation.annotationType(); } + + @Override + public Object getAttributeValue(String attributeName, Class attributeType) { + return null; + } } } diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/SynthesizedAnnotationSelectorTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/SynthesizedAnnotationSelectorTest.java index 0822fdb78..80f06aeef 100644 --- a/hutool-core/src/test/java/cn/hutool/core/annotation/SynthesizedAnnotationSelectorTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/SynthesizedAnnotationSelectorTest.java @@ -136,6 +136,11 @@ public class SynthesizedAnnotationSelectorTest { public Class annotationType() { return null; } + + @Override + public Object getAttributeValue(String attributeName, Class attributeType) { + return null; + } } } 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 4b6d6c387..f073a9cfa 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 @@ -60,10 +60,10 @@ public class SyntheticMetaAnnotationTest { Assert.assertEquals(syntheticMetaAnnotation.annotationType(), SynthesizedMetaAggregateAnnotation.class); Assert.assertEquals(3, syntheticMetaAnnotation.getAnnotations().length); - Assert.assertEquals("Child!", syntheticMetaAnnotation.getAttribute("childValue", String.class)); - Assert.assertEquals("Child!", syntheticMetaAnnotation.getAttribute("childValueAlias", String.class)); - Assert.assertEquals("Child's Parent!", syntheticMetaAnnotation.getAttribute("parentValue", String.class)); - Assert.assertEquals("Child's GrandParent!", syntheticMetaAnnotation.getAttribute("grandParentValue", String.class)); + Assert.assertEquals("Child!", syntheticMetaAnnotation.getAttributeValue("childValue", String.class)); + Assert.assertEquals("Child!", syntheticMetaAnnotation.getAttributeValue("childValueAlias", String.class)); + Assert.assertEquals("Child's Parent!", syntheticMetaAnnotation.getAttributeValue("parentValue", String.class)); + Assert.assertEquals("Child's GrandParent!", syntheticMetaAnnotation.getAttributeValue("grandParentValue", String.class)); } @Test From c27c74f192a072619e6a135d08da88303b1dc1dc Mon Sep 17 00:00:00 2001 From: huangchengxing <841396397@qq.com> Date: Sat, 16 Jul 2022 18:02:02 +0800 Subject: [PATCH 14/15] =?UTF-8?q?=E5=90=88=E6=88=90=E8=81=9A=E5=90=88?= =?UTF-8?q?=E6=B3=A8=E8=A7=A3=E6=94=AF=E6=8C=81=E5=A4=84=E7=90=86=E5=A4=9A?= =?UTF-8?q?=E4=B8=AA=E6=A0=B9=E6=B3=A8=E8=A7=A3=EF=BC=8C=E5=B9=B6=E4=B8=94?= =?UTF-8?q?=E5=8F=AF=E9=80=89=E6=8B=A9=E6=98=AF=E5=90=A6=E6=89=AB=E6=8F=8F?= =?UTF-8?q?=E6=A0=B9=E6=B3=A8=E8=A7=A3=E7=9A=84=E5=85=83=E6=B3=A8=E8=A7=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AbstractAnnotationSynthesizer.java | 14 ++- .../core/annotation/AnnotationUtil.java | 22 +++- ...enericSynthesizedAggregateAnnotation.java} | 105 ++++++++++++------ .../SynthesizedAggregateAnnotation.java | 13 +-- .../SynthesizedAnnotationProxy.java | 21 +++- .../scanner/EmptyAnnotationScanner.java | 33 ++++++ ...icSynthesizedAggregateAnnotationTest.java} | 45 ++++---- 7 files changed, 176 insertions(+), 77 deletions(-) rename hutool-core/src/main/java/cn/hutool/core/annotation/{SynthesizedMetaAggregateAnnotation.java => GenericSynthesizedAggregateAnnotation.java} (70%) create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/scanner/EmptyAnnotationScanner.java rename hutool-core/src/test/java/cn/hutool/core/annotation/{SyntheticMetaAnnotationTest.java => GenericSynthesizedAggregateAnnotationTest.java} (89%) diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractAnnotationSynthesizer.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractAnnotationSynthesizer.java index 5433182e4..1b04ebdcf 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractAnnotationSynthesizer.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AbstractAnnotationSynthesizer.java @@ -1,5 +1,6 @@ package cn.hutool.core.annotation; +import cn.hutool.core.annotation.scanner.AnnotationScanner; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.map.MapUtil; @@ -43,21 +44,32 @@ public abstract class AbstractAnnotationSynthesizer implements AnnotationSynt */ protected final Collection postProcessors; + /** + * 注解扫描器 + */ + protected final AnnotationScanner annotationScanner; + /** * 构造一个注解合成器 * * @param source 当前查找的注解对象 * @param annotationSelector 合成注解选择器 * @param annotationPostProcessors 注解后置处理器 + * @param annotationScanner 注解扫描器,该扫描器需要支持扫描注解类 */ protected AbstractAnnotationSynthesizer( - T source, SynthesizedAnnotationSelector annotationSelector, Collection annotationPostProcessors) { + T source, + SynthesizedAnnotationSelector annotationSelector, + Collection annotationPostProcessors, + AnnotationScanner annotationScanner) { Assert.notNull(source, "source must not null"); Assert.notNull(annotationSelector, "annotationSelector must not null"); Assert.notNull(annotationPostProcessors, "annotationPostProcessors must not null"); + Assert.notNull(annotationPostProcessors, "annotationScanner must not null"); this.source = source; this.annotationSelector = annotationSelector; + this.annotationScanner = annotationScanner; this.postProcessors = CollUtil.unmodifiable( CollUtil.sort(annotationPostProcessors, Comparator.comparing(SynthesizedAnnotationPostProcessor::order)) ); 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 3193e9546..271fc9e8a 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 @@ -355,7 +355,7 @@ public class AnnotationUtil { */ public static T getSynthesizedAnnotation(Annotation annotation, Class annotationType) { // TODO 缓存合成注解信息,避免重复解析 - return SynthesizedAggregateAnnotation.from(annotation).synthesize(annotationType); + return aggregatingFromAnnotationWithMeta(annotation).synthesize(annotationType); } /** @@ -470,4 +470,24 @@ public class AnnotationUtil { return method.getParameterCount() == 0 && method.getReturnType() != void.class; } + /** + * 对指定注解对象进行聚合 + * + * @param annotation 注解对象 + * @return 聚合注解 + */ + static SynthesizedAggregateAnnotation aggregatingFromAnnotation(Annotation annotation) { + return new GenericSynthesizedAggregateAnnotation(Collections.singletonList(annotation), EmptyAnnotationScanner.INSTANCE); + } + + /** + * 对指定注解对象及其元注解进行聚合 + * + * @param annotation 注解对象 + * @return 聚合注解 + */ + static SynthesizedAggregateAnnotation aggregatingFromAnnotationWithMeta(Annotation annotation) { + return new GenericSynthesizedAggregateAnnotation(annotation); + } + } diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedMetaAggregateAnnotation.java b/hutool-core/src/main/java/cn/hutool/core/annotation/GenericSynthesizedAggregateAnnotation.java similarity index 70% rename from hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedMetaAggregateAnnotation.java rename to hutool-core/src/main/java/cn/hutool/core/annotation/GenericSynthesizedAggregateAnnotation.java index d86938c21..8a5f7b7b9 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedMetaAggregateAnnotation.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/GenericSynthesizedAggregateAnnotation.java @@ -1,5 +1,6 @@ package cn.hutool.core.annotation; +import cn.hutool.core.annotation.scanner.AnnotationScanner; import cn.hutool.core.annotation.scanner.MetaAnnotationScanner; import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Opt; @@ -7,16 +8,14 @@ import cn.hutool.core.util.ObjectUtil; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; -import java.util.Arrays; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.Map; +import java.util.*; /** - * {@link SynthesizedAggregateAnnotation}的基本实现,表示一个根注解与根注解上的多层元注解的聚合得到的注解 + * {@link SynthesizedAggregateAnnotation}的基本实现,表示基于多个注解对象, + * 或多个根注解对象与他们的多层元注解对象的聚合得到的注解。 * - *

假设现有注解A,A上存在元注解B,B上存在元注解C,则对注解A进行解析, - * 将得到包含根注解A,以及其元注解B、C在内的合成元注解聚合{@link SynthesizedMetaAggregateAnnotation}。 + *

假设现有注解A,若指定的{@link #annotationScanner}支持扫描注解A的元注解, + * 且A上存在元注解B,B上存在元注解C,则对注解A进行解析,将得到包含根注解A,以及其元注解B、C在内的合成元注解聚合{@link GenericSynthesizedAggregateAnnotation}。 * 从{@link AnnotatedElement}的角度来说,得到的合成注解是一个同时承载有ABC三个注解对象的被注解元素, * 因此通过调用{@link AnnotatedElement}的相关方法将返回对应符合语义的注解对象。 * @@ -37,7 +36,7 @@ import java.util.Map; * * 若用户需要自行扩展,则需要保证上述三个处理器被正确注入当前实例。 * - *

{@link SynthesizedMetaAggregateAnnotation}支持通过{@link #getAttributeValue(String, Class)}, + *

{@link GenericSynthesizedAggregateAnnotation}支持通过{@link #getAttributeValue(String, Class)}, * 或通过{@link #synthesize(Class)}获得注解代理对象后获取指定类型的注解属性值, * 返回的属性值将根据合成注解中对应原始注解属性上的{@link Alias}与{@link Link}注解而有所变化。 * 通过当前实例获取属性值时,将经过{@link SynthesizedAnnotationAttributeProcessor}的处理。
@@ -46,9 +45,16 @@ import java.util.Map; * * @author huangchengxing * @see AnnotationUtil + * @see SynthesizedAnnotationProxy * @see SynthesizedAnnotationSelector + * @see SynthesizedAnnotationAttributeProcessor + * @see SynthesizedAnnotationPostProcessor + * @see AnnotationSynthesizer + * @see AnnotationScanner */ -public class SynthesizedMetaAggregateAnnotation extends AbstractAnnotationSynthesizer implements SynthesizedAggregateAnnotation { +public class GenericSynthesizedAggregateAnnotation + extends AbstractAnnotationSynthesizer> + implements SynthesizedAggregateAnnotation { /** * 根对象 @@ -71,13 +77,25 @@ public class SynthesizedMetaAggregateAnnotation extends AbstractAnnotationSynthe private final SynthesizedAnnotationAttributeProcessor attributeProcessor; /** - * 基于指定根注解,为其层级结构中的全部注解构造一个合成注解。 + * 基于指定根注解,为其与其元注解的层级结构中的全部注解构造一个合成注解。 * 当层级结构中出现了相同的注解对象时,将优先选择以距离根注解最近,且优先被扫描的注解对象, * 当获取值时,同样遵循该规则。 * * @param source 源注解 */ - public SynthesizedMetaAggregateAnnotation(Annotation source) { + public GenericSynthesizedAggregateAnnotation(Annotation... source) { + this(Arrays.asList(source), new MetaAnnotationScanner()); + } + + /** + * 基于指定根注解,为其层级结构中的全部注解构造一个合成注解。 + * 若扫描器支持对注解的层级结构进行扫描,则若层级结构中出现了相同的注解对象时, + * 将优先选择以距离根注解最近,且优先被扫描的注解对象,并且当获取注解属性值时同样遵循该规则。 + * + * @param source 源注解 + * @param annotationScanner 注解扫描器,该扫描器必须支持扫描注解类 + */ + public GenericSynthesizedAggregateAnnotation(List source, AnnotationScanner annotationScanner) { this( source, SynthesizedAnnotationSelector.NEAREST_AND_OLDEST_PRIORITY, new CacheableSynthesizedAnnotationAttributeProcessor(), @@ -85,26 +103,29 @@ public class SynthesizedMetaAggregateAnnotation extends AbstractAnnotationSynthe SynthesizedAnnotationPostProcessor.ALIAS_ANNOTATION_POST_PROCESSOR, SynthesizedAnnotationPostProcessor.MIRROR_LINK_ANNOTATION_POST_PROCESSOR, SynthesizedAnnotationPostProcessor.ALIAS_LINK_ANNOTATION_POST_PROCESSOR - ) + ), + annotationScanner ); } /** * 基于指定根注解,为其层级结构中的全部注解构造一个合成注解 * - * @param annotation 当前查找的注解对象 + * @param source 当前查找的注解对象 * @param annotationSelector 合成注解选择器 * @param attributeProcessor 注解属性处理器 * @param annotationPostProcessors 注解后置处理器 + * @param annotationScanner 注解扫描器,该扫描器必须支持扫描注解类 */ - public SynthesizedMetaAggregateAnnotation( - Annotation annotation, + public GenericSynthesizedAggregateAnnotation( + List source, SynthesizedAnnotationSelector annotationSelector, SynthesizedAnnotationAttributeProcessor attributeProcessor, - Collection annotationPostProcessors) { + Collection annotationPostProcessors, + AnnotationScanner annotationScanner) { this( null, 0, 0, - annotation, annotationSelector, attributeProcessor, annotationPostProcessors + source, annotationSelector, attributeProcessor, annotationPostProcessors, annotationScanner ); } @@ -114,18 +135,20 @@ public class SynthesizedMetaAggregateAnnotation extends AbstractAnnotationSynthe * @param root 根对象 * @param verticalDistance 距离根对象的水平距离 * @param horizontalDistance 距离根对象的垂直距离 - * @param source 当前查找的注解对象 + * @param source 当前查找的注解对象 * @param annotationSelector 合成注解选择器 * @param attributeProcessor 注解属性处理器 * @param annotationPostProcessors 注解后置处理器 + * @param annotationScanner 注解扫描器,该扫描器必须支持扫描注解类 */ - SynthesizedMetaAggregateAnnotation( + GenericSynthesizedAggregateAnnotation( Object root, int verticalDistance, int horizontalDistance, - Annotation source, + List source, SynthesizedAnnotationSelector annotationSelector, SynthesizedAnnotationAttributeProcessor attributeProcessor, - Collection annotationPostProcessors) { - super(source, annotationSelector, annotationPostProcessors); + Collection annotationPostProcessors, + AnnotationScanner annotationScanner) { + super(source, annotationSelector, annotationPostProcessors, annotationScanner); Assert.notNull(attributeProcessor, "attributeProcessor must not null"); this.root = ObjectUtil.defaultIfNull(root, this); @@ -169,21 +192,31 @@ public class SynthesizedMetaAggregateAnnotation extends AbstractAnnotationSynthe */ @Override protected Map, SynthesizedAnnotation> loadAnnotations() { - Assert.isFalse(SynthesizedAnnotationProxy.isProxyAnnotation(source.getClass()), "source [{}] has been synthesized"); Map, SynthesizedAnnotation> annotationMap = new LinkedHashMap<>(); - annotationMap.put(source.annotationType(), new MetaAnnotation(source, source, 0, 0)); - new MetaAnnotationScanner().scan( - (index, annotation) -> { - SynthesizedAnnotation oldAnnotation = annotationMap.get(annotation.annotationType()); - SynthesizedAnnotation newAnnotation = new MetaAnnotation(source, annotation, index, annotationMap.size()); - if (ObjectUtil.isNull(oldAnnotation)) { - annotationMap.put(annotation.annotationType(), newAnnotation); - } else { - annotationMap.put(annotation.annotationType(), annotationSelector.choose(oldAnnotation, newAnnotation)); - } - }, - source.annotationType(), null - ); + + // 根注解默认水平坐标为0,根注解的元注解坐标从1开始 + for (int i = 0; i < source.size(); i++) { + final Annotation sourceAnnotation = source.get(i); + Assert.isFalse(AnnotationUtil.isSynthesizedAnnotation(sourceAnnotation), "source [{}] has been synthesized"); + annotationMap.put(sourceAnnotation.annotationType(), new MetaAnnotation(sourceAnnotation, sourceAnnotation, 0, i)); + Assert.isTrue( + annotationScanner.support(sourceAnnotation.annotationType()), + "annotation scanner [{}] cannot support scan [{}]", + annotationScanner, sourceAnnotation.annotationType() + ); + annotationScanner.scan( + (index, annotation) -> { + SynthesizedAnnotation oldAnnotation = annotationMap.get(annotation.annotationType()); + SynthesizedAnnotation newAnnotation = new MetaAnnotation(sourceAnnotation, annotation, index + 1, annotationMap.size()); + if (ObjectUtil.isNull(oldAnnotation)) { + annotationMap.put(annotation.annotationType(), newAnnotation); + } else { + annotationMap.put(annotation.annotationType(), annotationSelector.choose(oldAnnotation, newAnnotation)); + } + }, + sourceAnnotation.annotationType(), null + ); + } return annotationMap; } diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAggregateAnnotation.java b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAggregateAnnotation.java index e6804d31e..e006f3f08 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAggregateAnnotation.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAggregateAnnotation.java @@ -33,7 +33,7 @@ import java.lang.annotation.Annotation; * @see SynthesizedAnnotationSelector * @see SynthesizedAnnotationAttributeProcessor * @see SynthesizedAnnotationPostProcessor - * @see SynthesizedMetaAggregateAnnotation + * @see GenericSynthesizedAggregateAnnotation */ public interface SynthesizedAggregateAnnotation extends AggregateAnnotation, Hierarchical, AnnotationSynthesizer, AnnotationAttributeValueProvider { @@ -99,15 +99,4 @@ public interface SynthesizedAggregateAnnotation extends AggregateAnnotation, Hie @Override Object getAttributeValue(String attributeName, Class attributeType); - /** - * 基于指定根注解,构建包括其元注解在内的合成注解 - * - * @param rootAnnotation 根注解 - * @param 注解类型 - * @return 合成注解 - */ - static SynthesizedAggregateAnnotation from(T rootAnnotation) { - return new SynthesizedMetaAggregateAnnotation(rootAnnotation); - } - } diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationProxy.java b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationProxy.java index 60562a15d..1dd7c89b7 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationProxy.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationProxy.java @@ -33,18 +33,17 @@ public class SynthesizedAnnotationProxy implements InvocationHandler { /** * 创建一个代理注解,生成的代理对象将是{@link SyntheticProxyAnnotation}与指定的注解类的子类。 - *

    - *
  • 当作为{@code annotationType}所指定的类型使用时,其属性将通过合成它的{@link SynthesizedAggregateAnnotation}获取;
  • - *
  • 当作为{@link SyntheticProxyAnnotation}或{@link SynthesizedAnnotation}使用时,将可以获得原始注解实例的相关信息;
  • - *
* * @param annotationType 注解类型 * @param annotationAttributeValueProvider 注解属性值获取器 + * @param annotation 合成注解 * @return 代理注解 */ @SuppressWarnings("unchecked") public static T create( - Class annotationType, AnnotationAttributeValueProvider annotationAttributeValueProvider, SynthesizedAnnotation annotation) { + Class annotationType, + AnnotationAttributeValueProvider annotationAttributeValueProvider, + SynthesizedAnnotation annotation) { if (ObjectUtil.isNull(annotation)) { return null; } @@ -59,6 +58,18 @@ public class SynthesizedAnnotationProxy implements InvocationHandler { ); } + /** + * 创建一个代理注解,生成的代理对象将是{@link SyntheticProxyAnnotation}与指定的注解类的子类。 + * + * @param annotationType 注解类型 + * @param annotation 合成注解 + * @return 代理注解 + */ + public static T create( + Class annotationType, SynthesizedAnnotation annotation) { + return create(annotationType, annotation, annotation); + } + /** * 该类是否为通过{@link SynthesizedAnnotationProxy}生成的代理类 * diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/EmptyAnnotationScanner.java b/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/EmptyAnnotationScanner.java new file mode 100644 index 000000000..62dc560fa --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/scanner/EmptyAnnotationScanner.java @@ -0,0 +1,33 @@ +package cn.hutool.core.annotation.scanner; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; +import java.util.Collections; +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Predicate; + +/** + * 默认不扫描任何元素的扫描器 + * + * @author huangchengxing + */ +public class EmptyAnnotationScanner implements AnnotationScanner { + + public static final EmptyAnnotationScanner INSTANCE = new EmptyAnnotationScanner(); + + @Override + public boolean support(AnnotatedElement annotatedEle) { + return true; + } + + @Override + public List getAnnotations(AnnotatedElement annotatedEle) { + return Collections.emptyList(); + } + + @Override + public void scan(BiConsumer consumer, AnnotatedElement annotatedEle, Predicate filter) { + // do nothing + } +} diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/SyntheticMetaAnnotationTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/GenericSynthesizedAggregateAnnotationTest.java similarity index 89% rename from hutool-core/src/test/java/cn/hutool/core/annotation/SyntheticMetaAnnotationTest.java rename to hutool-core/src/test/java/cn/hutool/core/annotation/GenericSynthesizedAggregateAnnotationTest.java index f073a9cfa..c59b9d6bd 100644 --- a/hutool-core/src/test/java/cn/hutool/core/annotation/SyntheticMetaAnnotationTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/GenericSynthesizedAggregateAnnotationTest.java @@ -10,13 +10,14 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Method; import java.util.Arrays; +import java.util.Collections; /** - * 合成注解{@link SynthesizedMetaAggregateAnnotation}的测试用例 + * 合成注解{@link GenericSynthesizedAggregateAnnotation}的测试用例 * * @author huangchengxing */ -public class SyntheticMetaAnnotationTest { +public class GenericSynthesizedAggregateAnnotationTest { @Test public void baseSynthesisAnnotationWorkTest() { @@ -25,10 +26,10 @@ public class SyntheticMetaAnnotationTest { final GrandParentAnnotation grandParentAnnotation = ChildAnnotation.class.getAnnotation(GrandParentAnnotation.class); final ParentAnnotation parentAnnotation = ChildAnnotation.class.getAnnotation(ParentAnnotation.class); final ChildAnnotation childAnnotation = AnnotatedClass.class.getAnnotation(ChildAnnotation.class); - final SynthesizedMetaAggregateAnnotation syntheticMetaAnnotation = new SynthesizedMetaAggregateAnnotation(childAnnotation); + final GenericSynthesizedAggregateAnnotation syntheticMetaAnnotation = new GenericSynthesizedAggregateAnnotation(childAnnotation); // Annotation & AnnotatedElement - Assert.assertEquals(SynthesizedMetaAggregateAnnotation.class, syntheticMetaAnnotation.annotationType()); + Assert.assertEquals(GenericSynthesizedAggregateAnnotation.class, syntheticMetaAnnotation.annotationType()); Assert.assertTrue(syntheticMetaAnnotation.isAnnotationPresent(GrandParentAnnotation.class)); Assert.assertTrue(syntheticMetaAnnotation.isAnnotationPresent(ParentAnnotation.class)); Assert.assertTrue(syntheticMetaAnnotation.isAnnotationPresent(ChildAnnotation.class)); @@ -55,9 +56,9 @@ public class SyntheticMetaAnnotationTest { @Test public void synthesisAnnotationAttributeTest() { final ChildAnnotation rootAnnotation = AnnotatedClass.class.getAnnotation(ChildAnnotation.class); - SynthesizedMetaAggregateAnnotation syntheticMetaAnnotation = new SynthesizedMetaAggregateAnnotation(rootAnnotation); - Assert.assertEquals(syntheticMetaAnnotation.getSource(), rootAnnotation); - Assert.assertEquals(syntheticMetaAnnotation.annotationType(), SynthesizedMetaAggregateAnnotation.class); + GenericSynthesizedAggregateAnnotation syntheticMetaAnnotation = new GenericSynthesizedAggregateAnnotation(rootAnnotation); + Assert.assertEquals(syntheticMetaAnnotation.getSource(), Collections.singletonList(rootAnnotation)); + Assert.assertEquals(syntheticMetaAnnotation.annotationType(), GenericSynthesizedAggregateAnnotation.class); Assert.assertEquals(3, syntheticMetaAnnotation.getAnnotations().length); Assert.assertEquals("Child!", syntheticMetaAnnotation.getAttributeValue("childValue", String.class)); @@ -69,7 +70,7 @@ public class SyntheticMetaAnnotationTest { @Test public void syntheticAnnotationTest() { final ChildAnnotation rootAnnotation = AnnotatedClass.class.getAnnotation(ChildAnnotation.class); - SynthesizedMetaAggregateAnnotation syntheticMetaAnnotation = new SynthesizedMetaAggregateAnnotation(rootAnnotation); + GenericSynthesizedAggregateAnnotation syntheticMetaAnnotation = new GenericSynthesizedAggregateAnnotation(rootAnnotation); final ChildAnnotation childAnnotation = syntheticMetaAnnotation.synthesize(ChildAnnotation.class); SynthesizedAnnotation childSyntheticAnnotation = syntheticMetaAnnotation.getSynthesizedAnnotation(ChildAnnotation.class); @@ -82,7 +83,7 @@ public class SyntheticMetaAnnotationTest { Assert.assertEquals("Child!", childAnnotation.childValue()); Assert.assertEquals("Child!", childAnnotation.childValueAlias()); Assert.assertEquals(childAnnotation.grandParentType(), Integer.class); - Assert.assertThrows(IllegalArgumentException.class, () -> new SynthesizedMetaAggregateAnnotation(childAnnotation)); + Assert.assertThrows(IllegalArgumentException.class, () -> new GenericSynthesizedAggregateAnnotation(childAnnotation)); final ParentAnnotation parentAnnotation = syntheticMetaAnnotation.synthesize(ParentAnnotation.class); SynthesizedAnnotation parentSyntheticAnnotation = syntheticMetaAnnotation.getSynthesizedAnnotation(ParentAnnotation.class); @@ -93,7 +94,7 @@ public class SyntheticMetaAnnotationTest { Assert.assertNotNull(parentAnnotation); Assert.assertEquals("Child's Parent!", parentAnnotation.parentValue()); Assert.assertEquals("java.lang.Void", parentAnnotation.grandParentType()); - Assert.assertThrows(IllegalArgumentException.class, () -> new SynthesizedMetaAggregateAnnotation(parentAnnotation)); + Assert.assertThrows(IllegalArgumentException.class, () -> new GenericSynthesizedAggregateAnnotation(parentAnnotation)); final GrandParentAnnotation grandParentAnnotation = syntheticMetaAnnotation.synthesize(GrandParentAnnotation.class); SynthesizedAnnotation grandParentSyntheticAnnotation = syntheticMetaAnnotation.getSynthesizedAnnotation(GrandParentAnnotation.class); @@ -105,13 +106,13 @@ public class SyntheticMetaAnnotationTest { Assert.assertNotNull(grandParentAnnotation); Assert.assertEquals("Child's GrandParent!", grandParentAnnotation.grandParentValue()); Assert.assertEquals(grandParentAnnotation.grandParentType(), Integer.class); - Assert.assertThrows(IllegalArgumentException.class, () -> new SynthesizedMetaAggregateAnnotation(grandParentAnnotation)); + Assert.assertThrows(IllegalArgumentException.class, () -> new GenericSynthesizedAggregateAnnotation(grandParentAnnotation)); } @Test public void linkTest() { final Method method = ReflectUtil.getMethod(AnnotationForLinkTest.class, "value"); - final SynthesizedAggregateAnnotation synthesizedAnnotationAggregator = new SynthesizedMetaAggregateAnnotation(method.getAnnotation(AliasFor.class)); + final SynthesizedAggregateAnnotation synthesizedAnnotationAggregator = new GenericSynthesizedAggregateAnnotation(method.getAnnotation(AliasFor.class)); final Link link = synthesizedAnnotationAggregator.synthesize(Link.class); Assert.assertEquals(AnnotationForLinkTest.class, link.annotation()); Assert.assertEquals("name", link.attribute()); @@ -120,19 +121,19 @@ public class SyntheticMetaAnnotationTest { @Test public void mirrorAttributeTest() { AnnotationForMirrorTest annotation = ClassForMirrorTest.class.getAnnotation(AnnotationForMirrorTest.class); - SynthesizedAggregateAnnotation synthetic = new SynthesizedMetaAggregateAnnotation(annotation); + SynthesizedAggregateAnnotation synthetic = new GenericSynthesizedAggregateAnnotation(annotation); AnnotationForMirrorTest syntheticAnnotation = synthetic.synthesize(AnnotationForMirrorTest.class); Assert.assertEquals("Foo", syntheticAnnotation.name()); Assert.assertEquals("Foo", syntheticAnnotation.value()); annotation = ClassForMirrorTest2.class.getAnnotation(AnnotationForMirrorTest.class); - synthetic = new SynthesizedMetaAggregateAnnotation(annotation); + synthetic = new GenericSynthesizedAggregateAnnotation(annotation); syntheticAnnotation = synthetic.synthesize(AnnotationForMirrorTest.class); Assert.assertEquals("Foo", syntheticAnnotation.name()); Assert.assertEquals("Foo", syntheticAnnotation.value()); annotation = ClassForMirrorTest3.class.getAnnotation(AnnotationForMirrorTest.class); - synthetic = new SynthesizedMetaAggregateAnnotation(annotation); + synthetic = new GenericSynthesizedAggregateAnnotation(annotation); syntheticAnnotation = synthetic.synthesize(AnnotationForMirrorTest.class); AnnotationForMirrorTest finalSyntheticAnnotation = syntheticAnnotation; Assert.assertThrows(IllegalArgumentException.class, finalSyntheticAnnotation::name); @@ -141,14 +142,14 @@ public class SyntheticMetaAnnotationTest { @Test public void aliasForTest() { AnnotationForAliasForTest annotation = ClassForAliasForTest.class.getAnnotation(AnnotationForAliasForTest.class); - SynthesizedAggregateAnnotation synthetic = new SynthesizedMetaAggregateAnnotation(annotation); + SynthesizedAggregateAnnotation synthetic = new GenericSynthesizedAggregateAnnotation(annotation); MetaAnnotationForAliasForTest metaAnnotation = synthetic.synthesize(MetaAnnotationForAliasForTest.class); Assert.assertEquals("Meta", metaAnnotation.name()); AnnotationForAliasForTest childAnnotation = synthetic.synthesize(AnnotationForAliasForTest.class); Assert.assertEquals("", childAnnotation.value()); annotation = ClassForAliasForTest2.class.getAnnotation(AnnotationForAliasForTest.class); - synthetic = new SynthesizedMetaAggregateAnnotation(annotation); + synthetic = new GenericSynthesizedAggregateAnnotation(annotation); metaAnnotation = synthetic.synthesize(MetaAnnotationForAliasForTest.class); Assert.assertEquals("Foo", metaAnnotation.name()); childAnnotation = synthetic.synthesize(AnnotationForAliasForTest.class); @@ -158,14 +159,14 @@ public class SyntheticMetaAnnotationTest { @Test public void forceAliasForTest() { AnnotationForceForAliasForTest annotation = ClassForForceAliasForTest.class.getAnnotation(AnnotationForceForAliasForTest.class); - SynthesizedAggregateAnnotation synthetic = new SynthesizedMetaAggregateAnnotation(annotation); + SynthesizedAggregateAnnotation synthetic = new GenericSynthesizedAggregateAnnotation(annotation); MetaAnnotationForForceAliasForTest metaAnnotation = synthetic.synthesize(MetaAnnotationForForceAliasForTest.class); Assert.assertEquals("", metaAnnotation.name()); AnnotationForceForAliasForTest childAnnotation = synthetic.synthesize(AnnotationForceForAliasForTest.class); Assert.assertEquals("", childAnnotation.value()); annotation = ClassForForceAliasForTest2.class.getAnnotation(AnnotationForceForAliasForTest.class); - synthetic = new SynthesizedMetaAggregateAnnotation(annotation); + synthetic = new GenericSynthesizedAggregateAnnotation(annotation); metaAnnotation = synthetic.synthesize(MetaAnnotationForForceAliasForTest.class); Assert.assertEquals("Foo", metaAnnotation.name()); childAnnotation = synthetic.synthesize(AnnotationForceForAliasForTest.class); @@ -175,7 +176,7 @@ public class SyntheticMetaAnnotationTest { @Test public void aliasForAndMirrorTest() { AnnotationForMirrorThenAliasForTest annotation = ClassForAliasForAndMirrorTest.class.getAnnotation(AnnotationForMirrorThenAliasForTest.class); - SynthesizedAggregateAnnotation synthetic = new SynthesizedMetaAggregateAnnotation(annotation); + SynthesizedAggregateAnnotation synthetic = new GenericSynthesizedAggregateAnnotation(annotation); MetaAnnotationForMirrorThenAliasForTest metaAnnotation = synthetic.synthesize(MetaAnnotationForMirrorThenAliasForTest.class); Assert.assertEquals("test", metaAnnotation.name()); Assert.assertEquals("test", metaAnnotation.value()); @@ -186,7 +187,7 @@ public class SyntheticMetaAnnotationTest { @Test public void multiAliasForTest() { final AnnotationForMultiAliasForTest annotation = ClassForMultiAliasForTest.class.getAnnotation(AnnotationForMultiAliasForTest.class); - final SynthesizedAggregateAnnotation synthetic = new SynthesizedMetaAggregateAnnotation(annotation); + final SynthesizedAggregateAnnotation synthetic = new GenericSynthesizedAggregateAnnotation(annotation); final MetaAnnotationForMultiAliasForTest1 metaAnnotation1 = synthetic.synthesize(MetaAnnotationForMultiAliasForTest1.class); Assert.assertEquals("test", metaAnnotation1.name()); @@ -200,7 +201,7 @@ public class SyntheticMetaAnnotationTest { @Test public void implicitAliasTest() { final AnnotationForImplicitAliasTest annotation = ClassForImplicitAliasTest.class.getAnnotation(AnnotationForImplicitAliasTest.class); - final SynthesizedAggregateAnnotation synthetic = new SynthesizedMetaAggregateAnnotation(annotation); + final SynthesizedAggregateAnnotation synthetic = new GenericSynthesizedAggregateAnnotation(annotation); final MetaAnnotationForImplicitAliasTest metaAnnotation = synthetic.synthesize(MetaAnnotationForImplicitAliasTest.class); Assert.assertEquals("Meta", metaAnnotation.name()); From 4e5cc6c5d0f879c6d0aecadec786f14e1a1e9b0b Mon Sep 17 00:00:00 2001 From: huangchengxing <841396397@qq.com> Date: Sat, 16 Jul 2022 18:37:33 +0800 Subject: [PATCH 15/15] add methods and comment --- .../core/annotation/AnnotationUtil.java | 288 +++++++++++------- .../GenericSynthesizedAnnotation.java | 2 +- 2 files changed, 175 insertions(+), 115 deletions(-) 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 271fc9e8a..0c3e70e1a 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 @@ -3,6 +3,7 @@ package cn.hutool.core.annotation; import cn.hutool.core.annotation.scanner.*; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.exceptions.UtilException; +import cn.hutool.core.lang.Opt; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ReflectUtil; @@ -315,104 +316,19 @@ public class AnnotationUtil { return annotationType.isAnnotationPresent(Inherited.class); } - /** - * 设置新的注解的属性(字段)值 - * - * @param annotation 注解对象 - * @param annotationField 注解属性(字段)名称 - * @param value 要更新的属性值 - * @since 5.5.2 - */ - @SuppressWarnings({"rawtypes", "unchecked"}) - public static void setValue(Annotation annotation, String annotationField, Object value) { - final Map memberValues = (Map) ReflectUtil.getFieldValue(Proxy.getInvocationHandler(annotation), "memberValues"); - memberValues.put(annotationField, value); - } - - /** - * 获取别名支持后的注解 - * - * @param annotationEle 被注解的类 - * @param annotationType 注解类型Class - * @param 注解类型 - * @return 别名支持后的注解 - * @since 5.7.23 - */ - @SuppressWarnings("unchecked") - public static T getAnnotationAlias(AnnotatedElement annotationEle, Class annotationType) { - final T annotation = getAnnotation(annotationEle, annotationType); - return (T) Proxy.newProxyInstance(annotationType.getClassLoader(), new Class[]{annotationType}, new AnnotationProxy<>(annotation)); - } - - /** - * 将指定注解实例与其元注解转为合成注解 - * - * @param annotation 注解对象 - * @param annotationType 注解类 - * @param 注解类型 - * @return 合成注解 - * @see SynthesizedAggregateAnnotation - */ - public static T getSynthesizedAnnotation(Annotation annotation, Class annotationType) { - // TODO 缓存合成注解信息,避免重复解析 - return aggregatingFromAnnotationWithMeta(annotation).synthesize(annotationType); - } - - /** - * 获取元素上距离指定元素最接近的合成注解 - *
    - *
  • 若元素是类,则递归解析全部父类和全部父接口上的注解;
  • - *
  • 若元素是方法、属性或注解,则只解析其直接声明的注解;
  • - *
- * - * @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission - * @param annotationType 注解类 - * @param 注解类型 - * @return 合成注解 - * @see SynthesizedAggregateAnnotation - */ - public static T getSynthesizedAnnotation(AnnotatedElement annotatedEle, Class annotationType) { - T target = annotatedEle.getAnnotation(annotationType); - if (ObjectUtil.isNotNull(target)) { - return target; - } - AnnotationScanner[] scanners = new AnnotationScanner[]{ - new MetaAnnotationScanner(), new TypeAnnotationScanner(), new MethodAnnotationScanner(), new FieldAnnotationScanner() - }; - return AnnotationScanner.scanByAnySupported(annotatedEle, scanners).stream() - .map(annotation -> getSynthesizedAnnotation(annotation, annotationType)) - .filter(Objects::nonNull) - .findFirst() - .orElse(null); - } - - /** - * 获取元素上所有指定注解 - *
    - *
  • 若元素是类,则递归解析全部父类和全部父接口上的注解;
  • - *
  • 若元素是方法、属性或注解,则只解析其直接声明的注解;
  • - *
- * - * @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission - * @param annotationType 注解类 - * @param 注解类型 - * @return 合成注解 - * @see SynthesizedAggregateAnnotation - */ - public static List getAllSynthesizedAnnotations(AnnotatedElement annotatedEle, Class annotationType) { - AnnotationScanner[] scanners = new AnnotationScanner[]{ - new MetaAnnotationScanner(), new TypeAnnotationScanner(), new MethodAnnotationScanner(), new FieldAnnotationScanner() - }; - return AnnotationScanner.scanByAnySupported(annotatedEle, scanners).stream() - .map(annotation -> getSynthesizedAnnotation(annotation, annotationType)) - .filter(Objects::nonNull) - .collect(Collectors.toList()); - } - /** * 扫描注解类,以及注解类的{@link Class}层级结构中的注解,将返回除了{@link #META_ANNOTATIONS}中指定的JDK默认注解外, * 按元注解对象与{@code annotationType}的距离和{@link Class#getAnnotations()}顺序排序的注解对象集合 * + *

比如:
+ * 若{@code annotationType}为 A,且A存在元注解B,B又存在元注解C和D,则有: + *

+	 *                              |-> C.class [@a, @b]
+	 *     A.class -> B.class [@a] -|
+	 *                              |-> D.class [@a, @c]
+	 * 
+ * 扫描A,则该方法最终将返回 {@code [@a, @a, @b, @a, @c]} + * * @param annotationType 注解类 * @return 注解对象集合 * @see MetaAnnotationScanner @@ -432,6 +348,16 @@ public class AnnotationUtil { * * 注解根据其声明类/接口被扫描的顺序排序,若注解都在同一个{@link Class}中被声明,则还会遵循{@link Class#getAnnotations()}的顺序。 * + *

比如:
+ * 若{@code targetClass}为{@code A.class},且{@code A.class}存在父类{@code B.class}、父接口{@code C.class}, + * 三个类的注解声明情况如下: + *

+	 *                   |-> B.class [@a, @b]
+	 *     A.class [@a] -|
+	 *                   |-> C.class [@a, @c]
+	 * 
+ * 则该方法最终将返回 {@code [@a, @a, @b, @a, @c]} + * * @param targetClass 类 * @return 注解对象集合 * @see TypeAnnotationScanner @@ -452,6 +378,14 @@ public class AnnotationUtil { * * 方法上的注解根据方法的声明类/接口被扫描的顺序排序,若注解都在同一个类的同一个方法中被声明,则还会遵循{@link Method#getAnnotations()}的顺序。 * + *

比如:
+ * 若方法X声明于{@code A.class},且重载/重写自父类{@code B.class},并且父类中的方法X由重写至其实现的接口{@code C.class}, + * 三个类的注解声明情况如下: + *

+	 *     A#X()[@a] -> B#X()[@b] -> C#X()[@c]
+	 * 
+ * 则该方法最终将返回 {@code [@a, @b, @c]} + * * @param method 方法 * @return 注解对象集合 * @see MethodAnnotationScanner @@ -460,6 +394,152 @@ public class AnnotationUtil { return new MethodAnnotationScanner(true).getIfSupport(method); } + /** + * 设置新的注解的属性(字段)值 + * + * @param annotation 注解对象 + * @param annotationField 注解属性(字段)名称 + * @param value 要更新的属性值 + * @since 5.5.2 + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + public static void setValue(Annotation annotation, String annotationField, Object value) { + final Map memberValues = (Map) ReflectUtil.getFieldValue(Proxy.getInvocationHandler(annotation), "memberValues"); + memberValues.put(annotationField, value); + } + + /** + * 该注解对象是否为通过代理类生成的合成注解 + * + * @param annotation 注解对象 + * @return 是否 + * @see SynthesizedAnnotationProxy#isProxyAnnotation(Class) + */ + public static boolean isSynthesizedAnnotation(Annotation annotation) { + return SynthesizedAnnotationProxy.isProxyAnnotation(annotation.getClass()); + } + + /** + * 获取别名支持后的注解 + * + * @param annotationEle 被注解的类 + * @param annotationType 注解类型Class + * @param 注解类型 + * @return 别名支持后的注解 + * @since 5.7.23 + */ + public static T getAnnotationAlias(AnnotatedElement annotationEle, Class annotationType) { + final T annotation = getAnnotation(annotationEle, annotationType); + return aggregatingFromAnnotation(annotation).synthesize(annotationType); + } + + /** + * 将指定注解实例与其元注解转为合成注解 + * + * @param annotationType 注解类 + * @param annotations 注解对象 + * @param 注解类型 + * @return 合成注解 + * @see SynthesizedAggregateAnnotation + */ + public static T getSynthesizedAnnotation(Class annotationType, Annotation... annotations) { + // TODO 缓存合成注解信息,避免重复解析 + return Opt.ofNullable(annotations) + .filter(ArrayUtil::isNotEmpty) + .map(AnnotationUtil::aggregatingFromAnnotationWithMeta) + .map(a -> a.synthesize(annotationType)) + .get(); + } + + /** + *

获取元素上距离指定元素最接近的合成注解 + *

    + *
  • 若元素是类,则递归解析全部父类和全部父接口上的注解;
  • + *
  • 若元素是方法、属性或注解,则只解析其直接声明的注解;
  • + *
+ * + *

注解合成规则如下: + * 若{@code AnnotatedEle}按顺序从上到下声明了A,B,C三个注解,且三注解存在元注解如下: + *

+	 *    A -> MA1 -> MA2
+	 *    B -> MB1 -> MB2
+	 *    C -> MC1
+	 * 
+ * 此时入参{@code annotationType}类型为{@code MB1},则最终将优先返回基于根注解B合成的合成注解 + * + * @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission + * @param annotationType 注解类 + * @param 注解类型 + * @return 合成注解 + * @see SynthesizedAggregateAnnotation + */ + public static T getSynthesizedAnnotation(AnnotatedElement annotatedEle, Class annotationType) { + T target = annotatedEle.getAnnotation(annotationType); + if (ObjectUtil.isNotNull(target)) { + return target; + } + AnnotationScanner[] scanners = new AnnotationScanner[]{ + new MetaAnnotationScanner(), new TypeAnnotationScanner(), new MethodAnnotationScanner(), new FieldAnnotationScanner() + }; + return AnnotationScanner.scanByAnySupported(annotatedEle, scanners).stream() + .map(annotation -> getSynthesizedAnnotation(annotationType, annotation)) + .filter(Objects::nonNull) + .findFirst() + .orElse(null); + } + + /** + * 获取元素上所有指定注解 + *
    + *
  • 若元素是类,则递归解析全部父类和全部父接口上的注解;
  • + *
  • 若元素是方法、属性或注解,则只解析其直接声明的注解;
  • + *
+ * + *

注解合成规则如下: + * 若{@code AnnotatedEle}按顺序从上到下声明了A,B,C三个注解,且三注解存在元注解如下: + *

+	 *    A -> M1 -> M2
+	 *    B -> M3 -> M1
+	 *    C -> M2
+	 * 
+ * 此时入参{@code annotationType}类型为{@code M1},则最终将返回基于根注解A与根注解B合成的合成注解。 + * + * @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission + * @param annotationType 注解类 + * @param 注解类型 + * @return 合成注解 + * @see SynthesizedAggregateAnnotation + */ + public static List getAllSynthesizedAnnotations(AnnotatedElement annotatedEle, Class annotationType) { + AnnotationScanner[] scanners = new AnnotationScanner[]{ + new MetaAnnotationScanner(), new TypeAnnotationScanner(), new MethodAnnotationScanner(), new FieldAnnotationScanner() + }; + return AnnotationScanner.scanByAnySupported(annotatedEle, scanners).stream() + .map(annotation -> getSynthesizedAnnotation(annotationType, annotation)) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + + /** + * 对指定注解对象进行聚合 + * + * @param annotations 注解对象 + * @return 聚合注解 + */ + public static SynthesizedAggregateAnnotation aggregatingFromAnnotation(Annotation... annotations) { + return new GenericSynthesizedAggregateAnnotation(Arrays.asList(annotations), EmptyAnnotationScanner.INSTANCE); + } + + /** + * 对指定注解对象及其元注解进行聚合 + * + * @param annotations 注解对象 + * @return 聚合注解 + */ + public static SynthesizedAggregateAnnotation aggregatingFromAnnotationWithMeta(Annotation... annotations) { + return new GenericSynthesizedAggregateAnnotation(Arrays.asList(annotations), new MetaAnnotationScanner()); + } + /** * 方法是否为注解属性方法。
* 方法无参数,且有返回值的方法认为是注解属性的方法。 @@ -470,24 +550,4 @@ public class AnnotationUtil { return method.getParameterCount() == 0 && method.getReturnType() != void.class; } - /** - * 对指定注解对象进行聚合 - * - * @param annotation 注解对象 - * @return 聚合注解 - */ - static SynthesizedAggregateAnnotation aggregatingFromAnnotation(Annotation annotation) { - return new GenericSynthesizedAggregateAnnotation(Collections.singletonList(annotation), EmptyAnnotationScanner.INSTANCE); - } - - /** - * 对指定注解对象及其元注解进行聚合 - * - * @param annotation 注解对象 - * @return 聚合注解 - */ - static SynthesizedAggregateAnnotation aggregatingFromAnnotationWithMeta(Annotation annotation) { - return new GenericSynthesizedAggregateAnnotation(annotation); - } - } diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/GenericSynthesizedAnnotation.java b/hutool-core/src/main/java/cn/hutool/core/annotation/GenericSynthesizedAnnotation.java index 63ad83080..303424995 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/GenericSynthesizedAnnotation.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/GenericSynthesizedAnnotation.java @@ -19,7 +19,7 @@ import java.util.stream.Stream; * @param 注解类型 * @author huangchengxing */ -public class GenericSynthesizedAnnotation implements Annotation, SynthesizedAnnotation { +public class GenericSynthesizedAnnotation implements SynthesizedAnnotation { private final R root; private final T annotation;