From 2ecda1d245666fb2f09d7c1581eae02e1e65484e Mon Sep 17 00:00:00 2001 From: huangchengxing <841396397@qq.com> Date: Tue, 5 Jul 2022 13:38:25 +0800 Subject: [PATCH 1/8] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=B7=B2=E5=90=88?= =?UTF-8?q?=E6=88=90=E6=B3=A8=E8=A7=A3=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../annotation/SynthesizedAnnotation.java | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotation.java 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..5c093ee6c --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotation.java @@ -0,0 +1,68 @@ +package cn.hutool.core.annotation; + +import java.lang.annotation.Annotation; + +/** + * 表示一个处于合成状态的注解对象 + * + * @author huangchengxing + */ +public interface SynthesizedAnnotation extends Annotation { + + /** + * 获取该合成注解对应的根节点 + * + * @return 数据源 + */ + T 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); + +} From 0659440cad3e6daf04f5289431149dc2851e283b Mon Sep 17 00:00:00 2001 From: huangchengxing <841396397@qq.com> Date: Tue, 5 Jul 2022 13:39:07 +0800 Subject: [PATCH 2/8] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=B3=A8=E8=A7=A3?= =?UTF-8?q?=E9=80=89=E6=8B=A9=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SynthesizedAnnotationSelector.java | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationSelector.java 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..65b76cf61 --- /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 被合成的注解 + */ + > A choose(A oldAnnotation, A newAnnotation); + + /** + * 返回距离根对象更近的注解,当距离一样时优先返回旧注解 + */ + class NearestAndOldestPrioritySelector implements SynthesizedAnnotationSelector { + @Override + public > A choose(A oldAnnotation, A newAnnotation) { + return newAnnotation.getVerticalDistance() < oldAnnotation.getVerticalDistance() ? newAnnotation : oldAnnotation; + } + } + + /** + * 返回距离根对象更近的注解,当距离一样时优先返回新注解 + */ + class NearestAndNewestPrioritySelector implements SynthesizedAnnotationSelector { + @Override + public > A choose(A oldAnnotation, A newAnnotation) { + return newAnnotation.getVerticalDistance() <= oldAnnotation.getVerticalDistance() ? newAnnotation : oldAnnotation; + } + } + + /** + * 返回距离根对象更远的注解,当距离一样时优先返回旧注解 + */ + class FarthestAndOldestPrioritySelector implements SynthesizedAnnotationSelector { + @Override + public > A choose(A oldAnnotation, A newAnnotation) { + return newAnnotation.getVerticalDistance() > oldAnnotation.getVerticalDistance() ? newAnnotation : oldAnnotation; + } + } + + /** + * 返回距离根对象更远的注解,当距离一样时优先返回新注解 + */ + class FarthestAndNewestPrioritySelector implements SynthesizedAnnotationSelector { + @Override + public > A choose(A oldAnnotation, A newAnnotation) { + return newAnnotation.getVerticalDistance() >= oldAnnotation.getVerticalDistance() ? newAnnotation : oldAnnotation; + } + } + +} From 32d0b65744d8969c5691af829bfbccca141e5339 Mon Sep 17 00:00:00 2001 From: huangchengxing <841396397@qq.com> Date: Tue, 5 Jul 2022 13:39:56 +0800 Subject: [PATCH 3/8] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=B3=A8=E8=A7=A3?= =?UTF-8?q?=E5=B1=9E=E6=80=A7=E5=A4=84=E7=90=86=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...nthesizedAnnotationAttributeProcessor.java | 49 +++++++++++++++++++ ...nthesizedAnnotationAttributeProcessor.java | 24 +++++++++ 2 files changed, 73 insertions(+) create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/CacheableSynthesizedAnnotationAttributeProcessor.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationAttributeProcessor.java diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/CacheableSynthesizedAnnotationAttributeProcessor.java b/hutool-core/src/main/java/cn/hutool/core/annotation/CacheableSynthesizedAnnotationAttributeProcessor.java new file mode 100644 index 000000000..3329bc2f1 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/CacheableSynthesizedAnnotationAttributeProcessor.java @@ -0,0 +1,49 @@ +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}实现, + * 构建时需要传入比较器,获取属性值时将根据比较器对合成注解进行排序, + * 然后选择具有所需属性的,排序最靠前的注解用于获取属性值 + * + * @param 合成注解类型 + * @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/SynthesizedAnnotationAttributeProcessor.java b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationAttributeProcessor.java new file mode 100644 index 000000000..022afb8ce --- /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; + +/** + * 合成注解属性选择器。用于中合成注解中从指定类型的注解里获取到对应的属性值 + * + * @author huangchengxing + */ +@FunctionalInterface +public interface SynthesizedAnnotationAttributeProcessor> { + + /** + * 从一批被合成注解中,获取指定名称与类型的属性值 + * + * @param attributeName 属性名称 + * @param attributeType 属性类型 + * @param synthesizedAnnotations 被合成的注解 + * @param 属性类型 + * @return 属性值 + */ + T getAttributeValue(String attributeName, Class attributeType, Collection synthesizedAnnotations); + +} From d20980096e32ea0344863663ed41d6d69b4d86e7 Mon Sep 17 00:00:00 2001 From: huangchengxing <841396397@qq.com> Date: Tue, 5 Jul 2022 13:47:11 +0800 Subject: [PATCH 4/8] =?UTF-8?q?=E5=B0=86SyntheticAnnotation=E6=8F=90?= =?UTF-8?q?=E5=8F=96=E4=B8=BA=E6=8E=A5=E5=8F=A3=EF=BC=8C=E5=8E=9F=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=E6=9B=B4=E5=90=8D=E4=B8=BASyntheticMetaAnnotation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/annotation/AnnotationUtil.java | 3 +- .../core/annotation/SyntheticAnnotation.java | 397 +++------------- .../annotation/SyntheticMetaAnnotation.java | 449 ++++++++++++++++++ .../annotation/SyntheticAnnotationTest.java | 87 ---- .../SyntheticMetaAnnotationTest.java | 87 ++++ 5 files changed, 603 insertions(+), 420 deletions(-) create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticMetaAnnotation.java delete mode 100644 hutool-core/src/test/java/cn/hutool/core/annotation/SyntheticAnnotationTest.java create mode 100644 hutool-core/src/test/java/cn/hutool/core/annotation/SyntheticMetaAnnotationTest.java 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/SyntheticAnnotation.java b/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticAnnotation.java index b40cfd0a6..4718ea857 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,78 +1,82 @@ 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。 - * - *

