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] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81=EF=BC=8C?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=8D=E5=87=86=E7=A1=AE=E7=9A=84=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=EF=BC=8C=E5=AE=8C=E5=96=84=E6=B5=8B=E8=AF=95=E7=94=A8?= =?UTF-8?q?=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()}的返回值进行排序, * 该值更小的处理器将被优先执行。 * + *

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

+ * * @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 {} + }