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 1ff1eb973..869412563 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 @@ -380,8 +380,7 @@ public class AnnotationUtil { new MetaAnnotationScanner(), new TypeAnnotationScanner(), new MethodAnnotationScanner(), new FieldAnnotationScanner() }; return AnnotationScanner.scanByAnySupported(annotatedEle, scanners).stream() - .map(SyntheticAnnotation::of) - .map(annotation -> annotation.getAnnotation(annotationType)) + .map(annotation -> getSynthesisAnnotation(annotation, annotationType)) .filter(Objects::nonNull) .collect(Collectors.toList()); } 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 new file mode 100644 index 000000000..8461ac2ac --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/CacheableSynthesizedAnnotationAttributeProcessor.java @@ -0,0 +1,47 @@ +package cn.hutool.core.annotation; + +import cn.hutool.core.map.multi.RowKeyTable; +import cn.hutool.core.map.multi.Table; +import cn.hutool.core.util.ObjectUtil; + +import java.util.Collection; +import java.util.Comparator; + +/** + * 带缓存功能的{@link SynthesizedAnnotationAttributeProcessor}实现, + * 构建时需要传入比较器,获取属性值时将根据比较器对合成注解进行排序, + * 然后选择具有所需属性的,排序最靠前的注解用于获取属性值 + * + * @author huangchengxing + */ +public class CacheableSynthesizedAnnotationAttributeProcessor implements SynthesizedAnnotationAttributeProcessor { + + private final Table, Object> valueCaches = new RowKeyTable<>(); + private final Comparator annotationComparator; + + /** + * 创建一个带缓存的注解值选择器 + * + * @param annotationComparator 注解比较器,排序更靠前的注解将被优先用于获取值 + */ + public CacheableSynthesizedAnnotationAttributeProcessor(Comparator annotationComparator) { + this.annotationComparator = annotationComparator; + } + + @SuppressWarnings("unchecked") + @Override + public T getAttributeValue(String attributeName, Class attributeType, Collection synthesizedAnnotations) { + Object value = valueCaches.get(attributeName, attributeType); + // 此处理论上不可能出现缓存值为nul的情况 + if (ObjectUtil.isNotNull(value)) { + return (T)value; + } + value = synthesizedAnnotations.stream() + .filter(ma -> ma.hasAttribute(attributeName, attributeType)) + .min(annotationComparator) + .map(ma -> ma.getAttribute(attributeName)) + .orElse(null); + valueCaches.put(attributeName, attributeType, value); + return (T)value; + } +} 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 new file mode 100644 index 000000000..62a3dc91a --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotation.java @@ -0,0 +1,69 @@ +package cn.hutool.core.annotation; + +import java.lang.annotation.Annotation; + +/** + * 用于在{@link SyntheticAnnotation}中表示一个处于合成状态的注解对象 + * + * @author huangchengxing + * @see SyntheticAnnotation + */ +public interface SynthesizedAnnotation extends Annotation { + + /** + * 获取该合成注解对应的根节点 + * + * @return 合成注解对应的根节点 + */ + Object getRoot(); + + /** + * 该合成注解是为根对象 + * + * @return 根对象 + */ + default boolean isRoot() { + return getRoot() == this; + } + + /** + * 获取被合成的注解对象 + * + * @return 注解对象 + */ + Annotation getAnnotation(); + + /** + * 获取该合成注解与根对象的垂直距离。 + * 默认情况下,该距离即为当前注解与根对象之间相隔的层级数。 + * + * @return 合成注解与根对象的垂直距离 + */ + int getVerticalDistance(); + + /** + * 获取该合成注解与根对象的水平距离。 + * 默认情况下,该距离即为当前注解与根对象之间相隔的已经被扫描到的注解数。 + * + * @return 合成注解与根对象的水平距离 + */ + int getHorizontalDistance(); + + /** + * 注解是否存在该名称相同,且类型一致的属性 + * + * @param attributeName 属性名 + * @param returnType 返回值类型 + * @return 是否存在该属性 + */ + boolean hasAttribute(String attributeName, Class returnType); + + /** + * 获取属性值 + * + * @param attributeName 属性名 + * @return 属性值 + */ + Object getAttribute(String attributeName); + +} 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 new file mode 100644 index 000000000..ad0464f5b --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationAttributeProcessor.java @@ -0,0 +1,24 @@ +package cn.hutool.core.annotation; + +import java.util.Collection; + +/** + * 合成注解属性选择器。用于在{@link SyntheticAnnotation}中从指定类型的合成注解里获取到对应的属性值 + * + * @author huangchengxing + */ +@FunctionalInterface +public interface SynthesizedAnnotationAttributeProcessor { + + /** + * 从一批被合成注解中,获取指定名称与类型的属性值 + * + * @param attributeName 属性名称 + * @param attributeType 属性类型 + * @param synthesizedAnnotations 被合成的注解 + * @param 属性类型 + * @return 属性值 + */ + R getAttributeValue(String attributeName, Class attributeType, Collection synthesizedAnnotations); + +} diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationSelector.java b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationSelector.java new file mode 100644 index 000000000..b6383fe30 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationSelector.java @@ -0,0 +1,81 @@ +package cn.hutool.core.annotation; + +/** + * 注解选择器,指定两个注解,选择其中一个返回。
+ * 该接口用于在{@link SyntheticAnnotation}中用于从一批相同的注解对象中筛选最终用于合成注解对象。 + * + * @author huangchengxing + */ +@FunctionalInterface +public interface SynthesizedAnnotationSelector { + + /** + * 返回距离根对象更近的注解,当距离一样时优先返回旧注解 + */ + SynthesizedAnnotationSelector NEAREST_AND_OLDEST_PRIORITY = new NearestAndOldestPrioritySelector(); + + /** + * 返回距离根对象更近的注解,当距离一样时优先返回新注解 + */ + SynthesizedAnnotationSelector NEAREST_AND_NEWEST_PRIORITY = new NearestAndNewestPrioritySelector(); + + /** + * 返回距离根对象更远的注解,当距离一样时优先返回旧注解 + */ + SynthesizedAnnotationSelector FARTHEST_AND_OLDEST_PRIORITY = new FarthestAndOldestPrioritySelector(); + + /** + * 返回距离根对象更远的注解,当距离一样时优先返回新注解 + */ + SynthesizedAnnotationSelector FARTHEST_AND_NEWEST_PRIORITY = new FarthestAndNewestPrioritySelector(); + + /** + * 比较两个被合成的注解,选择其中的一个并返回 + * + * @param oldAnnotation 已存在的注解,该参数不允许为空 + * @param newAnnotation 新获取的注解,该参数不允许为空 + * @return 被合成的注解 + */ + T choose(T oldAnnotation, T newAnnotation); + + /** + * 返回距离根对象更近的注解,当距离一样时优先返回旧注解 + */ + class NearestAndOldestPrioritySelector implements SynthesizedAnnotationSelector { + @Override + public T choose(T oldAnnotation, T newAnnotation) { + return newAnnotation.getVerticalDistance() < oldAnnotation.getVerticalDistance() ? newAnnotation : oldAnnotation; + } + } + + /** + * 返回距离根对象更近的注解,当距离一样时优先返回新注解 + */ + class NearestAndNewestPrioritySelector implements SynthesizedAnnotationSelector { + @Override + public T choose(T oldAnnotation, T newAnnotation) { + return newAnnotation.getVerticalDistance() <= oldAnnotation.getVerticalDistance() ? newAnnotation : oldAnnotation; + } + } + + /** + * 返回距离根对象更远的注解,当距离一样时优先返回旧注解 + */ + class FarthestAndOldestPrioritySelector implements SynthesizedAnnotationSelector { + @Override + public T choose(T oldAnnotation, T newAnnotation) { + return newAnnotation.getVerticalDistance() > oldAnnotation.getVerticalDistance() ? newAnnotation : oldAnnotation; + } + } + + /** + * 返回距离根对象更远的注解,当距离一样时优先返回新注解 + */ + class FarthestAndNewestPrioritySelector implements SynthesizedAnnotationSelector { + @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/SyntheticAnnotation.java b/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticAnnotation.java index b40cfd0a6..55c0c44eb 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 @@ -1,356 +1,119 @@ package cn.hutool.core.annotation; -import cn.hutool.core.annotation.scanner.MetaAnnotationScanner; -import cn.hutool.core.lang.Opt; -import cn.hutool.core.map.MapUtil; -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.AnnotatedElement; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Objects; -import java.util.stream.Collectors; -import java.util.stream.Stream; /** - * 表示一个根注解与根注解上的多层元注解合成的注解 + * 表示基于特定规则聚合的一组注解对象 * - *

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

合成注解一般被用于处理类层级结果中具有直接或间接关联的注解对象, + * 当实例被创建时,会获取到这些注解对象,并使用{@link SynthesizedAnnotationSelector}对类型相同的注解进行过滤, + * 并最终得到类型不重复的有效注解对象。这些有效注解将被包装为{@link SynthesizedAnnotation}, + * 然后最终用于“合成”一个{@link SynthesizedAnnotation}。 * - *

不同层级的元注解可能存在同名的属性,此时,若认为该合成注解X在第0层,则根注解A在第1层,B在第2层......以此类推。 - * 层级越低的注解中离根注解距离近,则属性优先级越高,即遵循“就近原则”。
- * 举个例子:若CBA同时存在属性y,则将X视为C,B或者A时,获得的y属性的值都与最底层元注解A的值保持一致。 - * 同理,不同层级可能会出现相同的元注解,比如A注解存在元注解B,C,但是C又存在元注解B,因此根据就近原则,A上的元注解B将优先于C上的元注解B生效。 - * 若两相同注解处于同一层级,则按照从其上一级“子注解”的{@link AnnotatedElement#getAnnotations()}的调用顺序排序。
- * {@link #getAnnotation(Class)}获得的代理类实例的属性值遵循该规则。 + *

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

同名属性将根据类型彼此隔离,即当不同层级的元注解存在同名的属性,但是属性类型不同时,此时低层级的属性并不会覆盖高层级注解的属性。 - * - *

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

合成注解允许通过{@link #syntheticAnnotation(Class)}合成一个指定的注解对象, + * 该方法返回的注解对象可能是原始的注解对象,也有可能通过动态代理的方式生成, + * 该对象实例的属性不一定来自对象本身,而是来自于经过{@link SynthesizedAnnotationAttributeProcessor} + * 处理后的、用于合成当前实例的全部关联注解的相关属性。 * * @author huangchengxing - * @see AnnotationUtil + * @see SynthesizedAnnotation + * @see SynthesizedAnnotationSelector + * @see SynthesizedAnnotationAttributeProcessor + * @see SyntheticMetaAnnotation */ -public class SyntheticAnnotation implements Annotation, AnnotatedElement { +public interface SyntheticAnnotation extends Annotation, AnnotatedElement { /** - * 根注解,即当前查找的注解 - */ - private final A source; - - /** - * 包含根注解以及其元注解在内的全部注解实例 - */ - private final Map, MetaAnnotation> metaAnnotationMap; - - /** - * 属性值缓存 - */ - private final Map, Object>> attributeCaches; - - /** - * 构造 + * 获取合成注解选择器 * - * @param annotation 当前查找的注解类 + * @return 合成注解选择器 */ - SyntheticAnnotation(A annotation) { - this.source = annotation; - this.metaAnnotationMap = new LinkedHashMap<>(); - this.attributeCaches = new HashMap<>(); - loadMetaAnnotations(); // TODO 是否可以添加注解类对应的元注解信息缓存,避免每次都要解析? - } + SynthesizedAnnotationSelector getAnnotationSelector(); /** - * 基于指定根注解,构建包括其元注解在内的合成注解 + * 获取合成注解属性处理器 * - * @param 注解类型 - * @param rootAnnotation 根注解 - * @return 合成注解 + * @return 合成注解属性处理器 */ - public static SyntheticAnnotation of(T rootAnnotation) { - return new SyntheticAnnotation<>(rootAnnotation); - } + SynthesizedAnnotationAttributeProcessor getAttributeProcessor(); /** - * 获取根注解 + * 获取已合成的注解 * - * @return 根注解 + * @param annotationType 注解类型 + * @return 已合成的注解 */ - public A getSource() { - return source; - } + SynthesizedAnnotation getSynthesizedAnnotation(Class annotationType); /** - * 获取已解析的元注解信息 + * 获取当前的注解类型 * - * @return 已解析的元注解信息 - */ - Map, MetaAnnotation> getMetaAnnotationMap() { - return metaAnnotationMap; - } - - /** - * 获取根注解类型 - * - * @return java.lang.Class<? extends java.lang.annotation.Annotation> + * @return 注解类型 */ @Override - public Class annotationType() { - return getSource().annotationType(); + default Class annotationType() { + return this.getClass(); } /** - * 根据指定的属性名与属性类型获取对应的属性值,若存在{@link Alias}则获取{@link Alias#value()}指定的别名属性的值 - *

当不同层级的注解之间存在同名同类型属性时,将优先获取更接近根注解的属性 - * - * @param attributeName 属性名 - * @param attributeType 属性类型 - * @return 属性 - */ - public Object getAttribute(String attributeName, Class attributeType) { - Map, Object> values = attributeCaches.computeIfAbsent(attributeName, t -> MapUtil.newHashMap()); - return values.computeIfAbsent(attributeType, a -> metaAnnotationMap.values() - .stream() - .filter(ma -> ma.hasAttribute(attributeName, attributeType)) // 集合默认是根据distance有序的,故此处无需再排序 - .findFirst() - .map(ma -> ma.getAttribute(attributeName)) - .orElse(null) - ); - } - - /** - * 若合成注解在存在指定元注解,则使用动态代理生成一个对应的注解实例 + * 获取指定注解对象 * * @param annotationType 注解类型 * @param 注解类型 - * @return 注解 + * @return 注解对象 */ - @SuppressWarnings("unchecked") @Override - public T getAnnotation(Class annotationType) { - if (metaAnnotationMap.containsKey(annotationType)) { - return (T) Proxy.newProxyInstance( - annotationType.getClassLoader(), - new Class[]{annotationType, Synthesized.class}, - new SyntheticAnnotationProxy<>(this, annotationType) - ); - } - return null; - } + T getAnnotation(Class annotationType); + + /** + * 是否存在指定注解 + * + * @param annotationType 注解类型 + * @return 是否 + */ + @Override + boolean isAnnotationPresent(Class annotationType); /** * 获取全部注解 * - * @return java.lang.annotation.Annotation[] + * @return 注解对象 */ @Override - public Annotation[] getAnnotations() { - return getMetaAnnotationMap().values().toArray(new MetaAnnotation[0]); - } + Annotation[] getAnnotations(); /** - * 获取根注解直接声明的注解,该方法正常情况下当只返回原注解 + * 获取合成注解 * - * @return 直接声明注解 + * @param annotationType 注解类型 + * @param 注解类型 + * @return 类型 */ - @Override - public Annotation[] getDeclaredAnnotations() { - return new Annotation[]{getSource()}; - } + T syntheticAnnotation(Class annotationType); /** - * 广度优先遍历并缓存该根注解上的全部元注解 - */ - private void loadMetaAnnotations() { - // 若该注解已经是合成注解,则直接使用已解析好的元注解信息 - if (source instanceof SyntheticAnnotation.Synthesized) { - this.metaAnnotationMap.putAll(((Synthesized) source).getMetaAnnotationMap()); - return; - } - // 扫描元注解 - metaAnnotationMap.put(source.annotationType(), new MetaAnnotation(source, 0)); - new MetaAnnotationScanner().scan( - (index, annotation) -> metaAnnotationMap.computeIfAbsent( - // 当出现重复的注解时,由于后添加的注解必然层级更高,优先级更低,因此当直接忽略 - annotation.annotationType(), t -> new MetaAnnotation(annotation, index) - ), - source.annotationType(), null - ); - } - - /** - * 元注解包装类 + * 获取属性值 * - * @author huangchengxing + * @param attributeName 属性名称 + * @param attributeType 属性类型 + * @return 属性值 */ - static class MetaAnnotation implements Annotation { - - private final Annotation annotation; - private final Map attributeMethodCaches; - private final int distance; - - public MetaAnnotation(Annotation annotation, int distance) { - this.annotation = annotation; - this.distance = distance; - this.attributeMethodCaches = AnnotationUtil.getAttributeMethods(annotation.annotationType()); - } - - /** - * 获取注解类型 - * - * @return 注解类型 - */ - @Override - public Class annotationType() { - return annotation.annotationType(); - } - - /** - * 获取元注解 - * - * @return 元注解 - */ - public Annotation get() { - return annotation; - } - - /** - * 获取根注解到元注解的距离 - * - * @return 根注解到元注解的距离 - */ - public int getDistance() { - return distance; - } - - /** - * 元注解是否存在该属性 - * - * @param attributeName 属性名 - * @return 是否存在该属性 - */ - public boolean hasAttribute(String attributeName) { - return attributeMethodCaches.containsKey(attributeName); - } - - /** - * 元注解是否存在该属性,且该属性的值类型是指定类型或其子类 - * - * @param attributeName 属性名 - * @param returnType 返回值类型 - * @return 是否存在该属性 - */ - public boolean hasAttribute(String attributeName, Class returnType) { - return Opt.ofNullable(attributeMethodCaches.get(attributeName)) - .filter(method -> ClassUtil.isAssignable(returnType, method.getReturnType())) - .isPresent(); - } - - /** - * 获取元注解的属性值 - * - * @param attributeName 属性名 - * @return 元注解的属性值 - */ - public Object getAttribute(String attributeName) { - return Opt.ofNullable(attributeMethodCaches.get(attributeName)) - .map(method -> ReflectUtil.invoke(annotation, method)) - .orElse(null); - } - - } + Object getAttribute(String attributeName, Class attributeType); /** - * 表示一个已经被合成的注解 + * 基于指定根注解,构建包括其元注解在内的合成注解 * - * @author huangchengxing + * @param rootAnnotation 根注解 + * @param 注解类型 + * @return 合成注解 */ - interface Synthesized { - - /** - * 获取合成注解中已解析的元注解信息 - * - * @return 合成注解中已解析的元注解信息 - */ - Map, MetaAnnotation> getMetaAnnotationMap(); - - static boolean isMetaAnnotationMapMethod(Method method) { - return StrUtil.equals("getMetaAnnotationMap", method.getName()); - } - + static SyntheticAnnotation of(T rootAnnotation) { + return new SyntheticMetaAnnotation(rootAnnotation); } - /** - * 合成注解代理类 - * - * @author huangchengxing - */ - static class SyntheticAnnotationProxy implements Annotation, InvocationHandler { - - private final Class annotationType; - private final SyntheticAnnotation syntheticAnnotation; - - public SyntheticAnnotationProxy(SyntheticAnnotation syntheticAnnotation, Class annotationType) { - this.syntheticAnnotation = syntheticAnnotation; - this.annotationType = annotationType; - } - - @Override - public Class annotationType() { - return annotationType; - } - - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - if (Synthesized.isMetaAnnotationMapMethod(method)) { - return syntheticAnnotation.getMetaAnnotationMap(); - } - if (ReflectUtil.isHashCodeMethod(method)) { - return getHashCode(); - } - if (ReflectUtil.isToStringMethod(method)) { - return getToString(); - } - return ObjectUtil.defaultIfNull( - syntheticAnnotation.getAttribute(method.getName(), method.getReturnType()), - () -> ReflectUtil.invoke(this, method, args) - ); - } - - /** - * 获取toString值 - * - * @return toString值 - */ - private String getToString() { - final String attributes = Stream.of(annotationType().getDeclaredMethods()) - .filter(AnnotationUtil::isAttributeMethod) - .map(method -> StrUtil.format("{}={}", method.getName(), syntheticAnnotation.getAttribute(method.getName(), method.getReturnType()))) - .collect(Collectors.joining(", ")); - return StrUtil.format("@{}({})", annotationType().getName(), attributes); - } - - /** - * 获取hashcode值 - * - * @return hashcode值 - */ - private int getHashCode() { - return Objects.hash((Object) syntheticAnnotation.getAnnotations()); - } - - } } 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 new file mode 100644 index 000000000..5cf2d1723 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticAnnotationProxy.java @@ -0,0 +1,141 @@ +package cn.hutool.core.annotation; + +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 cn.hutool.core.util.StrUtil; + +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.function.BiFunction; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * 合成注解代理类 + * + * @author huangchengxing + */ +class SyntheticAnnotationProxy implements InvocationHandler { + + private final SyntheticAnnotation syntheticAnnotation; + private final SynthesizedAnnotation annotation; + private final Map> methods; + + SyntheticAnnotationProxy(SyntheticAnnotation syntheticAnnotation, SynthesizedAnnotation annotation) { + this.syntheticAnnotation = syntheticAnnotation; + this.annotation = annotation; + this.methods = new HashMap<>(9); + loadMethods(); + } + + /** + * 创建一个代理注解,生成的代理对象将是{@link SyntheticProxyAnnotation}与指定的注解类的子类。 + *

+ * + * @param annotationType 注解类型 + * @param syntheticAnnotation 合成注解 + * @return 代理注解 + */ + @SuppressWarnings("unchecked") + 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; + } + return (T) Proxy.newProxyInstance( + annotationType.getClassLoader(), + new Class[]{annotationType, SyntheticProxyAnnotation.class}, + proxyHandler + ); + } + + static boolean isProxyAnnotation(Class targetClass) { + return ClassUtil.isAssignable(SyntheticProxyAnnotation.class, targetClass); + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + return Opt.ofNullable(methods.get(method.getName())) + .map(m -> m.apply(method, args)) + .orElseGet(() -> ReflectUtil.invoke(this, method, args)); + } + + // ========================= 代理方法 ========================= + + 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) -> 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])); + methods.put("getAttribute", (method, args) -> annotation.getAttribute((String)args[0])); + methods.put("annotationType", (method, args) -> annotation.annotationType()); + for (final Method declaredMethod : annotation.getAnnotation().annotationType().getDeclaredMethods()) { + methods.put(declaredMethod.getName(), (method, args) -> proxyAttributeValue(method)); + } + } + + 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()))) + .collect(Collectors.joining(", ")); + return StrUtil.format("@{}({})", annotation.annotationType().getName(), attributes); + } + + private int proxyHashCode() { + return Objects.hash(syntheticAnnotation, annotation); + } + + private Object proxyGetSyntheticAnnotation() { + return syntheticAnnotation; + } + + private Object proxyGetSynthesizedAnnotation() { + return annotation; + } + + private Object proxyAttributeValue(Method attributeMethod) { + return syntheticAnnotation.getAttribute(attributeMethod.getName(), attributeMethod.getReturnType()); + } + + /** + * 通过代理类生成的合成注解 + * + * @author huangchengxing + */ + interface SyntheticProxyAnnotation extends SynthesizedAnnotation { + + /** + * 获取该注解所属的合成注解 + * + * @return 合成注解 + */ + SyntheticAnnotation getSyntheticAnnotation(); + + /** + * 获取该代理注解对应的已合成注解 + * + * @return 理注解对应的已合成注解 + */ + SynthesizedAnnotation getSynthesizedAnnotation(); + + } + +} 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 new file mode 100644 index 000000000..c2a7a97d8 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticMetaAnnotation.java @@ -0,0 +1,369 @@ +package cn.hutool.core.annotation; + +import cn.hutool.core.annotation.scanner.MetaAnnotationScanner; +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; + +/** + * 表示一个根注解与根注解上的多层元注解合成的注解 + * + *

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

若认为该合成注解X在第0层,则根注解A在第1层,B在第2层......以此类推, + * 则相同或不同的层级中可能会出现类型相同的注解对象,此时将通过{@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()}的调用顺序排序。 + * + *

别名在合成注解中仍然有效,若注解X中任意属性上存在{@link Alias}注解,则{@link Alias#value()}指定的属性值将会覆盖注解属性的本身的值。
+ * {@link Alias}注解仅能指定注解X中存在的属性作为别名,不允许指定元注解或子类注解的属性。 + * + * @author huangchengxing + * @see AnnotationUtil + * @see SynthesizedAnnotationSelector + */ +public class SyntheticMetaAnnotation implements SyntheticAnnotation { + + /** + * 根注解,即当前查找的注解 + */ + private final Annotation source; + + /** + * 包含根注解以及其元注解在内的全部注解实例 + */ + private final Map, SynthesizedAnnotation> metaAnnotationMap; + + /** + * 合成注解选择器 + */ + private final SynthesizedAnnotationSelector annotationSelector; + + /** + * 合成注解属性处理器 + */ + private final SynthesizedAnnotationAttributeProcessor attributeProcessor; + + /** + * 基于指定根注解,为其层级结构中的全部注解构造一个合成注解。 + * 当层级结构中出现了相同的注解对象时,将优先选择以距离根注解最近,且优先被扫描的注解对象, + * 当获取值时,同样遵循该规则。 + * + * @param source 源注解 + */ + public SyntheticMetaAnnotation(Annotation source) { + this( + source, SynthesizedAnnotationSelector.NEAREST_AND_OLDEST_PRIORITY, + new CacheableSynthesizedAnnotationAttributeProcessor( + Comparator.comparing(SynthesizedAnnotation::getVerticalDistance) + .thenComparing(SynthesizedAnnotation::getHorizontalDistance) + ) + ); + } + + /** + * 基于指定根注解,为其层级结构中的全部注解构造一个合成注解 + * + * @param annotation 当前查找的注解对象 + * @param annotationSelector 合成注解选择器 + * @param attributeProcessor 注解属性处理器 + */ + public SyntheticMetaAnnotation( + Annotation annotation, + SynthesizedAnnotationSelector annotationSelector, + SynthesizedAnnotationAttributeProcessor attributeProcessor) { + Assert.notNull(annotation, "annotation must not null"); + Assert.notNull(annotationSelector, "annotationSelector must not null"); + Assert.notNull(attributeProcessor, "attributeProcessor must not null"); + + this.source = annotation; + this.annotationSelector = annotationSelector; + this.attributeProcessor = attributeProcessor; + this.metaAnnotationMap = new LinkedHashMap<>(); + loadMetaAnnotations(); + } + + /** + * 获取根注解 + * + * @return 根注解 + */ + public Annotation getSource() { + return source; + } + + /** + * 获取已解析的元注解信息 + * + * @return 已解析的元注解信息 + */ + Map, SynthesizedAnnotation> getMetaAnnotationMap() { + return metaAnnotationMap; + } + + /** + * 获取合成注解选择器 + * + * @return 合成注解选择器 + */ + @Override + public SynthesizedAnnotationSelector getAnnotationSelector() { + return this.annotationSelector; + } + + /** + * 获取合成注解属性处理器 + * + * @return 合成注解属性处理器 + */ + @Override + public SynthesizedAnnotationAttributeProcessor getAttributeProcessor() { + return this.attributeProcessor; + } + + /** + * 获取已合成的注解 + * + * @param annotationType 注解类型 + * @return 已合成的注解 + */ + @Override + public SynthesizedAnnotation getSynthesizedAnnotation(Class annotationType) { + return metaAnnotationMap.get(annotationType); + } + + /** + * 获取根注解类型 + * + * @return 注解类型 + */ + @Override + public Class annotationType() { + return this.getClass(); + } + + /** + * 根据指定的属性名与属性类型获取对应的属性值,若存在{@link Alias}则获取{@link Alias#value()}指定的别名属性的值 + *

当不同层级的注解之间存在同名同类型属性时,将优先获取更接近根注解的属性 + * + * @param attributeName 属性名 + * @param attributeType 属性类型 + * @return 属性 + */ + @Override + public Object getAttribute(String attributeName, Class attributeType) { + return attributeProcessor.getAttributeValue(attributeName, attributeType, metaAnnotationMap.values()); + } + + /** + * 获取被合成的注解 + * + * @param annotationType 注解类型 + * @param 注解类型 + * @return 注解对象 + */ + @Override + public T getAnnotation(Class annotationType) { + return Opt.ofNullable(annotationType) + .map(metaAnnotationMap::get) + .map(SynthesizedAnnotation::getAnnotation) + .map(annotationType::cast) + .orElse(null); + } + + /** + * 当前合成注解中是否存在指定元注解 + * + * @param annotationType 注解类型 + * @return 是否 + */ + @Override + public boolean isAnnotationPresent(Class annotationType) { + return metaAnnotationMap.containsKey(annotationType); + } + + /** + * 获取全部注解 + * + * @return 注解对象 + */ + @Override + public Annotation[] getAnnotations() { + return getMetaAnnotationMap().values().toArray(new MetaAnnotation[0]); + } + + /** + * 若合成注解在存在指定元注解,则使用动态代理生成一个对应的注解实例 + * + * @param annotationType 注解类型 + * @return 合成注解对象 + * @see SyntheticAnnotationProxy#create(Class, SyntheticAnnotation) + */ + @Override + public T syntheticAnnotation(Class annotationType) { + return SyntheticAnnotationProxy.create(annotationType, this); + } + + /** + * 获取根注解直接声明的注解,该方法正常情况下当只返回原注解 + * + * @return 直接声明注解 + */ + @Override + public Annotation[] getDeclaredAnnotations() { + return new Annotation[]{getSource()}; + } + + /** + * 广度优先遍历并缓存该根注解上的全部元注解 + */ + private void loadMetaAnnotations() { + Assert.isFalse(SyntheticAnnotationProxy.isProxyAnnotation(source.getClass()), "source [{}] has been synthesized"); + // 扫描元注解 + metaAnnotationMap.put(source.annotationType(), new MetaAnnotation(source, source, 0, 0)); + new MetaAnnotationScanner().scan( + (index, annotation) -> { + SynthesizedAnnotation oldAnnotation = metaAnnotationMap.get(annotation.annotationType()); + SynthesizedAnnotation newAnnotation = new MetaAnnotation(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 + ); + } + + /** + * 元注解包装类 + * + * @author huangchengxing + */ + public static class MetaAnnotation implements Annotation, SynthesizedAnnotation { + + private final Annotation root; + private final Annotation annotation; + private final Map attributeMethodCaches; + private final int verticalDistance; + private final int horizontalDistance; + + public MetaAnnotation(Annotation root, Annotation annotation, int verticalDistance, int horizontalDistance) { + this.root = root; + this.annotation = annotation; + this.verticalDistance = verticalDistance; + this.horizontalDistance = horizontalDistance; + this.attributeMethodCaches = AnnotationUtil.getAttributeMethods(annotation.annotationType()); + } + + /** + * 获取注解类型 + * + * @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.getReturnType())) + .isPresent(); + } + + /** + * 获取元注解的属性值 + * + * @param attributeName 属性名 + * @return 元注解的属性值 + */ + @Override + public Object getAttribute(String attributeName) { + return Opt.ofNullable(attributeMethodCaches.get(attributeName)) + .map(method -> ReflectUtil.invoke(annotation, method)) + .orElse(null); + } + + } + +} diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/SyntheticAnnotationTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/SyntheticAnnotationTest.java deleted file mode 100644 index 8e1db5d3f..000000000 --- a/hutool-core/src/test/java/cn/hutool/core/annotation/SyntheticAnnotationTest.java +++ /dev/null @@ -1,87 +0,0 @@ -package cn.hutool.core.annotation; - -import org.junit.Assert; -import org.junit.Test; - -import java.lang.annotation.*; -import java.util.Map; - -/** - * 合成注解{@link SyntheticAnnotation}的测试用例 - * - * @author huangchengxing - */ -public class SyntheticAnnotationTest { - - @Test - public void testSynthesisAnnotation() { - ChildAnnotation rootAnnotation = AnnotatedClass.class.getAnnotation(ChildAnnotation.class); - SyntheticAnnotation syntheticAnnotation = SyntheticAnnotation.of(rootAnnotation); - Assert.assertEquals(syntheticAnnotation.getSource(), rootAnnotation); - Assert.assertEquals(syntheticAnnotation.annotationType(), rootAnnotation.annotationType()); - Assert.assertEquals(1, syntheticAnnotation.getDeclaredAnnotations().length); - Assert.assertEquals(syntheticAnnotation.getDeclaredAnnotations()[0], rootAnnotation); - Assert.assertEquals(3, syntheticAnnotation.getAnnotations().length); - - Assert.assertEquals(syntheticAnnotation.getAttribute("childValue", String.class), "Child!"); - Assert.assertEquals(syntheticAnnotation.getAttribute("childValueAlias", String.class), "Child!"); - Assert.assertEquals(syntheticAnnotation.getAttribute("parentValue", String.class), "Child's Parent!"); - Assert.assertEquals(syntheticAnnotation.getAttribute("grandParentValue", String.class), "Child's GrandParent!"); - - Map, SyntheticAnnotation.MetaAnnotation> annotationMap = syntheticAnnotation.getMetaAnnotationMap(); - ChildAnnotation childAnnotation = syntheticAnnotation.getAnnotation(ChildAnnotation.class); - Assert.assertTrue(syntheticAnnotation.isAnnotationPresent(ChildAnnotation.class)); - Assert.assertNotNull(childAnnotation); - Assert.assertEquals(childAnnotation.childValue(), "Child!"); - Assert.assertEquals(childAnnotation.childValueAlias(), "Child!"); - Assert.assertEquals(childAnnotation.grandParentType(), Integer.class); - Assert.assertEquals(annotationMap, SyntheticAnnotation.of(childAnnotation).getMetaAnnotationMap()); - - ParentAnnotation parentAnnotation = syntheticAnnotation.getAnnotation(ParentAnnotation.class); - Assert.assertTrue(syntheticAnnotation.isAnnotationPresent(ParentAnnotation.class)); - Assert.assertNotNull(parentAnnotation); - Assert.assertEquals(parentAnnotation.parentValue(), "Child's Parent!"); - Assert.assertEquals(parentAnnotation.grandParentType(), "java.lang.Void"); - Assert.assertEquals(annotationMap, SyntheticAnnotation.of(parentAnnotation).getMetaAnnotationMap()); - - GrandParentAnnotation grandParentAnnotation = syntheticAnnotation.getAnnotation(GrandParentAnnotation.class); - Assert.assertTrue(syntheticAnnotation.isAnnotationPresent(GrandParentAnnotation.class)); - Assert.assertNotNull(grandParentAnnotation); - Assert.assertEquals(grandParentAnnotation.grandParentValue(), "Child's GrandParent!"); - Assert.assertEquals(grandParentAnnotation.grandParentType(), Integer.class); - Assert.assertEquals(annotationMap, SyntheticAnnotation.of(grandParentAnnotation).getMetaAnnotationMap()); - } - - // 注解结构如下: - // AnnotatedClass -> @ChildAnnotation -> @ParentAnnotation -> @GrandParentAnnotation - // -> @GrandParentAnnotation - @ChildAnnotation(childValueAlias = "Child!", grandParentType = Integer.class) - static class AnnotatedClass {} - - @Retention(RetentionPolicy.RUNTIME) - @Target({ ElementType.ANNOTATION_TYPE }) - @interface GrandParentAnnotation { - String grandParentValue() default ""; - Class grandParentType() default Void.class; - } - - @GrandParentAnnotation(grandParentValue = "Parent's GrandParent!") // 覆盖元注解@GrandParentAnnotation的属性 - @Retention(RetentionPolicy.RUNTIME) - @Target({ ElementType.TYPE }) - @interface ParentAnnotation { - String parentValue() default ""; - String grandParentType() default "java.lang.Void"; - } - - @GrandParentAnnotation(grandParentValue = "Child's GrandParent!") // 重复的元注解,靠近根注解的优先级高 - @ParentAnnotation(parentValue = "Child's Parent!") // 覆盖元注解@ParentAnnotation的属性 - @Retention(RetentionPolicy.RUNTIME) - @Target({ ElementType.METHOD, ElementType.TYPE }) - @interface ChildAnnotation { - String childValueAlias() default ""; - @Alias("childValueAlias") - String childValue() default ""; - Class grandParentType() default Void.class; - } - -} 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 new file mode 100644 index 000000000..009f1e205 --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/SyntheticMetaAnnotationTest.java @@ -0,0 +1,88 @@ +package cn.hutool.core.annotation; + +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; + +/** + * 合成注解{@link SyntheticMetaAnnotation}的测试用例 + * + * @author huangchengxing + */ +public class SyntheticMetaAnnotationTest { + + @Test + public void testSynthesisAnnotation() { + ChildAnnotation rootAnnotation = AnnotatedClass.class.getAnnotation(ChildAnnotation.class); + SyntheticMetaAnnotation syntheticMetaAnnotation = new SyntheticMetaAnnotation(rootAnnotation); + Assert.assertEquals(syntheticMetaAnnotation.getSource(), rootAnnotation); + Assert.assertEquals(syntheticMetaAnnotation.annotationType(), SyntheticMetaAnnotation.class); + Assert.assertEquals(1, syntheticMetaAnnotation.getDeclaredAnnotations().length); + Assert.assertEquals(syntheticMetaAnnotation.getDeclaredAnnotations()[0], rootAnnotation); + 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)); + + ChildAnnotation childAnnotation = syntheticMetaAnnotation.syntheticAnnotation(ChildAnnotation.class); + 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)); + + 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); + 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)); + } + + // 注解结构如下: + // AnnotatedClass -> @ChildAnnotation -> @ParentAnnotation -> @GrandParentAnnotation + // -> @GrandParentAnnotation + @ChildAnnotation(childValueAlias = "Child!", grandParentType = Integer.class) + static class AnnotatedClass {} + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.ANNOTATION_TYPE }) + @interface GrandParentAnnotation { + String grandParentValue() default ""; + Class grandParentType() default Void.class; + } + + @GrandParentAnnotation(grandParentValue = "Parent's GrandParent!") // 覆盖元注解@GrandParentAnnotation的属性 + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE }) + @interface ParentAnnotation { + String parentValue() default ""; + String grandParentType() default "java.lang.Void"; + } + + @GrandParentAnnotation(grandParentValue = "Child's GrandParent!") // 重复的元注解,靠近根注解的优先级高 + @ParentAnnotation(parentValue = "Child's Parent!") // 覆盖元注解@ParentAnnotation的属性 + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.METHOD, ElementType.TYPE }) + @interface ChildAnnotation { + String childValueAlias() default ""; + @Alias("childValueAlias") + String childValue() default ""; + Class grandParentType() default Void.class; + } + +}