不同层级的元注解可能存在同名的属性,此时,若认为该合成注解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)}获得的代理类实例的属性值遵循该规则。 - * - *

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

别名在合成注解中仍然有效,若注解X中任意属性上存在{@link Alias}注解,则{@link Alias#value()}指定的属性值将会覆盖注解属性的本身的值。
- * {@link Alias}注解仅能指定注解X中存在的属性作为别名,不允许指定元注解或子类注解的属性。 + * 表示基于特定规则聚合的一组注解 * * @author huangchengxing - * @see AnnotationUtil */ -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(); + + /** + * 获取合成注解属性处理器 + * + * @return 合成注解属性处理器 + */ + SynthesizedAnnotationAttributeProcessor getAttributeProcessor(); + + /** + * 获取当前的注解类型 + * + * @return 注解类型 + */ + @Override + default Class annotationType() { + return this.getClass(); } + /** + * 获取指定注解对象 + * + * @param annotationType 注解类型 + * @return 注解对象 + */ + @Override + T getAnnotation(Class annotationType); + + /** + * 是否存在指定注解 + * + * @param annotationType 注解类型 + * @return 是否 + */ + @Override + boolean isAnnotationPresent(Class annotationType); + + /** + * 获取全部注解 + * + * @return 注解对象 + */ + @Override + Annotation[] getAnnotations(); + + /** + * 获取合成注解 + * + * @param annotationType 注解类型 + * @return 类型 + */ + T syntheticAnnotation(Class annotationType); + + /** + * 获取属性值 + * + * @param attributeName 属性名称 + * @param attributeType 属性类型 + * @return 属性值 + */ + Object getAttribute(String attributeName, Class attributeType); + /** * 基于指定根注解,构建包括其元注解在内的合成注解 * @@ -80,277 +84,8 @@ public class SyntheticAnnotation implements Annotation, An * @param rootAnnotation 根注解 * @return 合成注解 */ - public static SyntheticAnnotation of(T rootAnnotation) { - return new SyntheticAnnotation<>(rootAnnotation); + static SyntheticAnnotation of(T rootAnnotation) { + return new SyntheticMetaAnnotation<>(rootAnnotation); } - /** - * 获取根注解 - * - * @return 根注解 - */ - public A getSource() { - return source; - } - - /** - * 获取已解析的元注解信息 - * - * @return 已解析的元注解信息 - */ - Map, MetaAnnotation> getMetaAnnotationMap() { - return metaAnnotationMap; - } - - /** - * 获取根注解类型 - * - * @return java.lang.Class<? extends java.lang.annotation.Annotation> - */ - @Override - public Class annotationType() { - return getSource().annotationType(); - } - - /** - * 根据指定的属性名与属性类型获取对应的属性值,若存在{@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 注解 - */ - @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; - } - - /** - * 获取全部注解 - * - * @return java.lang.annotation.Annotation[] - */ - @Override - public Annotation[] getAnnotations() { - return getMetaAnnotationMap().values().toArray(new MetaAnnotation[0]); - } - - /** - * 获取根注解直接声明的注解,该方法正常情况下当只返回原注解 - * - * @return 直接声明注解 - */ - @Override - public Annotation[] getDeclaredAnnotations() { - return new Annotation[]{getSource()}; - } - - /** - * 广度优先遍历并缓存该根注解上的全部元注解 - */ - 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 - */ - 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); - } - - } - - /** - * 表示一个已经被合成的注解 - * - * @author huangchengxing - */ - interface Synthesized { - - /** - * 获取合成注解中已解析的元注解信息 - * - * @return 合成注解中已解析的元注解信息 - */ - Map, MetaAnnotation> getMetaAnnotationMap(); - - static boolean isMetaAnnotationMapMethod(Method method) { - return StrUtil.equals("getMetaAnnotationMap", method.getName()); - } - - } - - /** - * 合成注解代理类 - * - * @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/SyntheticMetaAnnotation.java b/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticMetaAnnotation.java new file mode 100644 index 000000000..8fadd3261 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticMetaAnnotation.java @@ -0,0 +1,449 @@ +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 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.Comparator; +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。 + * + *

若认为该合成注解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 A source; + + /** + * 包含根注解以及其元注解在内的全部注解实例 + */ + private final Map, MetaAnnotation> metaAnnotationMap; + + /** + * 合成注解选择器 + */ + private final SynthesizedAnnotationSelector annotationSelector; + + /** + * 合成注解属性处理器 + */ + private final SynthesizedAnnotationAttributeProcessor attributeProcessor; + + /** + * 基于指定根注解,为其层级结构中的全部注解构造一个合成注解。 + * 当层级结构中出现了相同的注解对象时,将优先选择以距离根注解最近,且优先被扫描的注解对象, + * 当获取值时,同样遵循该规则。 + * + * @param source 源注解 + */ + public SyntheticMetaAnnotation(A source) { + this( + source, SynthesizedAnnotationSelector.NEAREST_AND_OLDEST_PRIORITY, + new CacheableSynthesizedAnnotationAttributeProcessor<>( + Comparator.comparing(MetaAnnotation::getVerticalDistance) + .thenComparing(MetaAnnotation::getHorizontalDistance) + ) + ); + } + + /** + * 基于指定根注解,为其层级结构中的全部注解构造一个合成注解 + * + * @param annotation 当前查找的注解类 + * @param annotationSelector 合成注解选择器 + */ + public SyntheticMetaAnnotation( + A 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 A getSource() { + return source; + } + + /** + * 获取已解析的元注解信息 + * + * @return 已解析的元注解信息 + */ + Map, MetaAnnotation> getMetaAnnotationMap() { + return metaAnnotationMap; + } + + /** + * 获取合成注解选择器 + * + * @return 合成注解选择器 + */ + @Override + public SynthesizedAnnotationSelector getAnnotationSelector() { + return this.annotationSelector; + } + + /** + * 获取合成注解属性处理器 + * + * @return 合成注解属性处理器 + */ + @Override + public SynthesizedAnnotationAttributeProcessor getAttributeProcessor() { + return null; + } + + /** + * 获取根注解类型 + * + * @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(MetaAnnotation::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 合成注解对象 + */ + @SuppressWarnings("unchecked") + @Override + public T syntheticAnnotation(Class annotationType) { + if (metaAnnotationMap.containsKey(annotationType)) { + return (T) Proxy.newProxyInstance( + annotationType.getClassLoader(), + new Class[]{annotationType, Synthesized.class}, + new SyntheticAnnotationProxy<>(this, annotationType) + ); + } + return null; + } + + /** + * 获取根注解直接声明的注解,该方法正常情况下当只返回原注解 + * + * @return 直接声明注解 + */ + @Override + public Annotation[] getDeclaredAnnotations() { + return new Annotation[]{getSource()}; + } + + /** + * 广度优先遍历并缓存该根注解上的全部元注解 + */ + private void loadMetaAnnotations() { + // 若该注解已经是合成注解,则直接使用已解析好的元注解信息 + if (source instanceof SyntheticMetaAnnotation.Synthesized) { + this.metaAnnotationMap.putAll(((Synthesized) source).getMetaAnnotationMap()); + return; + } + // 扫描元注解 + metaAnnotationMap.put(source.annotationType(), new MetaAnnotation(source, source, 0, 0)); + new MetaAnnotationScanner().scan( + (index, annotation) -> { + MetaAnnotation oldAnnotation = metaAnnotationMap.get(annotation.annotationType()); + MetaAnnotation 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; + } + + @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); + } + + } + + /** + * 表示一个已经被合成的注解 + * + * @author huangchengxing + */ + interface Synthesized { + + /** + * 获取合成注解中已解析的元注解信息 + * + * @return 合成注解中已解析的元注解信息 + */ + Map, MetaAnnotation> getMetaAnnotationMap(); + + static boolean isMetaAnnotationMapMethod(Method method) { + return StrUtil.equals("getMetaAnnotationMap", method.getName()); + } + + } + + /** + * 合成注解代理类 + * + * @author huangchengxing + */ + static class SyntheticAnnotationProxy implements Annotation, InvocationHandler { + + private final Class annotationType; + private final SyntheticMetaAnnotation syntheticMetaAnnotation; + + public SyntheticAnnotationProxy(SyntheticMetaAnnotation syntheticMetaAnnotation, Class annotationType) { + this.syntheticMetaAnnotation = syntheticMetaAnnotation; + 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 syntheticMetaAnnotation.getMetaAnnotationMap(); + } + if (ReflectUtil.isHashCodeMethod(method)) { + return getHashCode(); + } + if (ReflectUtil.isToStringMethod(method)) { + return getToString(); + } + return ObjectUtil.defaultIfNull( + syntheticMetaAnnotation.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(), syntheticMetaAnnotation.getAttribute(method.getName(), method.getReturnType()))) + .collect(Collectors.joining(", ")); + return StrUtil.format("@{}({})", annotationType().getName(), attributes); + } + + /** + * 获取hashcode值 + * + * @return hashcode值 + */ + private int getHashCode() { + return Objects.hash((Object) syntheticMetaAnnotation.getAnnotations()); + } + + } +} 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..002311593 --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/SyntheticMetaAnnotationTest.java @@ -0,0 +1,87 @@ +package cn.hutool.core.annotation; + +import org.junit.Assert; +import org.junit.Test; + +import java.lang.annotation.*; +import java.util.Map; + +/** + * 合成注解{@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)); + + Map, SyntheticMetaAnnotation.MetaAnnotation> annotationMap = syntheticMetaAnnotation.getMetaAnnotationMap(); + 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.assertEquals(annotationMap, new SyntheticMetaAnnotation<>(childAnnotation).getMetaAnnotationMap()); + + 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.assertEquals(annotationMap, new SyntheticMetaAnnotation<>(parentAnnotation).getMetaAnnotationMap()); + + 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.assertEquals(annotationMap, new SyntheticMetaAnnotation<>(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; + } + +} From 254e6f5315852aab9b0354d756ef5420fa9aba82 Mon Sep 17 00:00:00 2001 From: huangchengxing <841396397@qq.com> Date: Tue, 5 Jul 2022 16:03:11 +0800 Subject: [PATCH 5/8] =?UTF-8?q?=E5=B0=86=E5=90=88=E6=88=90=E6=B3=A8?= =?UTF-8?q?=E8=A7=A3=E4=BB=A3=E7=90=86=E7=B1=BB=E6=8F=90=E5=8F=96=E4=B8=BA?= =?UTF-8?q?=E5=A4=96=E9=83=A8=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/annotation/SyntheticAnnotation.java | 14 +- .../annotation/SyntheticAnnotationProxy.java | 143 ++++++++++++++++++ .../annotation/SyntheticMetaAnnotation.java | 115 ++------------ .../SyntheticMetaAnnotationTest.java | 6 +- 4 files changed, 174 insertions(+), 104 deletions(-) create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticAnnotationProxy.java 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 4718ea857..084c12566 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 @@ -6,6 +6,7 @@ import java.lang.reflect.AnnotatedElement; /** * 表示基于特定规则聚合的一组注解 * + * @param 合成注解类型 * @author huangchengxing */ public interface SyntheticAnnotation> extends Annotation, AnnotatedElement { @@ -24,6 +25,15 @@ public interface SyntheticAnnotation> extends */ SynthesizedAnnotationAttributeProcessor getAttributeProcessor(); + /** + * 获取已合成的注解 + * + * @param annotationType 注解类型 + * @param 注解类型 + * @return 已合成的注解 + */ + SynthesizedAnnotation getSynthesizedAnnotation(Class annotationType); + /** * 获取当前的注解类型 * @@ -38,6 +48,7 @@ public interface SyntheticAnnotation> extends * 获取指定注解对象 * * @param annotationType 注解类型 + * @param 注解类型 * @return 注解对象 */ @Override @@ -64,6 +75,7 @@ public interface SyntheticAnnotation> extends * 获取合成注解 * * @param annotationType 注解类型 + * @param 注解类型 * @return 类型 */ T syntheticAnnotation(Class annotationType); @@ -80,8 +92,8 @@ public interface SyntheticAnnotation> extends /** * 基于指定根注解,构建包括其元注解在内的合成注解 * - * @param 注解类型 * @param rootAnnotation 根注解 + * @param 注解类型 * @return 合成注解 */ static SyntheticAnnotation of(T rootAnnotation) { 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..101835f47 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticAnnotationProxy.java @@ -0,0 +1,143 @@ +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; + +/** + * 合成注解代理类 + * + * @param 代理的注解类型 + * @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 合成注解 + * @param 代理的注解类型 + * @return 代理注解 + */ + @SuppressWarnings("unchecked") + static A 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 (A) 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 (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 index 8fadd3261..708fc8fe1 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 @@ -6,19 +6,13 @@ 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.AnnotatedElement; -import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; -import java.lang.reflect.Proxy; import java.util.Comparator; import java.util.LinkedHashMap; import java.util.Map; -import java.util.Objects; -import java.util.stream.Collectors; -import java.util.stream.Stream; /** * 表示一个根注解与根注解上的多层元注解合成的注解 @@ -147,6 +141,18 @@ public class SyntheticMetaAnnotation implements SyntheticA return null; } + /** + * 获取已合成的注解 + * + * @param annotationType 注解类型 + * @return 已合成的注解 + */ + @SuppressWarnings("unchecked") + @Override + public SynthesizedAnnotation getSynthesizedAnnotation(Class annotationType) { + return (SynthesizedAnnotation)metaAnnotationMap.get(annotationType); + } + /** * 获取根注解类型 * @@ -212,18 +218,11 @@ public class SyntheticMetaAnnotation implements SyntheticA * * @param annotationType 注解类型 * @return 合成注解对象 + * @see SyntheticAnnotationProxy#create(Class, SyntheticAnnotation) */ - @SuppressWarnings("unchecked") @Override public T syntheticAnnotation(Class annotationType) { - if (metaAnnotationMap.containsKey(annotationType)) { - return (T) Proxy.newProxyInstance( - annotationType.getClassLoader(), - new Class[]{annotationType, Synthesized.class}, - new SyntheticAnnotationProxy<>(this, annotationType) - ); - } - return null; + return SyntheticAnnotationProxy.create(annotationType, this); } /** @@ -240,11 +239,7 @@ public class SyntheticMetaAnnotation implements SyntheticA * 广度优先遍历并缓存该根注解上的全部元注解 */ private void loadMetaAnnotations() { - // 若该注解已经是合成注解,则直接使用已解析好的元注解信息 - if (source instanceof SyntheticMetaAnnotation.Synthesized) { - this.metaAnnotationMap.putAll(((Synthesized) source).getMetaAnnotationMap()); - return; - } + Assert.isFalse(SyntheticAnnotationProxy.isProxyAnnotation(source.getClass()), "source [{}] has been synthesized"); // 扫描元注解 metaAnnotationMap.put(source.annotationType(), new MetaAnnotation(source, source, 0, 0)); new MetaAnnotationScanner().scan( @@ -366,84 +361,4 @@ public class SyntheticMetaAnnotation implements SyntheticA } - /** - * 表示一个已经被合成的注解 - * - * @author huangchengxing - */ - interface Synthesized { - - /** - * 获取合成注解中已解析的元注解信息 - * - * @return 合成注解中已解析的元注解信息 - */ - Map, MetaAnnotation> getMetaAnnotationMap(); - - static boolean isMetaAnnotationMapMethod(Method method) { - return StrUtil.equals("getMetaAnnotationMap", method.getName()); - } - - } - - /** - * 合成注解代理类 - * - * @author huangchengxing - */ - static class SyntheticAnnotationProxy implements Annotation, InvocationHandler { - - private final Class annotationType; - private final SyntheticMetaAnnotation syntheticMetaAnnotation; - - public SyntheticAnnotationProxy(SyntheticMetaAnnotation syntheticMetaAnnotation, Class annotationType) { - this.syntheticMetaAnnotation = syntheticMetaAnnotation; - 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 syntheticMetaAnnotation.getMetaAnnotationMap(); - } - if (ReflectUtil.isHashCodeMethod(method)) { - return getHashCode(); - } - if (ReflectUtil.isToStringMethod(method)) { - return getToString(); - } - return ObjectUtil.defaultIfNull( - syntheticMetaAnnotation.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(), syntheticMetaAnnotation.getAttribute(method.getName(), method.getReturnType()))) - .collect(Collectors.joining(", ")); - return StrUtil.format("@{}({})", annotationType().getName(), attributes); - } - - /** - * 获取hashcode值 - * - * @return hashcode值 - */ - private int getHashCode() { - return Objects.hash((Object) syntheticMetaAnnotation.getAnnotations()); - } - - } } 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 002311593..e85862be9 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 @@ -35,21 +35,21 @@ public class SyntheticMetaAnnotationTest { Assert.assertEquals("Child!", childAnnotation.childValue()); Assert.assertEquals("Child!", childAnnotation.childValueAlias()); Assert.assertEquals(childAnnotation.grandParentType(), Integer.class); - Assert.assertEquals(annotationMap, new SyntheticMetaAnnotation<>(childAnnotation).getMetaAnnotationMap()); + 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.assertEquals(annotationMap, new SyntheticMetaAnnotation<>(parentAnnotation).getMetaAnnotationMap()); + 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.assertEquals(annotationMap, new SyntheticMetaAnnotation<>(grandParentAnnotation).getMetaAnnotationMap()); + Assert.assertThrows(IllegalArgumentException.class, () -> new SyntheticMetaAnnotation<>(grandParentAnnotation)); } // 注解结构如下: From c343b51e380ed4e5c8272dca434f79ab12427d64 Mon Sep 17 00:00:00 2001 From: huangchengxing <841396397@qq.com> Date: Tue, 5 Jul 2022 16:20:49 +0800 Subject: [PATCH 6/8] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E4=B8=8D=E5=BF=85?= =?UTF-8?q?=E8=A6=81=E7=9A=84=E6=B3=9B=E5=9E=8B=E7=BA=A6=E6=9D=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...nthesizedAnnotationAttributeProcessor.java | 10 ++--- .../annotation/SynthesizedAnnotation.java | 4 +- ...nthesizedAnnotationAttributeProcessor.java | 6 +-- .../SynthesizedAnnotationSelector.java | 10 ++--- .../core/annotation/SyntheticAnnotation.java | 12 +++--- .../annotation/SyntheticAnnotationProxy.java | 26 ++++++------ .../annotation/SyntheticMetaAnnotation.java | 40 +++++++++---------- .../SyntheticMetaAnnotationTest.java | 15 +++---- 8 files changed, 59 insertions(+), 64 deletions(-) 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 3329bc2f1..8461ac2ac 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 @@ -12,27 +12,25 @@ import java.util.Comparator; * 构建时需要传入比较器,获取属性值时将根据比较器对合成注解进行排序, * 然后选择具有所需属性的,排序最靠前的注解用于获取属性值 * - * @param 合成注解类型 * @author huangchengxing */ -public class CacheableSynthesizedAnnotationAttributeProcessor> implements - SynthesizedAnnotationAttributeProcessor { +public class CacheableSynthesizedAnnotationAttributeProcessor implements SynthesizedAnnotationAttributeProcessor { private final Table, Object> valueCaches = new RowKeyTable<>(); - private final Comparator annotationComparator; + private final Comparator annotationComparator; /** * 创建一个带缓存的注解值选择器 * * @param annotationComparator 注解比较器,排序更靠前的注解将被优先用于获取值 */ - public CacheableSynthesizedAnnotationAttributeProcessor(Comparator annotationComparator) { + public CacheableSynthesizedAnnotationAttributeProcessor(Comparator annotationComparator) { this.annotationComparator = annotationComparator; } @SuppressWarnings("unchecked") @Override - public T getAttributeValue(String attributeName, Class attributeType, Collection synthesizedAnnotations) { + public T getAttributeValue(String attributeName, Class attributeType, Collection synthesizedAnnotations) { Object value = valueCaches.get(attributeName, attributeType); // 此处理论上不可能出现缓存值为nul的情况 if (ObjectUtil.isNotNull(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 index 5c093ee6c..8d81701b0 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotation.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotation.java @@ -7,14 +7,14 @@ import java.lang.annotation.Annotation; * * @author huangchengxing */ -public interface SynthesizedAnnotation extends Annotation { +public interface SynthesizedAnnotation extends Annotation { /** * 获取该合成注解对应的根节点 * * @return 数据源 */ - T getRoot(); + Object getRoot(); /** * 该合成注解是为根对象 diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationAttributeProcessor.java b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationAttributeProcessor.java index 022afb8ce..83f7c274b 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationAttributeProcessor.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationAttributeProcessor.java @@ -8,7 +8,7 @@ import java.util.Collection; * @author huangchengxing */ @FunctionalInterface -public interface SynthesizedAnnotationAttributeProcessor> { +public interface SynthesizedAnnotationAttributeProcessor { /** * 从一批被合成注解中,获取指定名称与类型的属性值 @@ -16,9 +16,9 @@ public interface SynthesizedAnnotationAttributeProcessor 属性类型 + * @param 属性类型 * @return 属性值 */ - T getAttributeValue(String attributeName, Class attributeType, Collection synthesizedAnnotations); + 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 index 65b76cf61..b6383fe30 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationSelector.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationSelector.java @@ -36,14 +36,14 @@ public interface SynthesizedAnnotationSelector { * @param newAnnotation 新获取的注解,该参数不允许为空 * @return 被合成的注解 */ - > A choose(A oldAnnotation, A newAnnotation); + T choose(T oldAnnotation, T newAnnotation); /** * 返回距离根对象更近的注解,当距离一样时优先返回旧注解 */ class NearestAndOldestPrioritySelector implements SynthesizedAnnotationSelector { @Override - public > A choose(A oldAnnotation, A newAnnotation) { + public T choose(T oldAnnotation, T newAnnotation) { return newAnnotation.getVerticalDistance() < oldAnnotation.getVerticalDistance() ? newAnnotation : oldAnnotation; } } @@ -53,7 +53,7 @@ public interface SynthesizedAnnotationSelector { */ class NearestAndNewestPrioritySelector implements SynthesizedAnnotationSelector { @Override - public > A choose(A oldAnnotation, A newAnnotation) { + public T choose(T oldAnnotation, T newAnnotation) { return newAnnotation.getVerticalDistance() <= oldAnnotation.getVerticalDistance() ? newAnnotation : oldAnnotation; } } @@ -63,7 +63,7 @@ public interface SynthesizedAnnotationSelector { */ class FarthestAndOldestPrioritySelector implements SynthesizedAnnotationSelector { @Override - public > A choose(A oldAnnotation, A newAnnotation) { + public T choose(T oldAnnotation, T newAnnotation) { return newAnnotation.getVerticalDistance() > oldAnnotation.getVerticalDistance() ? newAnnotation : oldAnnotation; } } @@ -73,7 +73,7 @@ public interface SynthesizedAnnotationSelector { */ class FarthestAndNewestPrioritySelector implements SynthesizedAnnotationSelector { @Override - public > A choose(A oldAnnotation, A newAnnotation) { + 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 084c12566..420e0f5c9 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 @@ -6,10 +6,9 @@ import java.lang.reflect.AnnotatedElement; /** * 表示基于特定规则聚合的一组注解 * - * @param 合成注解类型 * @author huangchengxing */ -public interface SyntheticAnnotation> extends Annotation, AnnotatedElement { +public interface SyntheticAnnotation extends Annotation, AnnotatedElement { /** * 获取合成注解选择器 @@ -23,16 +22,15 @@ public interface SyntheticAnnotation> extends * * @return 合成注解属性处理器 */ - SynthesizedAnnotationAttributeProcessor getAttributeProcessor(); + SynthesizedAnnotationAttributeProcessor getAttributeProcessor(); /** * 获取已合成的注解 * * @param annotationType 注解类型 - * @param 注解类型 * @return 已合成的注解 */ - SynthesizedAnnotation getSynthesizedAnnotation(Class annotationType); + SynthesizedAnnotation getSynthesizedAnnotation(Class annotationType); /** * 获取当前的注解类型 @@ -96,8 +94,8 @@ public interface SyntheticAnnotation> extends * @param 注解类型 * @return 合成注解 */ - static SyntheticAnnotation of(T rootAnnotation) { - return new SyntheticMetaAnnotation<>(rootAnnotation); + static SyntheticAnnotation of(T rootAnnotation) { + return new SyntheticMetaAnnotation(rootAnnotation); } } diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticAnnotationProxy.java b/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticAnnotationProxy.java index 101835f47..d27a650f4 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticAnnotationProxy.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticAnnotationProxy.java @@ -20,16 +20,15 @@ import java.util.stream.Stream; /** * 合成注解代理类 * - * @param 代理的注解类型 * @author huangchengxing */ -class SyntheticAnnotationProxy implements InvocationHandler { +class SyntheticAnnotationProxy implements InvocationHandler { - private final SyntheticAnnotation syntheticAnnotation; - private final SynthesizedAnnotation annotation; + private final SyntheticAnnotation syntheticAnnotation; + private final SynthesizedAnnotation annotation; private final Map> methods; - SyntheticAnnotationProxy(SyntheticAnnotation syntheticAnnotation, SynthesizedAnnotation annotation) { + SyntheticAnnotationProxy(SyntheticAnnotation syntheticAnnotation, SynthesizedAnnotation annotation) { this.syntheticAnnotation = syntheticAnnotation; this.annotation = annotation; this.methods = new HashMap<>(9); @@ -45,18 +44,17 @@ class SyntheticAnnotationProxy implements InvocationHandle * * @param annotationType 注解类型 * @param syntheticAnnotation 合成注解 - * @param 代理的注解类型 * @return 代理注解 */ @SuppressWarnings("unchecked") - static A create( - Class annotationType, SyntheticAnnotation syntheticAnnotation) { - final SynthesizedAnnotation annotation = syntheticAnnotation.getSynthesizedAnnotation(annotationType); - final SyntheticAnnotationProxy proxyHandler = new SyntheticAnnotationProxy<>(syntheticAnnotation, annotation); + 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 (A) Proxy.newProxyInstance( + return (T) Proxy.newProxyInstance( annotationType.getClassLoader(), new Class[]{annotationType, SyntheticProxyAnnotation.class}, proxyHandler @@ -122,21 +120,21 @@ class SyntheticAnnotationProxy implements InvocationHandle * * @author huangchengxing */ - interface SyntheticProxyAnnotation> extends SynthesizedAnnotation { + interface SyntheticProxyAnnotation extends SynthesizedAnnotation { /** * 获取该注解所属的合成注解 * * @return 合成注解 */ - SyntheticAnnotation getSyntheticAnnotation(); + SyntheticAnnotation getSyntheticAnnotation(); /** * 获取该代理注解对应的已合成注解 * * @return 理注解对应的已合成注解 */ - SynthesizedAnnotation getSynthesizedAnnotation(); + 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 index 708fc8fe1..d4ef3ce95 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 @@ -43,17 +43,17 @@ import java.util.Map; * @see AnnotationUtil * @see SynthesizedAnnotationSelector */ -public class SyntheticMetaAnnotation implements SyntheticAnnotation { +public class SyntheticMetaAnnotation implements SyntheticAnnotation { /** * 根注解,即当前查找的注解 */ - private final A source; + private final Annotation source; /** * 包含根注解以及其元注解在内的全部注解实例 */ - private final Map, MetaAnnotation> metaAnnotationMap; + private final Map, SynthesizedAnnotation> metaAnnotationMap; /** * 合成注解选择器 @@ -63,7 +63,7 @@ public class SyntheticMetaAnnotation implements SyntheticA /** * 合成注解属性处理器 */ - private final SynthesizedAnnotationAttributeProcessor attributeProcessor; + private final SynthesizedAnnotationAttributeProcessor attributeProcessor; /** * 基于指定根注解,为其层级结构中的全部注解构造一个合成注解。 @@ -72,12 +72,12 @@ public class SyntheticMetaAnnotation implements SyntheticA * * @param source 源注解 */ - public SyntheticMetaAnnotation(A source) { + public SyntheticMetaAnnotation(Annotation source) { this( source, SynthesizedAnnotationSelector.NEAREST_AND_OLDEST_PRIORITY, - new CacheableSynthesizedAnnotationAttributeProcessor<>( - Comparator.comparing(MetaAnnotation::getVerticalDistance) - .thenComparing(MetaAnnotation::getHorizontalDistance) + new CacheableSynthesizedAnnotationAttributeProcessor( + Comparator.comparing(SynthesizedAnnotation::getVerticalDistance) + .thenComparing(SynthesizedAnnotation::getHorizontalDistance) ) ); } @@ -89,9 +89,9 @@ public class SyntheticMetaAnnotation implements SyntheticA * @param annotationSelector 合成注解选择器 */ public SyntheticMetaAnnotation( - A annotation, + Annotation annotation, SynthesizedAnnotationSelector annotationSelector, - SynthesizedAnnotationAttributeProcessor attributeProcessor) { + SynthesizedAnnotationAttributeProcessor attributeProcessor) { Assert.notNull(annotation, "annotation must not null"); Assert.notNull(annotationSelector, "annotationSelector must not null"); Assert.notNull(attributeProcessor, "attributeProcessor must not null"); @@ -108,7 +108,7 @@ public class SyntheticMetaAnnotation implements SyntheticA * * @return 根注解 */ - public A getSource() { + public Annotation getSource() { return source; } @@ -117,7 +117,7 @@ public class SyntheticMetaAnnotation implements SyntheticA * * @return 已解析的元注解信息 */ - Map, MetaAnnotation> getMetaAnnotationMap() { + Map, SynthesizedAnnotation> getMetaAnnotationMap() { return metaAnnotationMap; } @@ -137,8 +137,8 @@ public class SyntheticMetaAnnotation implements SyntheticA * @return 合成注解属性处理器 */ @Override - public SynthesizedAnnotationAttributeProcessor getAttributeProcessor() { - return null; + public SynthesizedAnnotationAttributeProcessor getAttributeProcessor() { + return this.attributeProcessor; } /** @@ -149,8 +149,8 @@ public class SyntheticMetaAnnotation implements SyntheticA */ @SuppressWarnings("unchecked") @Override - public SynthesizedAnnotation getSynthesizedAnnotation(Class annotationType) { - return (SynthesizedAnnotation)metaAnnotationMap.get(annotationType); + public SynthesizedAnnotation getSynthesizedAnnotation(Class annotationType) { + return metaAnnotationMap.get(annotationType); } /** @@ -187,7 +187,7 @@ public class SyntheticMetaAnnotation implements SyntheticA public T getAnnotation(Class annotationType) { return Opt.ofNullable(annotationType) .map(metaAnnotationMap::get) - .map(MetaAnnotation::getAnnotation) + .map(SynthesizedAnnotation::getAnnotation) .map(annotationType::cast) .orElse(null); } @@ -244,8 +244,8 @@ public class SyntheticMetaAnnotation implements SyntheticA metaAnnotationMap.put(source.annotationType(), new MetaAnnotation(source, source, 0, 0)); new MetaAnnotationScanner().scan( (index, annotation) -> { - MetaAnnotation oldAnnotation = metaAnnotationMap.get(annotation.annotationType()); - MetaAnnotation newAnnotation = new MetaAnnotation(source, annotation, index, metaAnnotationMap.size()); + 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 { @@ -261,7 +261,7 @@ public class SyntheticMetaAnnotation implements SyntheticA * * @author huangchengxing */ - public static class MetaAnnotation implements Annotation, SynthesizedAnnotation { + public static class MetaAnnotation implements Annotation, SynthesizedAnnotation { private final Annotation root; private final Annotation annotation; 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 e85862be9..009f1e205 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 @@ -3,8 +3,10 @@ package cn.hutool.core.annotation; import org.junit.Assert; import org.junit.Test; -import java.lang.annotation.*; -import java.util.Map; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** * 合成注解{@link SyntheticMetaAnnotation}的测试用例 @@ -16,7 +18,7 @@ public class SyntheticMetaAnnotationTest { @Test public void testSynthesisAnnotation() { ChildAnnotation rootAnnotation = AnnotatedClass.class.getAnnotation(ChildAnnotation.class); - SyntheticMetaAnnotation syntheticMetaAnnotation = new SyntheticMetaAnnotation<>(rootAnnotation); + SyntheticMetaAnnotation syntheticMetaAnnotation = new SyntheticMetaAnnotation(rootAnnotation); Assert.assertEquals(syntheticMetaAnnotation.getSource(), rootAnnotation); Assert.assertEquals(syntheticMetaAnnotation.annotationType(), SyntheticMetaAnnotation.class); Assert.assertEquals(1, syntheticMetaAnnotation.getDeclaredAnnotations().length); @@ -28,28 +30,27 @@ public class SyntheticMetaAnnotationTest { Assert.assertEquals("Child's Parent!", syntheticMetaAnnotation.getAttribute("parentValue", String.class)); Assert.assertEquals("Child's GrandParent!", syntheticMetaAnnotation.getAttribute("grandParentValue", String.class)); - Map, SyntheticMetaAnnotation.MetaAnnotation> annotationMap = syntheticMetaAnnotation.getMetaAnnotationMap(); 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)); + 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)); + 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)); + Assert.assertThrows(IllegalArgumentException.class, () -> new SyntheticMetaAnnotation(grandParentAnnotation)); } // 注解结构如下: From 749ecb0e7da86069a30631317e3dafdaecae73d9 Mon Sep 17 00:00:00 2001 From: huangchengxing <841396397@qq.com> Date: Tue, 5 Jul 2022 16:53:58 +0800 Subject: [PATCH 7/8] fix comment --- .../annotation/SynthesizedAnnotation.java | 7 ++++--- ...nthesizedAnnotationAttributeProcessor.java | 6 +++--- .../core/annotation/SyntheticAnnotation.java | 20 ++++++++++++++++++- .../annotation/SyntheticAnnotationProxy.java | 2 +- 4 files changed, 27 insertions(+), 8 deletions(-) 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 8d81701b0..62a3dc91a 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotation.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotation.java @@ -3,23 +3,24 @@ package cn.hutool.core.annotation; import java.lang.annotation.Annotation; /** - * 表示一个处于合成状态的注解对象 + * 用于在{@link SyntheticAnnotation}中表示一个处于合成状态的注解对象 * * @author huangchengxing + * @see SyntheticAnnotation */ public interface SynthesizedAnnotation extends Annotation { /** * 获取该合成注解对应的根节点 * - * @return 数据源 + * @return 合成注解对应的根节点 */ Object getRoot(); /** * 该合成注解是为根对象 * - * @return 对象 + * @return 根对象 */ default boolean isRoot() { return getRoot() == this; diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationAttributeProcessor.java b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationAttributeProcessor.java index 83f7c274b..ad0464f5b 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationAttributeProcessor.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationAttributeProcessor.java @@ -3,7 +3,7 @@ package cn.hutool.core.annotation; import java.util.Collection; /** - * 合成注解属性选择器。用于中合成注解中从指定类型的注解里获取到对应的属性值 + * 合成注解属性选择器。用于在{@link SyntheticAnnotation}中从指定类型的合成注解里获取到对应的属性值 * * @author huangchengxing */ @@ -13,8 +13,8 @@ public interface SynthesizedAnnotationAttributeProcessor { /** * 从一批被合成注解中,获取指定名称与类型的属性值 * - * @param attributeName 属性名称 - * @param attributeType 属性类型 + * @param attributeName 属性名称 + * @param attributeType 属性类型 * @param synthesizedAnnotations 被合成的注解 * @param 属性类型 * @return 属性值 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 420e0f5c9..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 @@ -4,9 +4,27 @@ import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; /** - * 表示基于特定规则聚合的一组注解 + * 表示基于特定规则聚合的一组注解对象 + * + *

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

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

合成注解允许通过{@link #syntheticAnnotation(Class)}合成一个指定的注解对象, + * 该方法返回的注解对象可能是原始的注解对象,也有可能通过动态代理的方式生成, + * 该对象实例的属性不一定来自对象本身,而是来自于经过{@link SynthesizedAnnotationAttributeProcessor} + * 处理后的、用于合成当前实例的全部关联注解的相关属性。 * * @author huangchengxing + * @see SynthesizedAnnotation + * @see SynthesizedAnnotationSelector + * @see SynthesizedAnnotationAttributeProcessor + * @see SyntheticMetaAnnotation */ public interface SyntheticAnnotation extends Annotation, AnnotatedElement { diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticAnnotationProxy.java b/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticAnnotationProxy.java index d27a650f4..5cf2d1723 100644 --- a/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticAnnotationProxy.java +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/SyntheticAnnotationProxy.java @@ -86,7 +86,7 @@ class SyntheticAnnotationProxy implements InvocationHandler { 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 (Method declaredMethod : annotation.getAnnotation().annotationType().getDeclaredMethods()) { + for (final Method declaredMethod : annotation.getAnnotation().annotationType().getDeclaredMethods()) { methods.put(declaredMethod.getName(), (method, args) -> proxyAttributeValue(method)); } } From 66201839e3cb9abff1e66678e93493d00a60c776 Mon Sep 17 00:00:00 2001 From: huangchengxing <841396397@qq.com> Date: Tue, 5 Jul 2022 17:00:09 +0800 Subject: [PATCH 8/8] fix comment --- .../core/annotation/SyntheticMetaAnnotation.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) 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 d4ef3ce95..c2a7a97d8 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 @@ -85,8 +85,9 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation { /** * 基于指定根注解,为其层级结构中的全部注解构造一个合成注解 * - * @param annotation 当前查找的注解类 - * @param annotationSelector 合成注解选择器 + * @param annotation 当前查找的注解对象 + * @param annotationSelector 合成注解选择器 + * @param attributeProcessor 注解属性处理器 */ public SyntheticMetaAnnotation( Annotation annotation, @@ -147,7 +148,6 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation { * @param annotationType 注解类型 * @return 已合成的注解 */ - @SuppressWarnings("unchecked") @Override public SynthesizedAnnotation getSynthesizedAnnotation(Class annotationType) { return metaAnnotationMap.get(annotationType); @@ -308,15 +308,20 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation { } /** - * 获取根注解到元注解的距离 + * 获取该合成注解与根注解之间相隔的层级数 * - * @return 根注解到元注解的距离 + * @return 该合成注解与根注解之间相隔的层级数 */ @Override public int getVerticalDistance() { return verticalDistance; } + /** + * 获取该合成注解与根注解之间相隔的注解树 + * + * @return 该合成注解与根注解之间相隔的注解树 + */ @Override public int getHorizontalDistance() { return horizontalDistance;