添加支持处理元注解的增强AnnotatedElement包装器

This commit is contained in:
huangchengxing 2022-09-13 13:57:46 +08:00
parent a1a199513f
commit 77e065f302
2 changed files with 530 additions and 0 deletions

View File

@ -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;
/**
* <p>注解元素映射用于包装一个{@link AnnotatedElement}然后将被包装的元素上
* 直接声明的注解以及这些注解的元组全部解析为{@link ResolvedAnnotationMapping}
* 从而用于支持对元注解的访问操作<br>
*
* <p>默认情况下总是不扫描{@link java.lang}包下的注解
* 并且在当前实例中{@link Inherited}注解将不生效
* 即通过<em>directly</em>方法将无法获得父类上带有{@link Inherited}的注解
*
* <p>当通过静态工厂方法创建时该实例与关联的{@link ResolvedAnnotationMapping}都会针对{@link ResolvedAnnotationMapping}进行缓存
* 从而避免频繁的反射与代理造成不必要的性能损耗
*
* @author huangchengxing
* @see ResolvedAnnotationMapping
* @since 6.0.0
*/
public class MetaAnnotatedElement<T extends AnnotationMapping<Annotation>> implements AnnotatedElement, Iterable<T> {
/**
* 注解对象
*/
private final AnnotatedElement element;
/**
* 创建{@link AnnotationMapping}的工厂方法返回值为{@code null}时将忽略该注解
*/
private final BiFunction<T, Annotation, T> mappingFactory;
/**
* 注解映射此处为懒加载默认为{@code null}获取该属性必须通过{@link #getAnnotationMappings()}触发初始化
*/
private volatile Map<Class<? extends Annotation>, T> annotationMappings;
/**
* 获取{@link AnnotatedElement}上的注解结构该方法会针对相同的{@link AnnotatedElement}缓存映射对象
*
* @param element 被注解元素
* @param mappingFactory 创建{@link AnnotationMapping}的工厂方法返回值为{@code null}时将忽略该注解
* @param <A> {@link AnnotationMapping}类型
* @return {@link AnnotatedElement}上的注解结构
*/
public static <A extends AnnotationMapping<Annotation>> MetaAnnotatedElement<A> create(
final AnnotatedElement element, final BiFunction<A, Annotation, A> mappingFactory) {
return new MetaAnnotatedElement<>(element, mappingFactory);
}
/**
* 解析注解属性
*
* @param element 被注解元素
* @param mappingFactory 创建{@link AnnotationMapping}的工厂方法返回值为{@code null}时将忽略该注解
*/
MetaAnnotatedElement(final AnnotatedElement element, final BiFunction<T, Annotation, T> mappingFactory) {
this.element = Objects.requireNonNull(element);
this.mappingFactory = Objects.requireNonNull(mappingFactory);
// 等待懒加载
this.annotationMappings = null;
}
/**
* {@link AnnotatedElement}直接声明的注解的层级结构中获得注解映射对象
*
* @param annotationType 注解类型
* @return 注解映射对象
*/
public Optional<T> getMapping(final Class<? extends Annotation> 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<T> getDeclaredMapping(final Class<? extends Annotation> 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<? extends Annotation> annotationType) {
return getMapping(annotationType)
.isPresent();
}
/**
* {@link AnnotatedElement}直接声明的注解的层级结构中获得注解对象
*
* @param annotationType 注解类型
* @param <A> 注解类型
* @return 注解对象
*/
@Override
public <A extends Annotation> A getAnnotation(final Class<A> annotationType) {
return getMapping(annotationType)
.map(T::getResolvedAnnotation)
.map(annotationType::cast)
.orElse(null);
}
/**
* {@link AnnotatedElement}直接声明的注解中获得注解对象
*
* @param annotationType 注解类型
* @param <A> 注解类型
* @return 注解对象
*/
@Override
public <A extends Annotation> A getDeclaredAnnotation(final Class<A> annotationType) {
return getDeclaredMapping(annotationType)
.map(T::getResolvedAnnotation)
.map(annotationType::cast)
.orElse(null);
}
/**
* 获取{@link AnnotatedElement}直接的指定类型注解
*
* @param annotationType 注解类型
* @param <A> 注解类型
* @return {@link AnnotatedElement}直接声明的指定类型注解
*/
@SuppressWarnings("unchecked")
@Override
public <A extends Annotation> A[] getAnnotationsByType(final Class<A> 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 <A> 注解类型
* @return {@link AnnotatedElement}直接声明的指定类型注解
*/
@SuppressWarnings("unchecked")
@Override
public <A extends Annotation> A[] getDeclaredAnnotationsByType(final Class<A> 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<T> iterator() {
return getAnnotationMappings().values().iterator();
}
// ========================= protected =========================
/**
* 获取注解映射若当前实例未完成初始化则先进行初始化
*
* @return 不可变的注解映射集合
*/
protected final Map<Class<? extends Annotation>, T> getAnnotationMappings() {
initAnnotationMappingsIfNecessary();
return annotationMappings;
}
/**
* 该注解是否需要映射 <br>
* 默认情况下已经处理过或在{@link java.lang}包下的注解不会被处理
*
* @param mappings 当前已处理的注解
* @param annotation 注解对象
* @return 是否
*/
protected boolean isNeedMapping(final Map<Class<? extends Annotation>, 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<Class<? extends Annotation>, T> mappings = new LinkedHashMap<>(8);
initAnnotationMappings(mappings);
this.annotationMappings = Collections.unmodifiableMap(mappings);
}
}
}
}
/**
* 初始化
*/
private void initAnnotationMappings(final Map<Class<? extends Annotation>, T> mappings) {
final Deque<T> 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);
}
}

View File

@ -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<ResolvedAnnotationMapping, Annotation, ResolvedAnnotationMapping> RESOLVED_MAPPING_FACTORY =
(source, annotation) -> new ResolvedAnnotationMapping(source, annotation, true);
private static final BiFunction<ResolvedAnnotationMapping, Annotation, ResolvedAnnotationMapping> 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<ResolvedAnnotationMapping> 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<ResolvedAnnotationMapping> 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<ResolvedAnnotationMapping> 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<ResolvedAnnotationMapping> 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<ResolvedAnnotationMapping> 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<ResolvedAnnotationMapping> 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<ResolvedAnnotationMapping> 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<ResolvedAnnotationMapping> 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<ResolvedAnnotationMapping> 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<ResolvedAnnotationMapping> 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<ResolvedAnnotationMapping> iterator = element.iterator();
List<Annotation> 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<ResolvedAnnotationMapping> 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 {};
}