添加支持处理层级结构的增强AnnotatedElement包装器

This commit is contained in:
huangchengxing 2022-09-13 13:58:24 +08:00
parent 77e065f302
commit 4b38bc31d2
2 changed files with 596 additions and 0 deletions

View File

@ -0,0 +1,388 @@
package cn.hutool.core.annotation;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.reflect.ClassUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ArrayUtil;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.function.BiFunction;
import java.util.stream.Stream;
/**
* <p>表示一组处于在层级结构中具有关联关系的{@link AnnotatedElement}创建实例时
* 将扫描指定{@link AnnotatedElement}的层级结构中的所有{@link AnnotatedElement},
* 并将其包装为{@link MetaAnnotatedElement} <br>
* eg: <br>
* 若存在元素<em>A</em>有对应父类与父接口<em>B</em><em>C</em>
* 则根据<em>A</em>生成的{@link HierarchicalAnnotatedElements}实例将同时包含<em>A</em><em>B</em><em>C</em>,
* 该实例同时支持对这三个实例上直接声明的注解以及这些注解的元注解进行访问
*
* <p><strong>注解搜索范围</strong>
* <p>在当前实例中针对带有和不带<em>declared</em>关键字的方法定义如下
* <ul>
* <li>当方法带有<em>declared</em>关键字时查找范围仅限被保存的所有{@link AnnotatedElement}上直接声明的注解</li>
* <li>
* 当方法不带<em>declared</em>关键字时查找范围包括
* <ol>
* <li>被保存的所有{@link AnnotatedElement}上直接声明的注解及这些注解的元注解</li>
* <li>若是类则包括其所有父类和所有父接口上声明的注解和元注解</li>
* <li>
* 若是方法且不是静态/私有/<code>final</code>修饰的方法时
* 则额外获取包括其声明类的所有父类和所有父接口中与该方法具有相同方法签名的方法上的注解和元注解
* </li>
* </ol>
* </li>
* </ul>
*
* <p><strong>扫描顺序</strong>
* <p>{@link AnnotatedElement}具有层级结构式会按照广度优先扫描其本身(元素是{@link Class})
* 或其声明类(元素是{@link Method})的层级结构<br>
* 在该过程中总是先扫描父类再扫描父接口
* 若存在多个父接口则其扫描顺序遵循从{@link Class#getInterfaces()}获得该接口的顺序
*
* @author huangchengxing
* @since 6.0.0
*/
public class HierarchicalAnnotatedElements implements AnnotatedElement, Iterable<AnnotatedElement> {
/**
* 创建{@link AnnotatedElement}的工厂方法当返回{@code null}时将忽略该元素
*/
protected final BiFunction<Set<AnnotatedElement>, AnnotatedElement, AnnotatedElement> elementFactory;
/**
* 层级中的全部{@link AnnotatedElement}对象默认为懒加载需要通过{@link #getElementMappings()}触发初始化 <br>
* 该集合中的元素按照其与被包装的{@link AnnotatedElement}的距离和被按广度优先扫描的顺序排序
*/
private volatile Set<AnnotatedElement> elementMappings;
/**
* 被包装的{@link AnnotatedElement}对象
*/
protected final AnnotatedElement source;
/**
* 创建一个分层注解元素
*
* @param element 被包装的元素若元素已是{@link HierarchicalAnnotatedElements}则返回其本身
* @return {@link HierarchicalAnnotatedElements}实例
* {@code element}也是一个{@link HierarchicalAnnotatedElements}返回{@code element}本身
*/
public static HierarchicalAnnotatedElements create(final AnnotatedElement element) {
return create(element, (es, e) -> e);
}
/**
* 创建一个分层注解元素
*
* @param element 被包装的元素若元素已是{@link HierarchicalAnnotatedElements}则返回其本身
* @param elementFactory 创建{@link AnnotatedElement}的工厂方法当返回{@code null}时将忽略该元素
* @return {@link HierarchicalAnnotatedElements}实例
* {@code element}也是一个{@link HierarchicalAnnotatedElements}返回{@code element}本身
*/
public static HierarchicalAnnotatedElements create(
final AnnotatedElement element,
final BiFunction<Set<AnnotatedElement>, AnnotatedElement, AnnotatedElement> elementFactory) {
return element instanceof HierarchicalAnnotatedElements ?
(HierarchicalAnnotatedElements)element : new HierarchicalAnnotatedElements(element, elementFactory);
}
/**
* 构造
*
* @param element 被包装的元素
* @param elementFactory 创建{@link AnnotatedElement}的工厂方法当返回{@code null}时将忽略该元素
*/
HierarchicalAnnotatedElements(
final AnnotatedElement element,
final BiFunction<Set<AnnotatedElement>, AnnotatedElement, AnnotatedElement> elementFactory) {
this.source = Objects.requireNonNull(element);
// 懒加载
this.elementMappings = null;
this.elementFactory = Objects.requireNonNull(elementFactory);
}
/**
* 注解是否在层级结构中所有{@link AnnotatedElement}上的注解和元注解中存在
*
* @param annotationType 注解类型
* @return 是否
*/
@Override
public boolean isAnnotationPresent(final Class<? extends Annotation> annotationType) {
return getElementMappings().stream()
.anyMatch(element -> element.isAnnotationPresent(annotationType));
}
/**
* 从层级结构中所有{@link AnnotatedElement}上的注解和元注解中获取指定类型的注解
*
* @return 注解对象
*/
@Override
public Annotation[] getAnnotations() {
return getElementMappings().stream()
.map(AnnotatedElement::getAnnotations)
.filter(ArrayUtil::isNotEmpty)
.flatMap(Stream::of)
.toArray(Annotation[]::new);
}
/**
* 从层级结构中所有{@link AnnotatedElement}上的注解和元注解中获取指定类型的注解
*
* @param annotationType 注解类型
* @param <A> 注解类型
* @return 注解对象
*/
@Override
public <A extends Annotation> A getAnnotation(final Class<A> annotationType) {
return getElementMappings().stream()
.map(e -> e.getAnnotation(annotationType))
.filter(Objects::nonNull)
.findFirst()
.orElse(null);
}
/**
* 从层级结构中所有{@link AnnotatedElement}上的注解和元注解中获取指定类型的注解
*
* @param annotationType 注解类型
* @param <A> 注解类型
* @return 注解对象
*/
@SuppressWarnings("unchecked")
@Override
public <A extends Annotation> A[] getAnnotationsByType(final Class<A> annotationType) {
return getElementMappings().stream()
.map(e -> e.getAnnotationsByType(annotationType))
.filter(ArrayUtil::isNotEmpty)
.flatMap(Stream::of)
.toArray(size -> ArrayUtil.newArray(annotationType, size));
}
/**
* 获取层级结构中所有{@link AnnotatedElement}上直接声明的注解
*
* @return 注解对象
*/
@Override
public Annotation[] getDeclaredAnnotations() {
return getElementMappings().stream()
.map(AnnotatedElement::getDeclaredAnnotations)
.filter(ArrayUtil::isNotEmpty)
.flatMap(Stream::of)
.toArray(Annotation[]::new);
}
/**
* 获取层级结构中所有{@link AnnotatedElement}上直接声明的指定类型注解
*
* @param annotationType 注解类型
* @param <A> 注解类型
* @return 注解对象
*/
@Override
public <A extends Annotation> A getDeclaredAnnotation(final Class<A> annotationType) {
return getElementMappings().stream()
.map(element -> element.getDeclaredAnnotation(annotationType))
.filter(Objects::nonNull)
.findFirst()
.orElse(null);
}
/**
* 获取层级结构中所有{@link AnnotatedElement}上直接声明的指定类型注解
*
* @param annotationType 注解类型
* @param <A> 注解类型
* @return 注解对象
*/
@SuppressWarnings("unchecked")
@Override
public <A extends Annotation> A[] getDeclaredAnnotationsByType(final Class<A> annotationType) {
return (A[]) getElementMappings().stream()
.map(element -> element.getDeclaredAnnotationsByType(annotationType))
.filter(ArrayUtil::isNotEmpty)
.flatMap(Stream::of)
.toArray(size -> ArrayUtil.newArray(annotationType, size));
}
/**
* 获取注解元素映射集合的迭代器
*
* @return 迭代器
*/
@Override
public Iterator<AnnotatedElement> iterator() {
return getElementMappings().iterator();
}
/**
* 获取被包装的原始{@link AnnotatedElement}对象
*
* @return 注解对象
*/
public AnnotatedElement getElement() {
return source;
}
/**
* 比较两个实例是否相等
*
* @param o 对象
* @return 是否
*/
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
HierarchicalAnnotatedElements that = (HierarchicalAnnotatedElements)o;
return elementFactory.equals(that.elementFactory) && source.equals(that.source);
}
/**
* 获取实例的哈希值
*
* @return 哈希值
*/
@Override
public int hashCode() {
return Objects.hash(elementFactory, source);
}
// ========================= protected =========================
/**
* 获取当前元素及层级结构中的关联元素的映射对象
*
* @return 元素映射对象
*/
protected final Set<AnnotatedElement> getElementMappings() {
initElementMappingsIfNecessary();
return elementMappings;
}
/**
* 检验方法的签名是否与原始方法匹配
*
* @param source 原始的方法
* @param target 比较的方法
* @return 是否
*/
protected boolean isMatchMethod(final Method source, final Method target) {
return CharSequenceUtil.equals(source.getName(), target.getName())
// 不可为桥接方法或者合成方法
&& !target.isBridge() && !target.isSynthetic()
// 返回值需可通过原始方法的返回值转换得到
&& ClassUtil.isAssignable(target.getReturnType(), source.getReturnType())
// 参数数量必须一致且类型也必须严格一致但不检验泛型
&& Arrays.equals(source.getParameterTypes(), target.getParameterTypes());
}
// ========================= private =========================
/**
* 将元素转为{@link MetaAnnotatedElement}后添加至{@code mappings}
*/
private void collectElement(Set<AnnotatedElement> elements, final AnnotatedElement element) {
AnnotatedElement target = elementFactory.apply(elements, element);
if (Objects.nonNull(target)) {
elements.add(target);
}
}
/**
* 遍历层级结构获取层级结构中所有关联的{@link AnnotatedElement}并添加到{@link #elementMappings}
*/
private void initElementMappingsIfNecessary() {
// 双重检查保证初始化过程线程安全
if (Objects.isNull(elementMappings)) {
synchronized (this) {
if (Objects.isNull(elementMappings)) {
Set<AnnotatedElement> mappings = initElementMappings();
elementMappings = Collections.unmodifiableSet(mappings);
}
}
}
}
/**
* 遍历层级结构获取层级结构中所有关联的{@link AnnotatedElement}并添加到{@link #elementMappings}
*/
private Set<AnnotatedElement> initElementMappings() {
Set<AnnotatedElement> mappings = new LinkedHashSet<>();
// 原始元素是类
if (source instanceof Class) {
scanHierarchy(mappings, (Class<?>)source, false, source);
}
// 原始元素是方法
else if (source instanceof Method) {
final Method methodSource = (Method)source;
// 静态私有与被final关键字修饰方法无法被子类重写因此不可能具有层级结构
if (Modifier.isPrivate(methodSource.getModifiers())
|| Modifier.isFinal(methodSource.getModifiers())
|| Modifier.isStatic(methodSource.getModifiers())) {
collectElement(mappings, methodSource);
} else {
scanHierarchy(mappings, methodSource.getDeclaringClass(), true, methodSource);
}
}
return mappings;
}
/**
* 按广度优先遍历{@code type}的父类以及父接口并从类上/类中指定方法上获得所需的注解
*/
private void scanHierarchy(
Set<AnnotatedElement> mappings, Class<?> type, final boolean isMethod, final AnnotatedElement source) {
Method methodSource = isMethod ? (Method)source : null;
final Deque<Class<?>> deque = new LinkedList<>();
deque.addLast(type);
final Set<Class<?>> accessed = new HashSet<>();
while (!deque.isEmpty()) {
type = deque.removeFirst();
// 已访问过的类不再处理
if (!isNeedMapping(type, accessed)) {
continue;
}
// 收集元素
if (!isMethod) {
collectElement(mappings, type);
} else {
// TODO 改为通过带缓存的反射工具类完成
Stream.of(type.getDeclaredMethods())
.filter(method -> isMatchMethod(methodSource, method))
.forEach(method -> collectElement(mappings, method));
}
// 获取父类与父接口
accessed.add(type);
deque.addLast(type.getSuperclass());
CollUtil.addAll(deque, type.getInterfaces());
}
}
/**
* 是否需要处理该类不符合任意一点则不处理
* <ul>
* <li>该类不为{@code null}</li>
* <li>该类不为在{@code accessedTypes}中不存在</li>
* <li>该类不为{@link Object}</li>
* </ul>
*/
private boolean isNeedMapping(Class<?> type, Set<Class<?>> accessedTypes) {
return Objects.nonNull(type)
&& !accessedTypes.contains(type)
&& !Objects.equals(type, Object.class);
}
}

