From 77e065f302480d56fcc63f0917c411d8d2a4a4ad Mon Sep 17 00:00:00 2001 From: huangchengxing <841396397@qq.com> Date: Tue, 13 Sep 2022 13:57:46 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=94=AF=E6=8C=81=E5=A4=84?= =?UTF-8?q?=E7=90=86=E5=85=83=E6=B3=A8=E8=A7=A3=E7=9A=84=E5=A2=9E=E5=BC=BA?= =?UTF-8?q?AnnotatedElement=E5=8C=85=E8=A3=85=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/annotation/MetaAnnotatedElement.java | 322 ++++++++++++++++++ .../annotation/MetaAnnotatedElementTest.java | 208 +++++++++++ 2 files changed, 530 insertions(+) create mode 100644 hutool-core/src/main/java/cn/hutool/core/annotation/MetaAnnotatedElement.java create mode 100644 hutool-core/src/test/java/cn/hutool/core/annotation/MetaAnnotatedElementTest.java diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/MetaAnnotatedElement.java b/hutool-core/src/main/java/cn/hutool/core/annotation/MetaAnnotatedElement.java new file mode 100644 index 000000000..26e0567cc --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/annotation/MetaAnnotatedElement.java @@ -0,0 +1,322 @@ +package cn.hutool.core.annotation; + +import cn.hutool.core.stream.EasyStream; +import cn.hutool.core.text.CharSequenceUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjUtil; + +import java.lang.annotation.Annotation; +import java.lang.annotation.Inherited; +import java.lang.reflect.AnnotatedElement; +import java.util.*; +import java.util.function.BiFunction; +import java.util.stream.Stream; + +/** + *

注解元素映射,用于包装一个{@link AnnotatedElement},然后将被包装的元素上, + * 直接声明的注解以及这些注解的元组全部解析为{@link ResolvedAnnotationMapping}。 + * 从而用于支持对元注解的访问操作。
+ * + *

默认情况下,总是不扫描{@link java.lang}包下的注解, + * 并且在当前实例中,{@link Inherited}注解将不生效, + * 即通过directly方法将无法获得父类上带有{@link Inherited}的注解。 + * + *

当通过静态工厂方法创建时,该实例与关联的{@link ResolvedAnnotationMapping}都会针对{@link ResolvedAnnotationMapping}进行缓存, + * 从而避免频繁的反射与代理造成不必要的性能损耗。 + * + * @author huangchengxing + * @see ResolvedAnnotationMapping + * @since 6.0.0 + */ +public class MetaAnnotatedElement> implements AnnotatedElement, Iterable { + + /** + * 注解对象 + */ + private final AnnotatedElement element; + + /** + * 创建{@link AnnotationMapping}的工厂方法,返回值为{@code null}时将忽略该注解 + */ + private final BiFunction mappingFactory; + + /** + * 注解映射,此处为懒加载,默认为{@code null},获取该属性必须通过{@link #getAnnotationMappings()}触发初始化 + */ + private volatile Map, T> annotationMappings; + + /** + * 获取{@link AnnotatedElement}上的注解结构,该方法会针对相同的{@link AnnotatedElement}缓存映射对象 + * + * @param element 被注解元素 + * @param mappingFactory 创建{@link AnnotationMapping}的工厂方法,返回值为{@code null}时将忽略该注解 + * @param {@link AnnotationMapping}类型 + * @return {@link AnnotatedElement}上的注解结构 + */ + public static > MetaAnnotatedElement create( + final AnnotatedElement element, final BiFunction mappingFactory) { + return new MetaAnnotatedElement<>(element, mappingFactory); + } + + /** + * 解析注解属性 + * + * @param element 被注解元素 + * @param mappingFactory 创建{@link AnnotationMapping}的工厂方法,返回值为{@code null}时将忽略该注解 + */ + MetaAnnotatedElement(final AnnotatedElement element, final BiFunction mappingFactory) { + this.element = Objects.requireNonNull(element); + this.mappingFactory = Objects.requireNonNull(mappingFactory); + // 等待懒加载 + this.annotationMappings = null; + } + + /** + * 从{@link AnnotatedElement}直接声明的注解的层级结构中获得注解映射对象 + * + * @param annotationType 注解类型 + * @return 注解映射对象 + */ + public Optional getMapping(final Class annotationType) { + return Optional.ofNullable(annotationType) + .map(getAnnotationMappings()::get); + } + + /** + * 获取被包装的{@link AnnotatedElement} + * + * @return 被包装的{@link AnnotatedElement} + */ + public AnnotatedElement getElement() { + return element; + } + + /** + * 从{@link AnnotatedElement}直接声明的注解中获得注解映射对象 + * + * @param annotationType 注解类型 + * @return 注解映射对象 + */ + public Optional getDeclaredMapping(final Class annotationType) { + return EasyStream.of(getAnnotationMappings().values()) + .filter(T::isRoot) + .findFirst(mapping -> ObjUtil.equals(annotationType, mapping.annotationType())); + } + + /** + * 注解是否是{@link AnnotatedElement}直接声明的注解,或者在这些注解的层级结构中存在 + * + * @param annotationType 注解元素 + * @return 是否 + */ + @Override + public boolean isAnnotationPresent(Class annotationType) { + return getMapping(annotationType) + .isPresent(); + } + + /** + * 从{@link AnnotatedElement}直接声明的注解的层级结构中获得注解对象 + * + * @param annotationType 注解类型 + * @param 注解类型 + * @return 注解对象 + */ + @Override + public A getAnnotation(final Class annotationType) { + return getMapping(annotationType) + .map(T::getResolvedAnnotation) + .map(annotationType::cast) + .orElse(null); + } + + /** + * 从{@link AnnotatedElement}直接声明的注解中获得注解对象 + * + * @param annotationType 注解类型 + * @param 注解类型 + * @return 注解对象 + */ + @Override + public A getDeclaredAnnotation(final Class annotationType) { + return getDeclaredMapping(annotationType) + .map(T::getResolvedAnnotation) + .map(annotationType::cast) + .orElse(null); + } + + /** + * 获取{@link AnnotatedElement}直接的指定类型注解 + * + * @param annotationType 注解类型 + * @param 注解类型 + * @return {@link AnnotatedElement}直接声明的指定类型注解 + */ + @SuppressWarnings("unchecked") + @Override + public A[] getAnnotationsByType(final Class annotationType) { + final A result = getAnnotation(annotationType); + if (Objects.nonNull(result)) { + return (A[])new Annotation[]{ result }; + } + return ArrayUtil.newArray(annotationType, 0); + } + + /** + * 获取{@link AnnotatedElement}直接声明的指定类型注解 + * + * @param annotationType 注解类型 + * @param 注解类型 + * @return {@link AnnotatedElement}直接声明的指定类型注解 + */ + @SuppressWarnings("unchecked") + @Override + public A[] getDeclaredAnnotationsByType(final Class annotationType) { + final A result = getDeclaredAnnotation(annotationType); + if (Objects.nonNull(result)) { + return (A[])new Annotation[]{ result }; + } + return ArrayUtil.newArray(annotationType, 0); + } + + /** + * 获取{@link AnnotatedElement}直接声明的注解的映射对象 + * + * @return {@link AnnotatedElement}直接声明的注解的映射对象 + */ + @Override + public Annotation[] getDeclaredAnnotations() { + return getAnnotationMappings().values().stream() + .filter(T::isRoot) + .map(T::getResolvedAnnotation) + .toArray(Annotation[]::new); + } + + /** + * 获取所有注解 + * + * @return 所有注解 + */ + @Override + public Annotation[] getAnnotations() { + return getAnnotationMappings().values().stream() + .map(T::getResolvedAnnotation) + .toArray(Annotation[]::new); + } + + /** + * 获取注解映射对象集合的迭代器 + * + * @return 迭代器 + */ + @Override + public Iterator iterator() { + return getAnnotationMappings().values().iterator(); + } + + // ========================= protected ========================= + + /** + * 获取注解映射,若当前实例未完成初始化则先进行初始化 + * + * @return 不可变的注解映射集合 + */ + protected final Map, T> getAnnotationMappings() { + initAnnotationMappingsIfNecessary(); + return annotationMappings; + } + + /** + * 该注解是否需要映射
+ * 默认情况下,已经处理过、或在{@link java.lang}包下的注解不会被处理 + * + * @param mappings 当前已处理的注解 + * @param annotation 注解对象 + * @return 是否 + */ + protected boolean isNeedMapping(final Map, T> mappings, final Annotation annotation) { + return !CharSequenceUtil.startWith(annotation.annotationType().getName(), "java.lang.") + && !mappings.containsKey(annotation.annotationType()); + } + + // ========================= private ========================= + + /** + * 创建注解映射 + */ + private T createMapping(final T source, final Annotation annotation) { + return mappingFactory.apply(source, annotation); + } + + /** + * 扫描{@link AnnotatedElement}上直接声明的注解,然后按广度优先扫描这些注解的元注解, + * 直到将所有类型的注解对象皆加入{@link #annotationMappings}为止 + */ + private void initAnnotationMappingsIfNecessary() { + // 双重检查保证初始化过程线程安全 + if (Objects.isNull(annotationMappings)) { + synchronized (this) { + if (Objects.isNull(annotationMappings)) { + Map, T> mappings = new LinkedHashMap<>(8); + initAnnotationMappings(mappings); + this.annotationMappings = Collections.unmodifiableMap(mappings); + } + } + } + } + + /** + * 初始化 + */ + private void initAnnotationMappings(final Map, T> mappings) { + final Deque deque = new LinkedList<>(); + Arrays.stream(element.getDeclaredAnnotations()) + .filter(m -> isNeedMapping(mappings, m)) + .map(annotation -> createMapping(null, annotation)) + .filter(Objects::nonNull) + .forEach(deque::addLast); + while (!deque.isEmpty()) { + // 若已有该类型的注解,则不再进行扫描 + T mapping = deque.removeFirst(); + if (!isNeedMapping(mappings, mapping)) { + continue; + } + // 保存该注解,并将其需要处理的元注解也加入队列 + mappings.put(mapping.annotationType(), mapping); + Stream.of(mapping.annotationType().getDeclaredAnnotations()) + .map(annotation -> createMapping(mapping, annotation)) + .filter(Objects::nonNull) + .filter(m -> isNeedMapping(mappings, m)) + .forEach(deque::addLast); + } + } + + /** + * 比较两个实例是否相等 + * + * @param o 对象 + * @return 是否 + */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + MetaAnnotatedElement that = (MetaAnnotatedElement)o; + return element.equals(that.element) && mappingFactory.equals(that.mappingFactory); + } + + /** + * 获取实例的哈希值 + * + * @return 哈希值 + */ + + @Override + public int hashCode() { + return Objects.hash(element, mappingFactory); + } +} diff --git a/hutool-core/src/test/java/cn/hutool/core/annotation/MetaAnnotatedElementTest.java b/hutool-core/src/test/java/cn/hutool/core/annotation/MetaAnnotatedElementTest.java new file mode 100644 index 000000000..fd8a2f3cc --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/annotation/MetaAnnotatedElementTest.java @@ -0,0 +1,208 @@ +package cn.hutool.core.annotation; + +import org.junit.Assert; +import org.junit.Test; + +import java.lang.annotation.*; +import java.lang.reflect.AnnotatedElement; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.function.BiFunction; + +/** + * test for {@link MetaAnnotatedElement} + * + * @author huangchengxing + */ +public class MetaAnnotatedElementTest { + + private static final BiFunction RESOLVED_MAPPING_FACTORY = + (source, annotation) -> new ResolvedAnnotationMapping(source, annotation, true); + + private static final BiFunction MAPPING_FACTORY = + (source, annotation) -> new ResolvedAnnotationMapping(source, annotation, false); + + @Test + public void testEquals() { + AnnotatedElement element = new MetaAnnotatedElement<>(Foo.class, RESOLVED_MAPPING_FACTORY); + Assert.assertEquals(element, element); + Assert.assertNotEquals(element, null); + Assert.assertEquals(element, new MetaAnnotatedElement<>(Foo.class, RESOLVED_MAPPING_FACTORY)); + Assert.assertNotEquals(element, new MetaAnnotatedElement<>(Foo.class, MAPPING_FACTORY)); + Assert.assertNotEquals(element, new MetaAnnotatedElement<>(Annotation1.class, MAPPING_FACTORY)); + } + + @Test + public void testHashCode() { + int hashCode = new MetaAnnotatedElement<>(Foo.class, RESOLVED_MAPPING_FACTORY).hashCode(); + Assert.assertEquals(hashCode, new MetaAnnotatedElement<>(Foo.class, RESOLVED_MAPPING_FACTORY).hashCode()); + Assert.assertNotEquals(hashCode, new MetaAnnotatedElement<>(Foo.class, MAPPING_FACTORY).hashCode()); + } + + @Test + public void testCreate() { + // 第二次创建时优先从缓存中获取 + AnnotatedElement resolvedElement = MetaAnnotatedElement.create(Foo.class, RESOLVED_MAPPING_FACTORY); + Assert.assertEquals(resolvedElement, MetaAnnotatedElement.create(Foo.class, RESOLVED_MAPPING_FACTORY)); + AnnotatedElement element = MetaAnnotatedElement.create(Foo.class, MAPPING_FACTORY); + Assert.assertEquals(element, MetaAnnotatedElement.create(Foo.class, MAPPING_FACTORY)); + } + + @Test + public void testGetMapping() { + MetaAnnotatedElement element = MetaAnnotatedElement.create(Foo.class, MAPPING_FACTORY); + Assert.assertTrue(element.getMapping(Annotation1.class).isPresent()); + Assert.assertTrue(element.getMapping(Annotation2.class).isPresent()); + Assert.assertTrue(element.getMapping(Annotation3.class).isPresent()); + Assert.assertTrue(element.getMapping(Annotation4.class).isPresent()); + } + + @Test + public void testGetDeclaredMapping() { + MetaAnnotatedElement element = MetaAnnotatedElement.create(Foo.class, MAPPING_FACTORY); + Assert.assertFalse(element.getDeclaredMapping(Annotation1.class).isPresent()); + Assert.assertFalse(element.getDeclaredMapping(Annotation2.class).isPresent()); + Assert.assertTrue(element.getDeclaredMapping(Annotation3.class).isPresent()); + Assert.assertTrue(element.getDeclaredMapping(Annotation4.class).isPresent()); + } + + @Test + public void testIsAnnotationPresent() { + MetaAnnotatedElement element = MetaAnnotatedElement.create(Foo.class, MAPPING_FACTORY); + Assert.assertTrue(element.isAnnotationPresent(Annotation1.class)); + Assert.assertTrue(element.isAnnotationPresent(Annotation2.class)); + Assert.assertTrue(element.isAnnotationPresent(Annotation3.class)); + Assert.assertTrue(element.isAnnotationPresent(Annotation4.class)); + } + + @Test + public void testGetAnnotation() { + MetaAnnotatedElement element = MetaAnnotatedElement.create(Foo.class, MAPPING_FACTORY); + Annotation4 annotation4 = Foo.class.getAnnotation(Annotation4.class); + Annotation3 annotation3 = Foo.class.getAnnotation(Annotation3.class); + Annotation2 annotation2 = Annotation3.class.getAnnotation(Annotation2.class); + Annotation1 annotation1 = Annotation2.class.getAnnotation(Annotation1.class); + + Assert.assertEquals(annotation1, element.getAnnotation(Annotation1.class)); + Assert.assertEquals(annotation2, element.getAnnotation(Annotation2.class)); + Assert.assertEquals(annotation3, element.getAnnotation(Annotation3.class)); + Assert.assertEquals(annotation4, element.getAnnotation(Annotation4.class)); + } + + @Test + public void testGetDeclaredAnnotation() { + MetaAnnotatedElement element = MetaAnnotatedElement.create(Foo.class, MAPPING_FACTORY); + Annotation4 annotation4 = Foo.class.getAnnotation(Annotation4.class); + Annotation3 annotation3 = Foo.class.getAnnotation(Annotation3.class); + + Assert.assertNull(element.getDeclaredAnnotation(Annotation1.class)); + Assert.assertNull(element.getDeclaredAnnotation(Annotation2.class)); + Assert.assertEquals(annotation3, element.getDeclaredAnnotation(Annotation3.class)); + Assert.assertEquals(annotation4, element.getDeclaredAnnotation(Annotation4.class)); + } + + @Test + public void testGetAnnotationByType() { + MetaAnnotatedElement element = MetaAnnotatedElement.create(Foo.class, MAPPING_FACTORY); + Annotation4 annotation4 = Foo.class.getAnnotation(Annotation4.class); + Assert.assertArrayEquals( + new Annotation[]{ annotation4 }, + element.getAnnotationsByType(Annotation4.class) + ); + } + + @Test + public void testGetDeclaredAnnotationByType() { + MetaAnnotatedElement element = MetaAnnotatedElement.create(Foo.class, MAPPING_FACTORY); + Annotation4 annotation4 = Foo.class.getAnnotation(Annotation4.class); + Assert.assertArrayEquals( + new Annotation[]{ annotation4 }, + element.getDeclaredAnnotationsByType(Annotation4.class) + ); + } + + @Test + public void testGetAnnotations() { + MetaAnnotatedElement element = MetaAnnotatedElement.create(Foo.class, MAPPING_FACTORY); + Annotation4 annotation4 = Foo.class.getAnnotation(Annotation4.class); + Annotation3 annotation3 = Foo.class.getAnnotation(Annotation3.class); + Annotation2 annotation2 = Annotation3.class.getAnnotation(Annotation2.class); + Annotation1 annotation1 = Annotation2.class.getAnnotation(Annotation1.class); + Annotation[] annotations = new Annotation[]{ annotation3, annotation4, annotation2, annotation1 }; + + Assert.assertArrayEquals(annotations, element.getAnnotations()); + } + + @Test + public void testGetDeclaredAnnotations() { + MetaAnnotatedElement element = MetaAnnotatedElement.create(Foo.class, MAPPING_FACTORY); + Annotation4 annotation4 = Foo.class.getAnnotation(Annotation4.class); + Annotation3 annotation3 = Foo.class.getAnnotation(Annotation3.class); + Annotation[] annotations = new Annotation[]{ annotation3, annotation4 }; + + Assert.assertArrayEquals(annotations, element.getDeclaredAnnotations()); + } + + @Test + public void testIterator() { + MetaAnnotatedElement element = MetaAnnotatedElement.create(Foo.class, MAPPING_FACTORY); + Annotation4 annotation4 = Foo.class.getAnnotation(Annotation4.class); + Annotation3 annotation3 = Foo.class.getAnnotation(Annotation3.class); + Annotation2 annotation2 = Annotation3.class.getAnnotation(Annotation2.class); + Annotation1 annotation1 = Annotation2.class.getAnnotation(Annotation1.class); + Annotation[] annotations = new Annotation[]{ annotation3, annotation4, annotation2, annotation1 }; + + Iterator iterator = element.iterator(); + List mappingList = new ArrayList<>(); + iterator.forEachRemaining(mapping -> mappingList.add(mapping.getAnnotation())); + + Assert.assertEquals(Arrays.asList(annotations), mappingList); + } + + @Test + public void testGetElement() { + AnnotatedElement source = Foo.class; + MetaAnnotatedElement element = MetaAnnotatedElement.create(source, MAPPING_FACTORY); + Assert.assertSame(source, element.getElement()); + } + + @Target(ElementType.TYPE_USE) + @Retention(RetentionPolicy.RUNTIME) + private @interface Annotation1 { + @Alias("name") + String value() default ""; + @Alias("value") + String name() default ""; + } + + @Annotation1("Annotation2") + @Target(ElementType.TYPE_USE) + @Retention(RetentionPolicy.RUNTIME) + private @interface Annotation2 { + @Alias("name") + String value() default ""; + @Alias("value") + String name() default ""; + } + + @Annotation2("Annotation3") + @Target(ElementType.TYPE_USE) + @Retention(RetentionPolicy.RUNTIME) + private @interface Annotation3 { + String name() default ""; + } + + @Annotation2("Annotation4") + @Target(ElementType.TYPE_USE) + @Retention(RetentionPolicy.RUNTIME) + private @interface Annotation4 { + String value() default ""; + } + + @Annotation3(name = "foo") + @Annotation4("foo") + private static class Foo {}; + +}