!671 修复注解工具类getAnnotations的NPE问题,注解扫描器添新功能

Merge pull request !671 from Createsequence/refactor-annotation
This commit is contained in:
Looly 2022-06-30 10:09:12 +00:00 committed by Gitee
commit 9b19403960
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
13 changed files with 993 additions and 294 deletions

View File

@ -10,13 +10,7 @@ import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.*;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
@ -136,7 +130,7 @@ public class AnnotationUtil {
*/
public static <T> T[] getAnnotations(AnnotatedElement annotationEle, boolean isToCombination, Class<T> annotationType) {
final Annotation[] annotations = getAnnotations(annotationEle, isToCombination,
(annotation -> null == annotationType || annotationType.isAssignableFrom(annotation.getClass())));
(annotation -> null == annotationType || annotationType.isAssignableFrom(annotation.getClass())));
final T[] result = ArrayUtil.newArray(annotationType, annotations.length);
for (int i = 0; i < annotations.length; i++) {
@ -157,7 +151,7 @@ public class AnnotationUtil {
*/
public static Annotation[] getAnnotations(AnnotatedElement annotationEle, boolean isToCombination, Predicate<Annotation> predicate) {
if (null == annotationEle) {
return null;
return new Annotation[0];
}
if (isToCombination) {
@ -257,8 +251,8 @@ public class AnnotationUtil {
final String name = t.getName();
// 跳过自有的几个方法
return (false == "hashCode".equals(name)) //
&& (false == "toString".equals(name)) //
&& (false == "annotationType".equals(name));
&& (false == "toString".equals(name)) //
&& (false == "annotationType".equals(name));
}
return false;
});
@ -294,13 +288,13 @@ public class AnnotationUtil {
final Target target = annotationType.getAnnotation(Target.class);
if (null == target) {
return new ElementType[]{ElementType.TYPE, //
ElementType.FIELD, //
ElementType.METHOD, //
ElementType.PARAMETER, //
ElementType.CONSTRUCTOR, //
ElementType.LOCAL_VARIABLE, //
ElementType.ANNOTATION_TYPE, //
ElementType.PACKAGE//
ElementType.FIELD, //
ElementType.METHOD, //
ElementType.PARAMETER, //
ElementType.CONSTRUCTOR, //
ElementType.LOCAL_VARIABLE, //
ElementType.ANNOTATION_TYPE, //
ElementType.PACKAGE//
};
}
return target.value();
@ -358,9 +352,9 @@ public class AnnotationUtil {
/**
* 将指定注解实例与其元注解转为合成注解
*
* @param annotation 注解
* @param annotationType 注解类
* @param <T> 注解类型
* @param annotation 注解对象
* @param annotationType 注解类
* @param <T> 注解类型
* @return 合成注解
* @see SyntheticAnnotation
*/
@ -375,17 +369,17 @@ public class AnnotationUtil {
* <li>若元素是方法属性或注解则只解析其直接声明的注解;</li>
* </ul>
*
* @param annotatedElement 可注解元素
* @param annotationType 注解类
* @param <T> 注解类型
* @return 注解
* @param annotatedEle {@link AnnotatedElement}可以是ClassMethodFieldConstructorReflectPermission
* @param annotationType 注解类
* @param <T> 注解类型
* @return 合成注解
* @see SyntheticAnnotation
*/
public static <T extends Annotation> List<T> getAllSynthesisAnnotations(AnnotatedElement annotatedElement, Class<T> annotationType) {
public static <T extends Annotation> List<T> getAllSynthesisAnnotations(AnnotatedElement annotatedEle, Class<T> annotationType) {
AnnotationScanner[] scanners = new AnnotationScanner[] {
new MetaAnnotationScanner(), new TypeAnnotationScanner(), new MethodAnnotationScanner(), new FieldAnnotationScanner()
};
return AnnotationScanner.scanByAnySupported(annotatedElement, scanners).stream()
return AnnotationScanner.scanByAnySupported(annotatedEle, scanners).stream()
.map(SyntheticAnnotation::of)
.map(annotation -> annotation.getAnnotation(annotationType))
.filter(Objects::nonNull)
@ -393,7 +387,58 @@ public class AnnotationUtil {
}
/**
* 方法是否为注解属性方法 <br />
* 扫描注解类以及注解类的{@link Class}层级结构中的注解将返回除了{@link #META_ANNOTATIONS}中指定的JDK默认注解外
* 按元注解对象与{@code annotationType}的距离和{@link Class#getAnnotations()}顺序排序的注解对象集合
*
* @param annotationType 注解类
* @return 注解对象集合
* @see MetaAnnotationScanner
*/
public static List<Annotation> scanMetaAnnotation(Class<? extends Annotation> annotationType) {
return new MetaAnnotationScanner().getIfSupport(annotationType);
}
/**
* <p>扫描类以及类的{@link Class}层级结构中的注解将返回除了{@link #META_ANNOTATIONS}中指定的JDK默认元注解外,
* 全部类/接口的{@link Class#getAnnotations()}方法返回的注解对象<br>
* 层级结构将按广度优先递归遵循规则如下
* <ul>
* <li>同一层级中优先处理父类然后再处理父接口</li>
* <li>同一个接口在不同层级出现优先选择层级距离{@code targetClass}更近的接口</li>
* <li>同一个接口在相同层级出现优先选择其子类/子接口被先解析的那个</li>
* </ul>
* 注解根据其声明类/接口被扫描的顺序排序若注解都在同一个{@link Class}中被声明则还会遵循{@link Class#getAnnotations()}的顺序
*
* @param targetClass
* @return 注解对象集合
* @see TypeAnnotationScanner
*/
public static List<Annotation> scanClass(Class<?> targetClass) {
return new TypeAnnotationScanner().getIfSupport(targetClass);
}
/**
* <p>扫描方法以及该方法所在类的{@link Class}层级结构中的具有相同方法签名的方法
* 将返回除了{@link #META_ANNOTATIONS}中指定的JDK默认元注解外,
* 全部匹配方法上{@link Method#getAnnotations()}方法返回的注解对象<br>
* 方法所在类的层级结构将按广度优先递归遵循规则如下
* <ul>
* <li>同一层级中优先处理父类然后再处理父接口</li>
* <li>同一个接口在不同层级出现优先选择层级距离{@code targetClass}更近的接口</li>
* <li>同一个接口在相同层级出现优先选择其子类/子接口被先解析的那个</li>
* </ul>
* 方法上的注解根据方法的声明类/接口被扫描的顺序排序若注解都在同一个类的同一个方法中被声明则还会遵循{@link Method#getAnnotations()}的顺序
*
* @param method 方法
* @return 注解对象集合
* @see MethodAnnotationScanner
*/
public static List<Annotation> scanMethod(Method method) {
return new MethodAnnotationScanner(true).getIfSupport(method);
}
/**
* 方法是否为注解属性方法 <br>
* 方法无参数且有返回值的方法认为是注解属性的方法
*
* @param method 方法

View File

@ -0,0 +1,276 @@
package cn.hutool.core.annotation.scanner;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Proxy;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
/**
* 为需要从类的层级结构中获取注解的{@link AnnotationScanner}提供基本实现
*
* @author huangchengxing
*/
public abstract class AbstractTypeAnnotationScanner<T extends AbstractTypeAnnotationScanner<T>> implements AnnotationScanner {
/**
* 是否允许扫描父类
*/
// FIXME rename includeSuperClass
private boolean includeSupperClass;
/**
* 是否允许扫描父接口
*/
private boolean includeInterfaces;
/**
* 过滤器若类型无法通过该过滤器则该类型及其树结构将直接不被查找
*/
private Predicate<Class<?>> filter;
/**
* 排除的类型以上类型及其树结构将直接不被查找
*/
private final Set<Class<?>> excludeTypes;
/**
* 转换器
*/
private final List<UnaryOperator<Class<?>>> converters;
/**
* 是否有转换器
*/
private boolean hasConverters;
/**
* 当前实例
*/
private final T typedThis;
/**
* 构造一个类注解扫描器
*
* @param includeSupperClass 是否允许扫描父类
* @param includeInterfaces 是否允许扫描父接口
* @param filter 过滤器
* @param excludeTypes 不包含的类型
*/
@SuppressWarnings("unchecked")
protected AbstractTypeAnnotationScanner(boolean includeSupperClass, boolean includeInterfaces, Predicate<Class<?>> filter, Set<Class<?>> excludeTypes) {
Assert.notNull(filter, "filter must not null");
Assert.notNull(excludeTypes, "excludeTypes must not null");
this.includeSupperClass = includeSupperClass;
this.includeInterfaces = includeInterfaces;
this.filter = filter;
this.excludeTypes = excludeTypes;
this.converters = new ArrayList<>();
this.typedThis = (T)this;
}
/**
* 是否允许扫描父类
*
* @return 是否允许扫描父类
*/
public boolean isIncludeSupperClass() {
return includeSupperClass;
}
/**
* 是否允许扫描父接口
*
* @return 是否允许扫描父接口
*/
public boolean isIncludeInterfaces() {
return includeInterfaces;
}
/**
* 设置过滤器若类型无法通过该过滤器则该类型及其树结构将直接不被查找
*
* @param filter 过滤器
* @return 当前实例
*/
public T setFilter(Predicate<Class<?>> filter) {
Assert.notNull(filter, "filter must not null");
this.filter = filter;
return typedThis;
}
/**
* 添加不扫描的类型该类型及其树结构将直接不被查找
*
* @param excludeTypes 不扫描的类型
* @return 当前实例
*/
public T addExcludeTypes(Class<?>... excludeTypes) {
CollUtil.addAll(this.excludeTypes, excludeTypes);
return typedThis;
}
/**
* 添加转换器
*
* @param converter 转换器
* @return 当前实例
* @see JdkProxyClassConverter
*/
public T addConverters(UnaryOperator<Class<?>> converter) {
Assert.notNull(converter, "converter must not null");
this.converters.add(converter);
if (!this.hasConverters) {
this.hasConverters = CollUtil.isNotEmpty(this.converters);
}
return typedThis;
}
/**
* 是否允许扫描父类
*
* @param includeSupperClass 是否
* @return 当前实例
*/
protected T setIncludeSupperClass(boolean includeSupperClass) {
this.includeSupperClass = includeSupperClass;
return typedThis;
}
/**
* 是否允许扫描父接口
*
* @param includeInterfaces 是否
* @return 当前实例
*/
protected T setIncludeInterfaces(boolean includeInterfaces) {
this.includeInterfaces = includeInterfaces;
return typedThis;
}
/**
* 则根据广度优先递归扫描类的层级结构并对层级结构中类/接口声明的层级索引和它们声明的注解对象进行处理
*
* @param consumer 对获取到的注解和注解对应的层级索引的处理
* @param annotatedEle 注解元素
* @param filter 注解过滤器无法通过过滤器的注解不会被处理该参数允许为空
*/
@Override
public void scan(BiConsumer<Integer, Annotation> consumer, AnnotatedElement annotatedEle, Predicate<Annotation> filter) {
filter = ObjectUtil.defaultIfNull(filter, annotation -> true);
final Class<?> sourceClass = getClassFormAnnotatedElement(annotatedEle);
final Deque<List<Class<?>>> classDeque = CollUtil.newLinkedList(CollUtil.newArrayList(sourceClass));
final Set<Class<?>> accessedTypes = new LinkedHashSet<>();
int index = 0;
while (!classDeque.isEmpty()) {
final List<Class<?>> currClassQueue = classDeque.removeFirst();
final List<Class<?>> nextClassQueue = new ArrayList<>();
for (Class<?> targetClass : currClassQueue) {
targetClass = convert(targetClass);
// 过滤不需要处理的类
if (isNotNeedProcess(accessedTypes, targetClass)) {
continue;
}
accessedTypes.add(targetClass);
// 扫描父类
scanSuperClassIfNecessary(nextClassQueue, targetClass);
// 扫描接口
scanInterfaceIfNecessary(nextClassQueue, targetClass);
// 处理层级索引和注解
final Annotation[] targetAnnotations = getAnnotationsFromTargetClass(annotatedEle, index, targetClass);
for (final Annotation annotation : targetAnnotations) {
if (AnnotationUtil.isNotJdkMateAnnotation(annotation.annotationType()) || filter.test(annotation)) {
consumer.accept(index, annotation);
}
}
index++;
}
if (CollUtil.isNotEmpty(nextClassQueue)) {
classDeque.addLast(nextClassQueue);
}
}
}
/**
* 从要搜索的注解元素上获得要递归的类型
*
* @param annotatedElement 注解元素
* @return 要递归的类型
*/
protected abstract Class<?> getClassFormAnnotatedElement(AnnotatedElement annotatedElement);
/**
* 从类上获取最终所需的目标注解
*
* @param source 最初的注解元素
* @param index 类的层级索引
* @param targetClass
* @return 最终所需的目标注解
*/
protected abstract Annotation[] getAnnotationsFromTargetClass(AnnotatedElement source, int index, Class<?> targetClass);
/**
* 当前类是否不需要处理
*/
protected boolean isNotNeedProcess(Set<Class<?>> accessedTypes, Class<?> targetClass) {
return ObjectUtil.isNull(targetClass)
|| accessedTypes.contains(targetClass)
|| excludeTypes.contains(targetClass)
|| filter.negate().test(targetClass);
}
/**
* {@link #includeInterfaces}{@code true}则将目标类的父接口也添加到nextClasses
*/
protected void scanInterfaceIfNecessary(List<Class<?>> nextClasses, Class<?> targetClass) {
if (includeInterfaces) {
final Class<?>[] interfaces = targetClass.getInterfaces();
if (ArrayUtil.isNotEmpty(interfaces)) {
CollUtil.addAll(nextClasses, interfaces);
}
}
}
/**
* {@link #includeSupperClass}{@code true}则将目标类的父类也添加到nextClasses
*/
protected void scanSuperClassIfNecessary(List<Class<?>> nextClassQueue, Class<?> targetClass) {
if (includeSupperClass) {
final Class<?> superClass = targetClass.getSuperclass();
if (!ObjectUtil.equals(superClass, Object.class) && ObjectUtil.isNotNull(superClass)) {
nextClassQueue.add(superClass);
}
}
}
/**
* 若存在转换器则使用转换器对目标类进行转换
*/
protected Class<?> convert(Class<?> target) {
if (hasConverters) {
for (final UnaryOperator<Class<?>> converter : converters) {
target = converter.apply(target);
}
}
return target;
}
/**
* 若类型为jdk代理类则尝试转换为原始被代理类
*/
public static class JdkProxyClassConverter implements UnaryOperator<Class<?>> {
@Override
public Class<?> apply(Class<?> sourceClass) {
return Proxy.isProxyClass(sourceClass) ? apply(sourceClass.getSuperclass()) : sourceClass;
}
}
}

View File

@ -1,13 +1,17 @@
package cn.hutool.core.annotation.scanner;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -23,66 +27,100 @@ import java.util.stream.Stream;
public interface AnnotationScanner {
/**
* 是否支持扫描该注解元素
* 判断是否支持扫描该注解元素
*
* @param annotatedElement 可注解元素
* @return 是否支持扫描该注解元素
* @param annotatedEle {@link AnnotatedElement}可以是ClassMethodFieldConstructorReflectPermission
* @return 是否支持扫描该注解元素
*/
default boolean support(AnnotatedElement annotatedElement) {
default boolean support(AnnotatedElement annotatedEle) {
return false;
}
/**
* 获取注解元素上的全部注解调用该方法前需要确保调用{@link #support(AnnotatedElement)}返回为true
* 获取注解元素上的全部注解调用该方法前需要确保调用{@link #support(AnnotatedElement)}返回为true
*
* @param annotatedElement 可注解元素
* @return 元素上的注解
* @param annotatedEle {@link AnnotatedElement}可以是ClassMethodFieldConstructorReflectPermission
* @return 注解
*/
List<Annotation> getAnnotations(AnnotatedElement annotatedElement);
default List<Annotation> getAnnotations(AnnotatedElement annotatedEle) {
final List<Annotation> annotations = new ArrayList<>();
scan((index, annotation) -> annotations.add(annotation), annotatedEle, null);
return annotations;
}
/**
* {@link #support(AnnotatedElement)}返回{@code true}
* 则调用并返回{@link #getAnnotations(AnnotatedElement)}结果
* 否则返回{@link Collections#emptyList()}
*
* @param annotatedElement 元素
* @return 元素上的注解
* @param annotatedEle {@link AnnotatedElement}可以是ClassMethodFieldConstructorReflectPermission
* @return 注解
*/
default List<Annotation> getIfSupport(AnnotatedElement annotatedElement) {
return support(annotatedElement) ? getAnnotations(annotatedElement) : Collections.emptyList();
default List<Annotation> getIfSupport(AnnotatedElement annotatedEle) {
return support(annotatedEle) ? getAnnotations(annotatedEle) : Collections.emptyList();
}
/**
* 扫描注解元素的层级结构若存在然后对获取到的注解和注解对应的层级索引进行处理
* 调用该方法前需要确保调用{@link #support(AnnotatedElement)}返回为true
*
* @param consumer 对获取到的注解和注解对应的层级索引的处理
* @param annotatedEle {@link AnnotatedElement}可以是ClassMethodFieldConstructorReflectPermission
* @param filter 注解过滤器无法通过过滤器的注解不会被处理该参数允许为空
*/
default void scan(BiConsumer<Integer, Annotation> consumer, AnnotatedElement annotatedEle, Predicate<Annotation> filter) {
filter = ObjectUtil.defaultIfNull(filter, annotation -> true);
for (final Annotation annotation : annotatedEle.getAnnotations()) {
if (AnnotationUtil.isNotJdkMateAnnotation(annotation.annotationType()) && filter.test(annotation)) {
consumer.accept(0, annotation);
}
}
}
/**
* {@link #support(AnnotatedElement)}返回{@code true}则调用{@link #scan(BiConsumer, AnnotatedElement, Predicate)}
*
* @param consumer 对获取到的注解和注解对应的层级索引的处理
* @param annotatedEle {@link AnnotatedElement}可以是ClassMethodFieldConstructorReflectPermission
* @param filter 注解过滤器无法通过过滤器的注解不会被处理该参数允许为空
*/
default void scanIfSupport(BiConsumer<Integer, Annotation> consumer, AnnotatedElement annotatedEle, Predicate<Annotation> filter) {
if (support(annotatedEle)) {
scan(consumer, annotatedEle, filter);
}
}
/**
* 给定一组扫描器使用第一个支持处理该类型元素的扫描器获取元素上可能存在的注解
*
* @param annotatedElement 可注解元素
* @param scanners 注解扫描器
* @param annotatedEle {@link AnnotatedElement}可以是ClassMethodFieldConstructorReflectPermission
* @param scanners 注解扫描器
* @return 注解
*/
static List<Annotation> scanByAnySupported(AnnotatedElement annotatedElement, AnnotationScanner... scanners) {
if (ObjectUtil.isNull(annotatedElement) && ArrayUtil.isNotEmpty(scanners)) {
static List<Annotation> scanByAnySupported(AnnotatedElement annotatedEle, AnnotationScanner... scanners) {
if (ObjectUtil.isNull(annotatedEle) && ArrayUtil.isNotEmpty(scanners)) {
return Collections.emptyList();
}
return Stream.of(scanners)
.filter(scanner -> scanner.support(annotatedElement))
.filter(scanner -> scanner.support(annotatedEle))
.findFirst()
.map(scanner -> scanner.getAnnotations(annotatedElement))
.map(scanner -> scanner.getAnnotations(annotatedEle))
.orElseGet(Collections::emptyList);
}
/**
* 根据指定的扫描器扫描元素上可能存在的注解
*
* @param annotatedElement 可注解元素
* @param scanners 注解扫描器
* @param annotatedEle {@link AnnotatedElement}可以是ClassMethodFieldConstructorReflectPermission
* @param scanners 注解扫描器
* @return 注解
*/
static List<Annotation> scanByAllScanner(AnnotatedElement annotatedElement, AnnotationScanner... scanners) {
if (ObjectUtil.isNull(annotatedElement) && ArrayUtil.isNotEmpty(scanners)) {
static List<Annotation> scanByAllScanner(AnnotatedElement annotatedEle, AnnotationScanner... scanners) {
if (ObjectUtil.isNull(annotatedEle) && ArrayUtil.isNotEmpty(scanners)) {
return Collections.emptyList();
}
return Stream.of(scanners)
.map(scanner -> scanner.getIfSupport(annotatedElement))
.map(scanner -> scanner.getIfSupport(annotatedEle))
.flatMap(Collection::stream)
.collect(Collectors.toList());
}

View File

@ -1,11 +1,13 @@
package cn.hutool.core.annotation.scanner;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.util.ObjectUtil;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
/**
* 扫描{@link Field}上的注解
@ -14,14 +16,32 @@ import java.util.List;
*/
public class FieldAnnotationScanner implements AnnotationScanner {
/**
* 判断是否支持扫描该注解元素仅当注解元素是{@link Field}时返回{@code true}
*
* @param annotatedEle {@link AnnotatedElement}可以是ClassMethodFieldConstructorReflectPermission
* @return 是否支持扫描该注解元素
*/
@Override
public boolean support(AnnotatedElement annotatedElement) {
return annotatedElement instanceof Field;
public boolean support(AnnotatedElement annotatedEle) {
return annotatedEle instanceof Field;
}
/**
* 扫描{@link Field}上直接声明的注解调用前需要确保调用{@link #support(AnnotatedElement)}返回为true
*
* @param consumer 对获取到的注解和注解对应的层级索引的处理
* @param annotatedEle {@link AnnotatedElement}可以是ClassMethodFieldConstructorReflectPermission
* @param filter 注解过滤器无法通过过滤器的注解不会被处理该参数允许为空
*/
@Override
public List<Annotation> getAnnotations(AnnotatedElement annotatedElement) {
return CollUtil.newArrayList(annotatedElement.getAnnotations());
public void scan(BiConsumer<Integer, Annotation> consumer, AnnotatedElement annotatedEle, Predicate<Annotation> filter) {
filter = ObjectUtil.defaultIfNull(filter, annotation -> true);
for (final Annotation annotation : annotatedEle.getAnnotations()) {
if (AnnotationUtil.isNotJdkMateAnnotation(annotation.annotationType()) && filter.test(annotation)) {
consumer.accept(0, annotation);
}
}
}
}

View File

@ -31,7 +31,7 @@ public class MetaAnnotationScanner implements AnnotationScanner {
private final boolean includeSupperMetaAnnotation;
/**
* 构造
* 构造一个元注解扫描器
*
* @param includeSupperMetaAnnotation 获取当前注解的元注解后是否继续递归扫描的元注解的元注解
*/
@ -46,30 +46,53 @@ public class MetaAnnotationScanner implements AnnotationScanner {
this(true);
}
/**
* 判断是否支持扫描该注解元素仅当注解元素是{@link Annotation}接口的子类{@link Class}时返回{@code true}
*
* @param annotatedEle {@link AnnotatedElement}可以是ClassMethodFieldConstructorReflectPermission
* @return 是否支持扫描该注解元素
*/
@Override
public boolean support(AnnotatedElement annotatedElement) {
return (annotatedElement instanceof Class && ClassUtil.isAssignable(Annotation.class, (Class<?>) annotatedElement));
public boolean support(AnnotatedElement annotatedEle) {
return (annotatedEle instanceof Class && ClassUtil.isAssignable(Annotation.class, (Class<?>)annotatedEle));
}
/**
* 获取注解元素上的全部注解调用该方法前需要确保调用{@link #support(AnnotatedElement)}返回为true
*
* @param annotatedEle {@link AnnotatedElement}可以是ClassMethodFieldConstructorReflectPermission
* @return 注解
*/
@Override
public List<Annotation> getAnnotations(AnnotatedElement annotatedEle) {
final List<Annotation> annotations = new ArrayList<>();
scan(
(index, annotation) -> annotations.add(annotation), annotatedEle,
annotation -> ObjectUtil.notEqual(annotation, annotatedEle)
);
return annotations;
}
/**
* 按广度优先扫描指定注解上的元注解对扫描到的注解与层级索引进行操作
*
* @param consumer 当前层级索引与操作
* @param source 源注解
* @param filter 过滤器
* @author huangchengxing
* @param consumer 当前层级索引与操作
* @param annotatedEle {@link AnnotatedElement}可以是ClassMethodFieldConstructorReflectPermission
* @param filter 过滤器
*/
public void scan(BiConsumer<Integer, Annotation> consumer, Class<? extends Annotation> source, Predicate<Annotation> filter) {
@SuppressWarnings("unchecked")
@Override
public void scan(BiConsumer<Integer, Annotation> consumer, AnnotatedElement annotatedEle, Predicate<Annotation> filter) {
filter = ObjectUtil.defaultIfNull(filter, t -> true);
final Deque<List<Class<? extends Annotation>>> deque = CollUtil.newLinkedList(CollUtil.newArrayList(source));
final Deque<List<Class<? extends Annotation>>> deque = CollUtil.newLinkedList(CollUtil.newArrayList((Class<? extends Annotation>)annotatedEle));
int distance = 0;
do {
final List<Class<? extends Annotation>> annotationTypes = deque.removeFirst();
for (final Class<? extends Annotation> type : annotationTypes) {
final List<Annotation> metaAnnotations = Stream.of(type.getAnnotations())
.filter(a -> !AnnotationUtil.isJdkMetaAnnotation(a.annotationType()))
.filter(filter)
.collect(Collectors.toList());
.filter(a -> !AnnotationUtil.isJdkMetaAnnotation(a.annotationType()))
.filter(filter)
.collect(Collectors.toList());
for (final Annotation metaAnnotation : metaAnnotations) {
consumer.accept(distance, metaAnnotation);
}
@ -79,16 +102,4 @@ public class MetaAnnotationScanner implements AnnotationScanner {
} while (includeSupperMetaAnnotation && !deque.isEmpty());
}
@SuppressWarnings("unchecked")
@Override
public List<Annotation> getAnnotations(AnnotatedElement annotatedElement) {
final List<Annotation> annotations = new ArrayList<>();
scan(
(index, annotation) -> annotations.add(annotation),
(Class<? extends Annotation>) annotatedElement,
annotation -> ObjectUtil.notEqual(annotation, annotatedElement)
);
return annotations;
}
}

View File

@ -1,27 +1,121 @@
package cn.hutool.core.annotation.scanner;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Stream;
/**
* 扫描{@link Method}上的注解
*
* @author huangchengxing
*/
public class MethodAnnotationScanner implements AnnotationScanner {
public class MethodAnnotationScanner extends AbstractTypeAnnotationScanner<MethodAnnotationScanner> implements AnnotationScanner {
@Override
public boolean support(AnnotatedElement annotatedElement) {
return annotatedElement instanceof Method;
/**
* 构造一个方法注解扫描器
*
* @param scanSameSignatureMethod 是否扫描类层级结构中具有相同方法签名的方法
* @param filter 过滤器
* @param excludeTypes 不包含的类型
*/
public MethodAnnotationScanner(boolean scanSameSignatureMethod, Predicate<Class<?>> filter, Set<Class<?>> excludeTypes) {
super(scanSameSignatureMethod, scanSameSignatureMethod, filter, excludeTypes);
}
/**
* 构造一个类注解扫描器
*
* @param scanSameSignatureMethod 是否扫描类层级结构中具有相同方法签名的方法
*/
public MethodAnnotationScanner(boolean scanSameSignatureMethod) {
this(scanSameSignatureMethod, targetClass -> true, CollUtil.newLinkedHashSet());
}
/**
* 构造一个类注解扫描器仅扫描该方法上直接声明的注解
*/
public MethodAnnotationScanner() {
this(false);
}
/**
* 判断是否支持扫描该注解元素仅当注解元素是{@link Method}时返回{@code true}
*
* @param annotatedEle {@link AnnotatedElement}可以是ClassMethodFieldConstructorReflectPermission
* @return boolean 是否支持扫描该注解元素
*/
@Override
public List<Annotation> getAnnotations(AnnotatedElement annotatedElement) {
return CollUtil.newArrayList(annotatedElement.getAnnotations());
public boolean support(AnnotatedElement annotatedEle) {
return annotatedEle instanceof Method;
}
/**
* 获取声明该方法的类
*
* @param annotatedElement 注解元素
* @return 要递归的类型
* @see Method#getDeclaringClass()
*/
@Override
protected Class<?> getClassFormAnnotatedElement(AnnotatedElement annotatedElement) {
return ((Method)annotatedElement).getDeclaringClass();
}
/**
* 若父类/父接口中方法具有相同的方法签名则返回该方法上的注解
*
* @param source 原始方法
* @param index 类的层级索引
* @param targetClass
* @return 最终所需的目标注解
*/
@Override
protected Annotation[] getAnnotationsFromTargetClass(AnnotatedElement source, int index, Class<?> targetClass) {
final Method sourceMethod = (Method) source;
return Stream.of(targetClass.getDeclaredMethods())
.filter(superMethod -> !superMethod.isBridge())
.filter(superMethod -> hasSameSignature(sourceMethod, superMethod))
.map(AnnotatedElement::getAnnotations)
.flatMap(Stream::of)
.toArray(Annotation[]::new);
}
/**
* 设置是否扫描类层级结构中具有相同方法签名的方法
*
* @param scanSuperMethodIfOverride 是否扫描类层级结构中具有相同方法签名的方法
* @return 当前实例
*/
public MethodAnnotationScanner setScanSameSignatureMethod(boolean scanSuperMethodIfOverride) {
setIncludeInterfaces(scanSuperMethodIfOverride);
setIncludeSupperClass(scanSuperMethodIfOverride);
return this;
}
/**
* 该方法是否具备与扫描的方法相同的方法签名
*/
private boolean hasSameSignature(Method sourceMethod, Method superMethod) {
if (false == StrUtil.equals(sourceMethod.getName(), superMethod.getName())) {
return false;
}
final Class<?>[] sourceParameterTypes = sourceMethod.getParameterTypes();
final Class<?>[] targetParameterTypes = superMethod.getParameterTypes();
if (sourceParameterTypes.length != targetParameterTypes.length) {
return false;
}
if (!ArrayUtil.containsAll(sourceParameterTypes, targetParameterTypes)) {
return false;
}
return ClassUtil.isAssignable(superMethod.getReturnType(), sourceMethod.getReturnType());
}
}

View File

@ -1,56 +1,20 @@
package cn.hutool.core.annotation.scanner;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Proxy;
import java.util.*;
import java.util.Set;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 扫描{@link Class}上的注解
*
* @author huangchengxing
*/
public class TypeAnnotationScanner implements AnnotationScanner {
/**
* 是否允许扫描父类
*/
private boolean includeSupperClass;
/**
* 是否允许扫描父接口
*/
private boolean includeInterfaces;
/**
* 过滤器若类型无法通过该过滤器则该类型及其树结构将直接不被查找
*/
private Predicate<Class<?>> filter;
/**
* 排除的类型以上类型及其树结构将直接不被查找
*/
private final Set<Class<?>> excludeTypes;
/**
* 转换器
*/
private final List<UnaryOperator<Class<?>>> converters;
/**
* 是否有转换器
*/
private boolean hasConverters;
public class TypeAnnotationScanner extends AbstractTypeAnnotationScanner<TypeAnnotationScanner> implements AnnotationScanner {
/**
* 构造一个类注解扫描器
@ -61,165 +25,80 @@ public class TypeAnnotationScanner implements AnnotationScanner {
* @param excludeTypes 不包含的类型
*/
public TypeAnnotationScanner(boolean includeSupperClass, boolean includeInterfaces, Predicate<Class<?>> filter, Set<Class<?>> excludeTypes) {
Assert.notNull(filter, "filter must not null");
Assert.notNull(excludeTypes, "excludeTypes must not null");
this.includeSupperClass = includeSupperClass;
this.includeInterfaces = includeInterfaces;
this.filter = filter;
this.excludeTypes = excludeTypes;
this.converters = new ArrayList<>();
super(includeSupperClass, includeInterfaces, filter, excludeTypes);
}
/**
* 构建一个类注解扫描器默认允许扫描指定元素的父类以及父接口
*/
public TypeAnnotationScanner() {
this(true, true, t -> true, CollUtil.newHashSet());
this(true, true, t -> true, CollUtil.newLinkedHashSet());
}
/**
* 是否允许扫描父类
* 判断是否支持扫描该注解元素仅当注解元素是{@link Class}接时返回{@code true}
*
* @return 是否允许扫描父类
* @param annotatedEle {@link AnnotatedElement}可以是ClassMethodFieldConstructorReflectPermission
* @return 是否支持扫描该注解元素
*/
public boolean isIncludeSupperClass() {
return includeSupperClass;
}
/**
* 是否允许扫描父接口
*
* @return 是否允许扫描父接口
*/
public boolean isIncludeInterfaces() {
return includeInterfaces;
}
/**
* 设置过滤器若类型无法通过该过滤器则该类型及其树结构将直接不被查找
*
* @param filter 过滤器
* @return 当前实例
*/
public TypeAnnotationScanner setFilter(Predicate<Class<?>> filter) {
Assert.notNull(filter, "filter must not null");
this.filter = filter;
return this;
}
/**
* 添加不扫描的类型该类型及其树结构将直接不被查找
*
* @param excludeTypes 不扫描的类型
* @return 当前实例
*/
public TypeAnnotationScanner addExcludeTypes(Class<?>... excludeTypes) {
CollUtil.addAll(this.excludeTypes, excludeTypes);
return this;
}
/**
* 添加转换器
*
* @param converter 转换器
* @return 当前实例
* @see JdkProxyClassConverter
*/
public TypeAnnotationScanner addConverters(UnaryOperator<Class<?>> converter) {
Assert.notNull(converter, "converter must not null");
this.converters.add(converter);
if (!this.hasConverters) {
this.hasConverters = true;
}
return this;
}
/**
* 是否允许扫描父类
*
* @param includeSupperClass 是否
* @return 当前实例
*/
public TypeAnnotationScanner setIncludeSupperClass(boolean includeSupperClass) {
this.includeSupperClass = includeSupperClass;
return this;
}
/**
* 是否允许扫描父接口
*
* @param includeInterfaces 是否
* @return 当前实例
*/
public TypeAnnotationScanner setIncludeInterfaces(boolean includeInterfaces) {
this.includeInterfaces = includeInterfaces;
return this;
}
@Override
public boolean support(AnnotatedElement annotatedElement) {
return annotatedElement instanceof Class;
}
@Override
public List<Annotation> getAnnotations(AnnotatedElement annotatedElement) {
return scan((Class<?>) annotatedElement).stream()
.map(Class::getAnnotations)
.flatMap(Stream::of)
.filter(a -> !AnnotationUtil.isJdkMetaAnnotation(a.annotationType()))
.collect(Collectors.toList());
}
private Class<?> convert(Class<?> target) {
if (hasConverters) {
converters.forEach(c -> c.apply(target));
}
return target;
public boolean support(AnnotatedElement annotatedEle) {
return annotatedEle instanceof Class;
}
/**
* 递归遍历当前类父类及其实现的父接口
* 将注解元素转为{@link Class}
*
* @param annotatedEle {@link AnnotatedElement}可以是ClassMethodFieldConstructorReflectPermission
* @return 要递归的类型
*/
@Override
protected Class<?> getClassFormAnnotatedElement(AnnotatedElement annotatedEle) {
return (Class<?>)annotatedEle;
}
/**
* 获取{@link Class#getAnnotations()}
*
* @param source 最初的注解元素
* @param index 类的层级索引
* @param targetClass
* @return 类上直接声明的注解
*/
private Set<Class<?>> scan(Class<?> targetClass) {
Deque<Class<?>> classDeque = CollUtil.newLinkedList(targetClass);
Set<Class<?>> accessedTypes = new HashSet<>();
while (!classDeque.isEmpty()) {
Class<?> target = convert(classDeque.removeFirst());
// 若当前类已经访问过则无需再次处理
if (ObjectUtil.isNull(target) || accessedTypes.contains(target) || excludeTypes.contains(target) || filter.negate().test(target)) {
continue;
}
accessedTypes.add(target);
@Override
protected Annotation[] getAnnotationsFromTargetClass(AnnotatedElement source, int index, Class<?> targetClass) {
return targetClass.getAnnotations();
}
// 扫描父类
if (includeSupperClass) {
Class<?> superClass = target.getSuperclass();
if (!ObjectUtil.equals(superClass, Object.class) && ObjectUtil.isNotNull(superClass)) {
classDeque.addLast(superClass);
}
}
/**
* 是否允许扫描父类
*
* @param includeSupperClass 是否允许扫描父类
* @return 当前实例
*/
@Override
public TypeAnnotationScanner setIncludeSupperClass(boolean includeSupperClass) {
return super.setIncludeSupperClass(includeSupperClass);
}
// 扫描接口
if (includeInterfaces) {
Class<?>[] interfaces = target.getInterfaces();
if (ArrayUtil.isNotEmpty(interfaces)) {
CollUtil.addAll(classDeque, interfaces);
}
}
}
return accessedTypes;
/**
* 是否允许扫描父接口
*
* @param includeInterfaces 是否允许扫描父类
* @return 当前实例
*/
@Override
public TypeAnnotationScanner setIncludeInterfaces(boolean includeInterfaces) {
return super.setIncludeInterfaces(includeInterfaces);
}
/**
* 若类型为jdk代理类则尝试转换为原始被代理类
*/
public static class JdkProxyClassConverter implements UnaryOperator<Class<?>> {
@Override
public Class<?> apply(Class<?> sourceClass) {
return Proxy.isProxyClass(sourceClass) ? sourceClass.getSuperclass() : sourceClass;
return Proxy.isProxyClass(sourceClass) ? apply(sourceClass.getSuperclass()) : sourceClass;
}
}

View File

@ -1,9 +1,14 @@
package cn.hutool.core.annotation;
import cn.hutool.core.util.ReflectUtil;
import org.junit.Assert;
import org.junit.Test;
import java.lang.annotation.Annotation;
import java.lang.annotation.*;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
public class AnnotationUtilTest {
@ -46,4 +51,93 @@ public class AnnotationUtilTest {
}
}
@Test
public void scanMetaAnnotationTest() {
// RootAnnotation -> RootMetaAnnotation1 -> RootMetaAnnotation2 -> RootMetaAnnotation3
// -> RootMetaAnnotation3
List<Annotation> annotations = AnnotationUtil.scanMetaAnnotation(RootAnnotation.class);
Assert.assertEquals(4, annotations.size());
Assert.assertEquals(RootMetaAnnotation3.class, annotations.get(0).annotationType());
Assert.assertEquals(RootMetaAnnotation1.class, annotations.get(1).annotationType());
Assert.assertEquals(RootMetaAnnotation2.class, annotations.get(2).annotationType());
Assert.assertEquals(RootMetaAnnotation3.class, annotations.get(3).annotationType());
}
@Test
public void scanClassTest() {
// TargetClass -> TargetSuperClass ----------------------------------> SuperInterface
// -> TargetSuperInterface -> SuperTargetSuperInterface -> SuperInterface
List<Annotation> annotations = AnnotationUtil.scanClass(TargetClass.class);
Assert.assertEquals(5, annotations.size());
Assert.assertEquals("TargetClass", ((AnnotationForTest)annotations.get(0)).value());
Assert.assertEquals("TargetSuperClass", ((AnnotationForTest)annotations.get(1)).value());
Assert.assertEquals("TargetSuperInterface", ((AnnotationForTest)annotations.get(2)).value());
Assert.assertEquals("SuperInterface", ((AnnotationForTest)annotations.get(3)).value());
Assert.assertEquals("SuperTargetSuperInterface", ((AnnotationForTest)annotations.get(4)).value());
}
@Test
public void scanMethodTest() {
// TargetClass -> TargetSuperClass
// -> TargetSuperInterface
Method method = ReflectUtil.getMethod(TargetClass.class, "testMethod");
Assert.assertNotNull(method);
List<Annotation> annotations = AnnotationUtil.scanMethod(method);
Assert.assertEquals(3, annotations.size());
Assert.assertEquals("TargetClass", ((AnnotationForTest)annotations.get(0)).value());
Assert.assertEquals("TargetSuperClass", ((AnnotationForTest)annotations.get(1)).value());
Assert.assertEquals("TargetSuperInterface", ((AnnotationForTest)annotations.get(2)).value());
}
@Retention(RetentionPolicy.RUNTIME)
public @interface RootMetaAnnotation3 {}
@RootMetaAnnotation3
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface RootMetaAnnotation2 {}
@RootMetaAnnotation2
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface RootMetaAnnotation1 {}
@RootMetaAnnotation3
@RootMetaAnnotation1
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
public @interface RootAnnotation {}
@AnnotationForTest("TargetClass")
static class TargetClass extends TargetSuperClass implements TargetSuperInterface {
@Override
@AnnotationForTest("TargetClass")
public List<?> testMethod() { return Collections.emptyList(); }
}
@AnnotationForTest("TargetSuperClass")
static class TargetSuperClass implements SuperInterface {
@AnnotationForTest("TargetSuperClass")
public Collection<?> testMethod() { return Collections.emptyList(); }
}
@AnnotationForTest("TargetSuperInterface")
interface TargetSuperInterface extends SuperTargetSuperInterface {
@AnnotationForTest("TargetSuperInterface")
Object testMethod();
}
@AnnotationForTest("SuperTargetSuperInterface")
interface SuperTargetSuperInterface extends SuperInterface{}
@AnnotationForTest("SuperInterface")
interface SuperInterface{}
}

View File

@ -11,5 +11,5 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.METHOD, ElementType.FIELD })
@interface AnnotationForScannerTest {
String value() default "";
}

View File

@ -7,16 +7,25 @@ import org.junit.Test;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author huangchengxing
*/
public class FieldAnnotationScannerTest {
@Test
public void testFieldAnnotationScanner() {
FieldAnnotationScanner scanner = new FieldAnnotationScanner();
public void supportTest() {
AnnotationScanner scanner = new FieldAnnotationScanner();
Assert.assertTrue(scanner.support(ReflectUtil.getField(Example.class, "id")));
Assert.assertFalse(scanner.support(ReflectUtil.getMethod(Example.class, "getId")));
Assert.assertFalse(scanner.support(null));
Assert.assertFalse(scanner.support(Example.class));
}
@Test
public void getAnnotationsTest() {
AnnotationScanner scanner = new FieldAnnotationScanner();
Field field = ReflectUtil.getField(Example.class, "id");
Assert.assertNotNull(field);
Assert.assertTrue(scanner.support(field));
@ -25,9 +34,27 @@ public class FieldAnnotationScannerTest {
Assert.assertEquals(AnnotationForScannerTest.class, CollUtil.getFirst(annotations).annotationType());
}
@Test
public void scanTest() {
AnnotationScanner scanner = new FieldAnnotationScanner();
Field field = ReflectUtil.getField(Example.class, "id");
Map<Integer, List<Annotation>> map = new HashMap<>();
scanner.scan(
(index, annotation) -> map.computeIfAbsent(index, i -> new ArrayList<>()).add(annotation),
field, null
);
Assert.assertEquals(1, map.size());
Assert.assertEquals(1, map.get(0).size());
Assert.assertEquals(AnnotationForScannerTest.class, map.get(0).get(0).annotationType());
}
public static class Example {
@AnnotationForScannerTest
private Integer id;
public Integer getId() {
return id;
}
}
}

View File

@ -1,21 +1,30 @@
package cn.hutool.core.annotation.scanner;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ReflectUtil;
import org.junit.Assert;
import org.junit.Test;
import java.lang.annotation.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author huangchengxing
* @date 2022/06/10 16:51
*/
public class MateAnnotationScannerTest {
@Test
public void testMateAnnotationScanner() {
public void supportTest() {
AnnotationScanner scanner = new MetaAnnotationScanner();
Assert.assertTrue(scanner.support(AnnotationForScannerTest.class));
Assert.assertFalse(scanner.support(ReflectUtil.getField(Example.class, "id")));
Assert.assertFalse(scanner.support(ReflectUtil.getMethod(Example.class, "getId")));
Assert.assertFalse(scanner.support(null));
Assert.assertFalse(scanner.support(Example.class));
}
@Test
public void getAnnotationsTest() {
AnnotationScanner scanner = new MetaAnnotationScanner();
Assert.assertTrue(scanner.support(AnnotationForScannerTest3.class));
Map<Class<? extends Annotation>, Annotation> annotations = CollUtil.toMap(scanner.getAnnotations(AnnotationForScannerTest3.class), new HashMap<>(), Annotation::annotationType);
@ -35,8 +44,32 @@ public class MateAnnotationScannerTest {
Assert.assertFalse(annotations.containsKey(AnnotationForScannerTest3.class));
}
@AnnotationForScannerTest3
static class Example {}
@Test
public void scanTest() {
AnnotationScanner scanner = new MetaAnnotationScanner();
Map<Integer, List<Annotation>> map = new HashMap<>();
scanner.scan(
(index, annotation) -> map.computeIfAbsent(index, i -> new ArrayList<>()).add(annotation),
AnnotationForScannerTest3.class, null
);
Assert.assertEquals(3, map.size());
Assert.assertEquals(1, map.get(0).size());
Assert.assertEquals(AnnotationForScannerTest2.class, map.get(0).get(0).annotationType());
Assert.assertEquals(1, map.get(1).size());
Assert.assertEquals(AnnotationForScannerTest1.class, map.get(1).get(0).annotationType());
Assert.assertEquals(1, map.get(2).size());
Assert.assertEquals(AnnotationForScannerTest.class, map.get(2).get(0).annotationType());
}
static class Example {
private Integer id;
public Integer getId() {
return id;
}
}
@AnnotationForScannerTest
@Retention(RetentionPolicy.RUNTIME)

View File

@ -1,35 +1,138 @@
package cn.hutool.core.annotation.scanner;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import org.junit.Assert;
import org.junit.Test;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.List;
import java.util.*;
/**
* @author huangchengxing
*/
public class MethodAnnotationScannerTest {
@Test
public void testMethodAnnotationScanner() {
public void supportTest() {
AnnotationScanner scanner = new MethodAnnotationScanner();
Assert.assertTrue(scanner.support(ReflectUtil.getMethod(Example.class, "test")));
Assert.assertFalse(scanner.support(null));
Assert.assertFalse(scanner.support(Example.class));
Assert.assertFalse(scanner.support(ReflectUtil.getField(Example.class, "id")));
}
@Test
public void getAnnotationsTest() {
AnnotationScanner scanner = new MethodAnnotationScanner();
Method method = ReflectUtil.getMethod(Example.class, "test");
Assert.assertNotNull(method);
Assert.assertTrue(scanner.support(method));
// 不查找父类中具有相同方法签名的方法
List<Annotation> annotations = scanner.getAnnotations(method);
Assert.assertEquals(1, annotations.size());
Assert.assertEquals(CollUtil.getFirst(annotations).annotationType(), AnnotationForScannerTest.class);
// 查找父类中具有相同方法签名的方法
scanner = new MethodAnnotationScanner(true);
annotations = scanner.getAnnotations(method);
Assert.assertEquals(3, annotations.size());
Assert.assertEquals("Example", ((AnnotationForScannerTest) annotations.get(0)).value());
Assert.assertEquals("SuperClass", ((AnnotationForScannerTest) annotations.get(1)).value());
Assert.assertEquals("SuperInterface", ((AnnotationForScannerTest) annotations.get(2)).value());
// 查找父类中具有相同方法签名的方法但是不查找SuperInterface
scanner = new MethodAnnotationScanner(true).addExcludeTypes(SuperInterface.class);
annotations = scanner.getAnnotations(method);
Assert.assertEquals(2, annotations.size());
Assert.assertEquals("Example", ((AnnotationForScannerTest) annotations.get(0)).value());
Assert.assertEquals("SuperClass", ((AnnotationForScannerTest) annotations.get(1)).value());
// 查找父类中具有相同方法签名的方法但是只查找SuperClass
scanner = new MethodAnnotationScanner(true)
.setFilter(t -> ClassUtil.isAssignable(SuperClass.class, t));
annotations = scanner.getAnnotations(method);
Assert.assertEquals(2, annotations.size());
Assert.assertEquals("Example", ((AnnotationForScannerTest) annotations.get(0)).value());
Assert.assertEquals("SuperClass", ((AnnotationForScannerTest) annotations.get(1)).value());
}
static class Example {
@AnnotationForScannerTest
public void test() {
@Test
public void scanTest() {
Method method = ReflectUtil.getMethod(Example.class, "test");
// 不查找父类中具有相同方法签名的方法
Map<Integer, List<Annotation>> map = new HashMap<>();
new MethodAnnotationScanner(false).scan(
(index, annotation) -> map.computeIfAbsent(index, i -> new ArrayList<>()).add(annotation),
method, null
);
Assert.assertEquals(1, map.get(0).size());
Assert.assertEquals("Example", ((AnnotationForScannerTest) map.get(0).get(0)).value());
// 查找父类中具有相同方法签名的方法
map.clear();
new MethodAnnotationScanner(true).scan(
(index, annotation) -> map.computeIfAbsent(index, i -> new ArrayList<>()).add(annotation),
method, null
);
Assert.assertEquals(3, map.size());
Assert.assertEquals(1, map.get(0).size());
Assert.assertEquals("Example", ((AnnotationForScannerTest) map.get(0).get(0)).value());
Assert.assertEquals(1, map.get(1).size());
Assert.assertEquals("SuperClass", ((AnnotationForScannerTest) map.get(1).get(0)).value());
Assert.assertEquals(1, map.get(2).size());
Assert.assertEquals("SuperInterface", ((AnnotationForScannerTest) map.get(2).get(0)).value());
// 查找父类中具有相同方法签名的方法但是不查找SuperInterface
map.clear();
new MethodAnnotationScanner(true)
.addExcludeTypes(SuperInterface.class)
.scan(
(index, annotation) -> map.computeIfAbsent(index, i -> new ArrayList<>()).add(annotation),
method, null
);
Assert.assertEquals(2, map.size());
Assert.assertEquals(1, map.get(0).size());
Assert.assertEquals("Example", ((AnnotationForScannerTest) map.get(0).get(0)).value());
Assert.assertEquals(1, map.get(1).size());
Assert.assertEquals("SuperClass", ((AnnotationForScannerTest) map.get(1).get(0)).value());
// 查找父类中具有相同方法签名的方法但是只查找SuperClass
map.clear();
new MethodAnnotationScanner(true)
.setFilter(t -> ClassUtil.isAssignable(SuperClass.class, t))
.scan(
(index, annotation) -> map.computeIfAbsent(index, i -> new ArrayList<>()).add(annotation),
method, null
);
Assert.assertEquals(2, map.size());
Assert.assertEquals(1, map.get(0).size());
Assert.assertEquals("Example", ((AnnotationForScannerTest) map.get(0).get(0)).value());
Assert.assertEquals(1, map.get(1).size());
Assert.assertEquals("SuperClass", ((AnnotationForScannerTest) map.get(1).get(0)).value());
}
static class Example extends SuperClass {
private Integer id;
@Override
@AnnotationForScannerTest("Example")
public List<?> test() { return Collections.emptyList(); }
}
static class SuperClass implements SuperInterface {
@Override
@AnnotationForScannerTest("SuperClass")
public Collection<?> test() { return Collections.emptyList(); }
}
interface SuperInterface {
@AnnotationForScannerTest("SuperInterface")
Object test();
}
}
}

View File

@ -1,62 +1,141 @@
package cn.hutool.core.annotation.scanner;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import org.junit.Assert;
import org.junit.Test;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author huangchengxing
* @date 2022/06/10 16:51
*/
public class TypeAnnotationScannerTest {
@Test
public void testTypeAnnotationScanner() {
public void supportTest() {
AnnotationScanner scanner = new TypeAnnotationScanner();
Assert.assertTrue(scanner.support(Example.class));
Assert.assertFalse(scanner.support(ReflectUtil.getField(Example.class, "id")));
Assert.assertFalse(scanner.support(ReflectUtil.getMethod(Example.class, "getId")));
Assert.assertFalse(scanner.support(null));
}
@Test
public void getAnnotationsTest() {
AnnotationScanner scanner = new TypeAnnotationScanner();
List<Annotation> annotations = scanner.getAnnotations(Example.class);
Assert.assertEquals(3, annotations.size());
annotations.forEach(a -> Assert.assertEquals(a.annotationType(), AnnotationForScannerTest.class));
// 不查找父接口
scanner = new TypeAnnotationScanner().setIncludeInterfaces(false);
Assert.assertTrue(scanner.support(Example.class));
annotations = scanner.getAnnotations(Example.class);
Assert.assertEquals(2, annotations.size());
annotations.forEach(a -> Assert.assertEquals(a.annotationType(), AnnotationForScannerTest.class));
// 不查找父类
scanner = new TypeAnnotationScanner().setIncludeSupperClass(false);
Assert.assertTrue(scanner.support(Example.class));
annotations = scanner.getAnnotations(Example.class);
Assert.assertEquals(1, annotations.size());
annotations.forEach(a -> Assert.assertEquals(a.annotationType(), AnnotationForScannerTest.class));
// 不查找ExampleSupplerClass.class
scanner = new TypeAnnotationScanner().addExcludeTypes(ExampleSupplerClass.class);
Assert.assertTrue(scanner.support(Example.class));
annotations = scanner.getAnnotations(Example.class);
Assert.assertEquals(1, annotations.size());
annotations.forEach(a -> Assert.assertEquals(a.annotationType(), AnnotationForScannerTest.class));
// 只查找ExampleSupplerClass.class
scanner = new TypeAnnotationScanner().setFilter(t -> ClassUtil.isAssignable(ExampleSupplerClass.class, t));
Assert.assertTrue(scanner.support(Example.class));
annotations = scanner.getAnnotations(Example.class);
Assert.assertEquals(2, annotations.size());
annotations.forEach(a -> Assert.assertEquals(a.annotationType(), AnnotationForScannerTest.class));
}
@AnnotationForScannerTest
@Test
public void scanTest() {
Map<Integer, List<Annotation>> map = new HashMap<>();
// 查找父类与父接口
new TypeAnnotationScanner().scan(
(index, annotation) -> map.computeIfAbsent(index, i -> new ArrayList<>()).add(annotation),
Example.class, null
);
Assert.assertEquals(3, map.size());
Assert.assertEquals(1, map.get(0).size());
Assert.assertEquals("Example", ((AnnotationForScannerTest) map.get(0).get(0)).value());
Assert.assertEquals(1, map.get(1).size());
Assert.assertEquals("ExampleSupplerClass", ((AnnotationForScannerTest) map.get(1).get(0)).value());
Assert.assertEquals(1, map.get(2).size());
Assert.assertEquals("ExampleInterface", ((AnnotationForScannerTest) map.get(2).get(0)).value());
// 不查找父接口
map.clear();
new TypeAnnotationScanner()
.setIncludeInterfaces(false)
.scan(
(index, annotation) -> map.computeIfAbsent(index, i -> new ArrayList<>()).add(annotation),
Example.class, null
);
Assert.assertEquals(2, map.size());
Assert.assertEquals(1, map.get(0).size());
Assert.assertEquals("Example", ((AnnotationForScannerTest) map.get(0).get(0)).value());
Assert.assertEquals(1, map.get(1).size());
Assert.assertEquals("ExampleSupplerClass", ((AnnotationForScannerTest) map.get(1).get(0)).value());
// 不查找父类
map.clear();
new TypeAnnotationScanner()
.setIncludeSupperClass(false)
.scan(
(index, annotation) -> map.computeIfAbsent(index, i -> new ArrayList<>()).add(annotation),
Example.class, null
);
Assert.assertEquals(1, map.size());
Assert.assertEquals(1, map.get(0).size());
Assert.assertEquals("Example", ((AnnotationForScannerTest) map.get(0).get(0)).value());
// 不查找ExampleSupplerClass.class
map.clear();
new TypeAnnotationScanner()
.addExcludeTypes(ExampleSupplerClass.class)
.scan(
(index, annotation) -> map.computeIfAbsent(index, i -> new ArrayList<>()).add(annotation),
Example.class, null
);
Assert.assertEquals(1, map.size());
Assert.assertEquals(1, map.get(0).size());
Assert.assertEquals("Example", ((AnnotationForScannerTest) map.get(0).get(0)).value());
// 只查找ExampleSupplerClass.class
map.clear();
new TypeAnnotationScanner()
.setFilter(t -> ClassUtil.isAssignable(ExampleSupplerClass.class, t))
.scan(
(index, annotation) -> map.computeIfAbsent(index, i -> new ArrayList<>()).add(annotation),
Example.class, null
);
Assert.assertEquals(2, map.size());
Assert.assertEquals(1, map.get(0).size());
Assert.assertEquals("Example", ((AnnotationForScannerTest) map.get(0).get(0)).value());
Assert.assertEquals(1, map.get(1).size());
Assert.assertEquals("ExampleSupplerClass", ((AnnotationForScannerTest) map.get(1).get(0)).value());
}
@AnnotationForScannerTest("ExampleSupplerClass")
static class ExampleSupplerClass implements ExampleInterface {}
@AnnotationForScannerTest
@AnnotationForScannerTest("ExampleInterface")
interface ExampleInterface {}
@AnnotationForScannerTest
static class Example extends ExampleSupplerClass {}
@AnnotationForScannerTest("Example")
static class Example extends ExampleSupplerClass {
private Integer id;
public Integer getId() {
return id;
}
}
}