View File

@ -0,0 +1,208 @@
package cn.hutool.core.annotation;
import lombok.SneakyThrows;
import org.junit.Assert;
import org.junit.Test;
import java.lang.annotation.*;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.*;
import java.util.function.BiFunction;
/**
* test for {@link HierarchicalAnnotatedElements}
*
* @author huangchengxing
*/
public class HierarchicalAnnotatedElementTest {
private static final BiFunction<Set<AnnotatedElement>, AnnotatedElement, AnnotatedElement> ELEMENT_MAPPING_FACTORY = (es, e) -> e;
@SneakyThrows
@Test
public void testCreateFromMethod() {
Method method1 = Foo.class.getDeclaredMethod("method");
HierarchicalAnnotatedElements elements = HierarchicalAnnotatedElements.create(method1);
Assert.assertEquals(3, elements.getElementMappings().size());
Method method2 = Foo.class.getDeclaredMethod("method2");
elements = HierarchicalAnnotatedElements.create(method2);
Assert.assertEquals(1, elements.getElementMappings().size());
}
@Test
public void testCreate() {
HierarchicalAnnotatedElements elements = HierarchicalAnnotatedElements.create(Foo.class);
Assert.assertNotNull(elements);
Assert.assertEquals(3, elements.getElementMappings().size());
elements = HierarchicalAnnotatedElements.create(Foo.class, ELEMENT_MAPPING_FACTORY);
Assert.assertNotNull(elements);
Assert.assertEquals(3, elements.getElementMappings().size());
Assert.assertEquals(elements, HierarchicalAnnotatedElements.create(elements, ELEMENT_MAPPING_FACTORY));
}
@Test
public void testEquals() {
HierarchicalAnnotatedElements elements = HierarchicalAnnotatedElements.create(Foo.class, ELEMENT_MAPPING_FACTORY);
Assert.assertEquals(elements, elements);
Assert.assertEquals(elements, HierarchicalAnnotatedElements.create(Foo.class, ELEMENT_MAPPING_FACTORY));
Assert.assertNotEquals(elements, HierarchicalAnnotatedElements.create(Super.class, ELEMENT_MAPPING_FACTORY));
Assert.assertNotEquals(elements, HierarchicalAnnotatedElements.create(Foo.class, (es, e) -> e));
Assert.assertNotEquals(elements, null);
}
@Test
public void testHashCode() {
int hashCode = HierarchicalAnnotatedElements.create(Foo.class, ELEMENT_MAPPING_FACTORY).hashCode();
Assert.assertEquals(hashCode, HierarchicalAnnotatedElements.create(Foo.class, ELEMENT_MAPPING_FACTORY).hashCode());
Assert.assertNotEquals(hashCode, HierarchicalAnnotatedElements.create(Super.class, ELEMENT_MAPPING_FACTORY).hashCode());
Assert.assertNotEquals(hashCode, HierarchicalAnnotatedElements.create(Foo.class, (es, e) -> e).hashCode());
}
@Test
public void testGetElement() {
AnnotatedElement element = Foo.class;
HierarchicalAnnotatedElements elements = HierarchicalAnnotatedElements.create(element, ELEMENT_MAPPING_FACTORY);
Assert.assertSame(element, elements.getElement());
}
@Test
public void testIsAnnotationPresent() {
HierarchicalAnnotatedElements elements = HierarchicalAnnotatedElements.create(Foo.class);
Assert.assertTrue(elements.isAnnotationPresent(Annotation1.class));
Assert.assertTrue(elements.isAnnotationPresent(Annotation2.class));
Assert.assertTrue(elements.isAnnotationPresent(Annotation3.class));
}
@Test
public void testGetAnnotations() {
HierarchicalAnnotatedElements elements = HierarchicalAnnotatedElements.create(Foo.class);
Annotation1 annotation1 = Foo.class.getAnnotation(Annotation1.class);
Annotation2 annotation2 = Super.class.getAnnotation(Annotation2.class);
Annotation3 annotation3 = Interface.class.getAnnotation(Annotation3.class);
Annotation[] annotations = new Annotation[]{ annotation1, annotation2, annotation3 };
Assert.assertArrayEquals(annotations, elements.getAnnotations());
}
@Test
public void testGetAnnotation() {
HierarchicalAnnotatedElements elements = HierarchicalAnnotatedElements.create(Foo.class);
Annotation1 annotation1 = Foo.class.getAnnotation(Annotation1.class);
Assert.assertEquals(annotation1, elements.getAnnotation(Annotation1.class));
Annotation2 annotation2 = Super.class.getAnnotation(Annotation2.class);
Assert.assertEquals(annotation2, elements.getAnnotation(Annotation2.class));
Annotation3 annotation3 = Interface.class.getAnnotation(Annotation3.class);
Assert.assertEquals(annotation3, elements.getAnnotation(Annotation3.class));
}
@Test
public void testGetAnnotationsByType() {
HierarchicalAnnotatedElements elements = HierarchicalAnnotatedElements.create(Foo.class);
Annotation1 annotation1 = Foo.class.getAnnotation(Annotation1.class);
Assert.assertArrayEquals(new Annotation[]{ annotation1 }, elements.getAnnotationsByType(Annotation1.class));
Annotation2 annotation2 = Super.class.getAnnotation(Annotation2.class);
Assert.assertArrayEquals(new Annotation[]{ annotation2 }, elements.getAnnotationsByType(Annotation2.class));
Annotation3 annotation3 = Interface.class.getAnnotation(Annotation3.class);
Assert.assertArrayEquals(new Annotation[]{ annotation3 }, elements.getAnnotationsByType(Annotation3.class));
}
@Test
public void testGetDeclaredAnnotationsByType() {
HierarchicalAnnotatedElements elements = HierarchicalAnnotatedElements.create(Foo.class);
Annotation1 annotation1 = Foo.class.getAnnotation(Annotation1.class);
Assert.assertArrayEquals(new Annotation[]{ annotation1 }, elements.getDeclaredAnnotationsByType(Annotation1.class));
Annotation2 annotation2 = Super.class.getAnnotation(Annotation2.class);
Assert.assertArrayEquals(new Annotation[]{ annotation2 }, elements.getDeclaredAnnotationsByType(Annotation2.class));
Annotation3 annotation3 = Interface.class.getAnnotation(Annotation3.class);
Assert.assertArrayEquals(new Annotation[]{ annotation3 }, elements.getDeclaredAnnotationsByType(Annotation3.class));
}
@Test
public void testGetDeclaredAnnotation() {
HierarchicalAnnotatedElements elements = HierarchicalAnnotatedElements.create(Foo.class);
Annotation1 annotation1 = Foo.class.getAnnotation(Annotation1.class);
Assert.assertEquals(annotation1, elements.getDeclaredAnnotation(Annotation1.class));
Annotation2 annotation2 = Super.class.getAnnotation(Annotation2.class);
Assert.assertEquals(annotation2, elements.getDeclaredAnnotation(Annotation2.class));
Annotation3 annotation3 = Interface.class.getAnnotation(Annotation3.class);
Assert.assertEquals(annotation3, elements.getDeclaredAnnotation(Annotation3.class));
}
@Test
public void testGetDeclaredAnnotations() {
HierarchicalAnnotatedElements elements = HierarchicalAnnotatedElements.create(Foo.class);
Annotation1 annotation1 = Foo.class.getAnnotation(Annotation1.class);
Annotation2 annotation2 = Super.class.getAnnotation(Annotation2.class);
Annotation3 annotation3 = Interface.class.getAnnotation(Annotation3.class);
Annotation[] annotations = new Annotation[]{ annotation1, annotation2, annotation3 };
Assert.assertArrayEquals(annotations, elements.getDeclaredAnnotations());
}
@Test
public void testIterator() {
HierarchicalAnnotatedElements elements = HierarchicalAnnotatedElements.create(Foo.class);
Iterator<AnnotatedElement> iterator = elements.iterator();
Assert.assertNotNull(iterator);
List<AnnotatedElement> elementList = new ArrayList<>();
iterator.forEachRemaining(elementList::add);
Assert.assertEquals(Arrays.asList(Foo.class, Super.class, Interface.class), elementList);
}
@Target({ElementType.TYPE_USE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
private @interface Annotation3 { }
@Target({ElementType.TYPE_USE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
private @interface Annotation2 { }
@Target({ElementType.TYPE_USE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
private @interface Annotation1 { }
@Annotation3
private interface Interface {
@Annotation3
String method();
@Annotation3
static String method2() { return null; }
}
@Annotation2
private static class Super {
@Annotation2
public String method() { return null; }
@Annotation2
public static String method2() { return null; }
}
@Annotation1
private static class Foo extends Super implements Interface {
@Annotation1
@Override
public String method() { return null; }
@Annotation1
public static String method2() { return null; }
};
}