!654 新增注解扫描器和合成注解

Merge pull request !654 from Createsequence/feat-synthetic-annotation
This commit is contained in:
Looly 2022-06-27 12:08:55 +00:00 committed by Gitee
commit 44503270bf
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
14 changed files with 1247 additions and 16 deletions

View File

@ -1,7 +1,13 @@
package cn.hutool.core.annotation;
import cn.hutool.core.annotation.scanner.*;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import java.lang.annotation.Annotation;
@ -14,9 +20,11 @@ import java.lang.annotation.Target;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 注解工具类<br>
@ -27,6 +35,58 @@ import java.util.function.Predicate;
*/
public class AnnotationUtil {
/**
* 元注解
*/
static final Set<Class<? extends Annotation>> META_ANNOTATIONS = CollUtil.newHashSet(Target.class, //
Retention.class, //
Inherited.class, //
Documented.class, //
SuppressWarnings.class, //
Override.class, //
Deprecated.class//
);
/**
* 是否为Jdk自带的元注解<br />
* 包括
* <ul>
* <li>{@link Target}</li>
* <li>{@link Retention}</li>
* <li>{@link Inherited}</li>
* <li>{@link Documented}</li>
* <li>{@link SuppressWarnings}</li>
* <li>{@link Override}</li>
* <li>{@link Deprecated}</li>
* </ul>
*
* @param annotationType 注解类型
* @return 是否为Jdk自带的元注解
*/
public static boolean isJdkMateAnnotation(Class<? extends Annotation> annotationType) {
return META_ANNOTATIONS.contains(annotationType);
}
/**
* 是否不为Jdk自带的元注解<br />
* 包括
* <ul>
* <li>{@link Target}</li>
* <li>{@link Retention}</li>
* <li>{@link Inherited}</li>
* <li>{@link Documented}</li>
* <li>{@link SuppressWarnings}</li>
* <li>{@link Override}</li>
* <li>{@link Deprecated}</li>
* </ul>
*
* @param annotationType 注解类型
* @return 是否为Jdk自带的元注解
*/
public static boolean isNotJdkMateAnnotation(Class<? extends Annotation> annotationType) {
return !isJdkMateAnnotation(annotationType);
}
/**
* 将指定的被注解的元素转换为组合注解元素
*
@ -294,4 +354,84 @@ public class AnnotationUtil {
final T annotation = getAnnotation(annotationEle, annotationType);
return (T) Proxy.newProxyInstance(annotationType.getClassLoader(), new Class[]{annotationType}, new AnnotationProxy<>(annotation));
}
/**
* 将指定注解实例与其元注解转为合成注解
*
* @param annotation 注解
* @param annotationType 注解类型
* @param <T> 注解类型
* @return 合成注解
* @see SyntheticAnnotation
*/
public static <T extends Annotation> T getSynthesisAnnotation(Annotation annotation, Class<T> annotationType) {
return SyntheticAnnotation.of(annotation).getAnnotation(annotationType);
}
/**
* 获取元素上所有指定注解
* <ul>
* <li>若元素是类则递归解析全部父类和全部父接口上的注解;</li>
* <li>若元素是方法属性或注解则只解析其直接声明的注解;</li>
* </ul>
*
* @param annotatedElement 可注解元素
* @param annotationType 注解类型
* @param <T> 注解类型
* @return 注解
* @see SyntheticAnnotation
*/
public static <T extends Annotation> List<T> getAllSynthesisAnnotations(AnnotatedElement annotatedElement, Class<T> annotationType) {
AnnotationScanner[] scanners = new AnnotationScanner[] {
new MateAnnotationScanner(), new TypeAnnotationScanner(), new MethodAnnotationScanner(), new FieldAnnotationScanner()
};
return AnnotationScanner.scanByAnySupported(annotatedElement, scanners).stream()
.map(SyntheticAnnotation::of)
.map(annotation -> annotation.getAnnotation(annotationType))
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
/**
* 方法是否为注解属性方法 <br />
* 方法无参数且有返回值的方法认为是注解属性的方法
*
* @param method 方法
*/
static boolean isAttributeMethod(Method method) {
return method.getParameterCount() == 0 && method.getReturnType() != void.class;
}
/**
* 获取注解的全部属性值获取方法
*
* @param annotationType 注解
* @return 注解的全部属性值
* @throws IllegalArgumentException 当别名属性在注解中不存在或别名属性的值与原属性的值类型不一致时抛出
*/
static Map<String, Method> getAttributeMethods(Class<? extends Annotation> annotationType) {
// 获取全部注解属性值
Map<String, Method> attributeMethods = Stream.of(annotationType.getDeclaredMethods())
.filter(AnnotationUtil::isAttributeMethod)
.collect(Collectors.toMap(Method::getName, Function.identity()));
// 处理别名
attributeMethods.forEach((methodName, method) -> {
String alias = Opt.ofNullable(method.getAnnotation(Alias.class))
.map(Alias::value)
.orElse(null);
if (ObjectUtil.isNull(alias)) {
return;
}
// 存在别名则将原本的值替换为别名对应的值
Assert.isTrue(attributeMethods.containsKey(alias), "No method for alias: [{}]", alias);
Method aliasAttributeMethod = attributeMethods.get(alias);
Assert.isTrue(
ObjectUtil.isNull(aliasAttributeMethod) || ClassUtil.isAssignable(method.getReturnType(), aliasAttributeMethod.getReturnType()),
"Return type of the alias method [{}] is inconsistent with the original [{}]",
aliasAttributeMethod.getClass(), method.getParameterTypes()
);
attributeMethods.put(methodName, aliasAttributeMethod);
});
return attributeMethods;
}
}

View File

@ -39,18 +39,6 @@ public class CombinationAnnotationElement implements AnnotatedElement, Serializa
return new CombinationAnnotationElement(element, predicate);
}
/**
* 元注解
*/
private static final Set<Class<? extends Annotation>> META_ANNOTATIONS = CollUtil.newHashSet(Target.class, //
Retention.class, //
Inherited.class, //
Documented.class, //
SuppressWarnings.class, //
Override.class, //
Deprecated.class//
);
/**
* 注解类型与注解对象对应表
*/
@ -138,7 +126,7 @@ public class CombinationAnnotationElement implements AnnotatedElement, Serializa
// 直接注解
for (Annotation annotation : annotations) {
annotationType = annotation.annotationType();
if (false == META_ANNOTATIONS.contains(annotationType)) {
if (AnnotationUtil.isNotJdkMateAnnotation(annotationType)) {
if(test(annotation)){
declaredAnnotationMap.put(annotationType, annotation);
}
@ -157,7 +145,7 @@ public class CombinationAnnotationElement implements AnnotatedElement, Serializa
Class<? extends Annotation> annotationType;
for (Annotation annotation : annotations) {
annotationType = annotation.annotationType();
if (false == META_ANNOTATIONS.contains(annotationType)) {
if (AnnotationUtil.isNotJdkMateAnnotation(annotationType)) {
if(test(annotation)){
annotationMap.put(annotationType, annotation);
}

View File

@ -0,0 +1,353 @@
package cn.hutool.core.annotation;
import cn.hutool.core.annotation.scanner.MateAnnotationScanner;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 表示一个根注解与根注解上的多层元注解合成的注解
*
* <p>假设现有注解AA上存在元注解BB上存在元注解C则对A解析得到的合成注解X则CBA都是X的元注解X为根注解<br />
* 通过{@link #isAnnotationPresent(Class)}可确定指定类型是注解是否是该合成注解的元注解即是否为当前实例的父类
* 若指定注解是当前实例的元注解则通过{@link #getAnnotation(Class)}可获得动态代理生成的对应的注解实例<br />
* 需要注意的是由于认为合并注解X以最初的根注解A作为元注解因此{@link #getAnnotations()}{@link #getDeclaredAnnotations()}
* 都将只能获得A
*
* <p>不同层级的元注解可能存在同名的属性此时若认为该合成注解X在第0层则根注解A在第1层B在第2层......以此类推
* 层级越低的注解中离根注解距离近则属性优先级越高即遵循就近原则<br />
* 举个例子若CBA同时存在属性y则将X视为CB或者A时获得的y属性的值都与最底层元注解A的值保持一致
* 同理不同层级可能会出现相同的元注解比如A注解存在元注解BC但是C又存在元注解B因此根据就近原则A上的元注解B将优先于C上的元注解B生效
* 若两相同注解处于同一层级则按照从其上一级子注解{@link AnnotatedElement#getAnnotations()}的调用顺序排序<br />
* {@link #getAnnotation(Class)}获得的代理类实例的属性值遵循该规则
*
* <p>同名属性将根据类型彼此隔离即当不同层级的元注解存在同名的属性但是属性类型不同时此时低层级的属性并不会覆盖高层级注解的属性
*
* <p>别名在合成注解中仍然有效若注解X中任意属性上存在{@link Alias}注解{@link Alias#value()}指定的属性值将会覆盖注解属性的本身的值<br />
* {@link Alias}注解仅能指定注解X中存在的属性作为别名不允许指定元注解或子类注解的属性
*
* @author huangchengxing
* @see AnnotationUtil
*/
public class SyntheticAnnotation<A extends Annotation> implements Annotation, AnnotatedElement {
/**
* 根注解即当前查找的注解
*/
private final A source;
/**
* 包含根注解以及其元注解在内的全部注解实例
*/
private final Map<Class<? extends Annotation>, MetaAnnotation> metaAnnotationMap;
/**
* 属性值缓存
*/
private final Map<String, Map<Class<?>, Object>> attributeCaches;
/**
* 构造
*
* @param annotation 当前查找的注解类
*/
SyntheticAnnotation(A annotation) {
this.source = annotation;
this.metaAnnotationMap = new LinkedHashMap<>();
this.attributeCaches = new HashMap<>();
loadMetaAnnotations(); // TODO 是否可以添加注解类对应的元注解信息缓存避免每次都要解析
}
/**
* 基于指定根注解构建包括其元注解在内的合成注解
*
* @param rootAnnotation 根注解
* @return 合成注解
*/
public static <T extends Annotation> SyntheticAnnotation<T> of(T rootAnnotation) {
return new SyntheticAnnotation<>(rootAnnotation);
}
/**
* 获取根注解
*
* @return 根注解
*/
public A getSource() {
return source;
}
/**
* 获取已解析的元注解信息
*
* @return 已解析的元注解信息
*/
Map<Class<? extends Annotation>, MetaAnnotation> getMetaAnnotationMap() {
return metaAnnotationMap;
}
/**
* 获取根注解类型
*
* @return java.lang.Class<? extends java.lang.annotation.Annotation>
*/
@Override
public Class<? extends Annotation> annotationType() {
return getSource().annotationType();
}
/**
* 根据指定的属性名与属性类型获取对应的属性值若存在{@link Alias}则获取{@link Alias#value()}指定的别名属性的值
* <p>当不同层级的注解之间存在同名同类型属性时将优先获取更接近根注解的属性
*
* @param attributeName 属性名
*/
public Object getAttribute(String attributeName, Class<?> attributeType) {
Map<Class<?>, Object> values = attributeCaches.computeIfAbsent(attributeName, t -> MapUtil.newHashMap());
return values.computeIfAbsent(attributeType, a -> metaAnnotationMap.values()
.stream()
.filter(ma -> ma.hasAttribute(attributeName, attributeType)) // 集合默认是根据distance有序的故此处无需再排序
.findFirst()
.map(ma -> ma.getAttribute(attributeName))
.orElse(null)
);
}
/**
* 若合成注解在存在指定元注解则使用动态代理生成一个对应的注解实例
*
* @param annotationType 注解类型
* @param <T> 注解类型
* @return 注解
*/
@SuppressWarnings("unchecked")
@Override
public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
if (metaAnnotationMap.containsKey(annotationType)) {
return (T) Proxy.newProxyInstance(
annotationType.getClassLoader(),
new Class[]{annotationType, Synthesized.class},
new SyntheticAnnotationProxy<>(this, annotationType)
);
}
return null;
}
/**
* 获取全部注解
*
* @return java.lang.annotation.Annotation[]
*/
@Override
public Annotation[] getAnnotations() {
return getMetaAnnotationMap().values().toArray(new MetaAnnotation[0]);
}
/**
* 获取根注解直接声明的注解该方法正常情况下当只返回原注解
*
* @return 直接声明注解
*/
@Override
public Annotation[] getDeclaredAnnotations() {
return new Annotation[]{ getSource() };
}
/**
* 广度优先遍历并缓存该根注解上的全部元注解
*/
private void loadMetaAnnotations() {
// 若该注解已经是合成注解则直接使用已解析好的元注解信息
if (source instanceof SyntheticAnnotation.Synthesized) {
this.metaAnnotationMap.putAll(((Synthesized)source).getMetaAnnotationMap());
return;
}
// 扫描元注解
metaAnnotationMap.put(source.annotationType(), new MetaAnnotation(source, 0));
new MateAnnotationScanner().scan(
(index, annotation) -> metaAnnotationMap.computeIfAbsent(
// 当出现重复的注解时由于后添加的注解必然层级更高优先级更低因此当直接忽略
annotation.annotationType(), t -> new MetaAnnotation(annotation, index)
),
source.annotationType(), null
);
}
/**
* 元注解包装类
*
* @author huangchengxing
*/
static class MetaAnnotation implements Annotation {
private final Annotation annotation;
private final Map<String, Method> attributeMethodCaches;
private final int distance;
public MetaAnnotation(Annotation annotation, int distance) {
this.annotation = annotation;
this.distance = distance;
this.attributeMethodCaches = AnnotationUtil.getAttributeMethods(annotation.annotationType());
}
/**
* 获取注解类型
*
* @return 注解类型
*/
@Override
public Class<? extends Annotation> annotationType() {
return annotation.annotationType();
}
/**
* 获取元注解
*
* @return 元注解
*/
public Annotation get() {
return annotation;
}
/**
* 获取根注解到元注解的距离
*
* @return 根注解到元注解的距离
*/
public int getDistance() {
return distance;
}
/**
* 元注解是否存在该属性
*
* @param attributeName 属性名
* @return 是否存在该属性
*/
public boolean hasAttribute(String attributeName) {
return attributeMethodCaches.containsKey(attributeName);
}
/**
* 元注解是否存在该属性且该属性的值类型是指定类型或其子类
*
* @param attributeName 属性名
* @param returnType 返回值类型
* @return 是否存在该属性
*/
public boolean hasAttribute(String attributeName, Class<?> returnType) {
return Opt.ofNullable(attributeMethodCaches.get(attributeName))
.filter(method -> ClassUtil.isAssignable(returnType, method.getReturnType()))
.isPresent();
}
/**
* 获取元注解的属性值
*
* @param attributeName 属性名
* @return 元注解的属性值
*/
public Object getAttribute(String attributeName) {
return Opt.ofNullable(attributeMethodCaches.get(attributeName))
.map(method -> ReflectUtil.invoke(annotation, method))
.orElse(null);
}
}
/**
* 表示一个已经被合成的注解
*
* @author huangchengxing
*/
interface Synthesized {
/**
* 获取合成注解中已解析的元注解信息
*
* @return 合成注解中已解析的元注解信息
*/
Map<Class<? extends Annotation>, MetaAnnotation> getMetaAnnotationMap();
static boolean isMetaAnnotationMapMethod(Method method) {
return StrUtil.equals("getMetaAnnotationMap", method.getName());
}
}
/**
* 合成注解代理类
*
* @author huangchengxing
*/
static class SyntheticAnnotationProxy<A extends Annotation> implements Annotation, InvocationHandler {
private final Class<A> annotationType;
private final SyntheticAnnotation<?> syntheticAnnotation;
public SyntheticAnnotationProxy(SyntheticAnnotation<?> syntheticAnnotation, Class<A> annotationType) {
this.syntheticAnnotation = syntheticAnnotation;
this.annotationType = annotationType;
}
@Override
public Class<? extends Annotation> annotationType() {
return annotationType;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Synthesized.isMetaAnnotationMapMethod(method)) {
return syntheticAnnotation.getMetaAnnotationMap();
}
if (ReflectUtil.isHashCodeMethod(method)) {
return getHashCode();
}
if (ReflectUtil.isToStringMethod(method)) {
return getToString();
}
return ObjectUtil.defaultIfNull(
syntheticAnnotation.getAttribute(method.getName(), method.getReturnType()),
() -> ReflectUtil.invoke(this, method, args)
);
}
/**
* 获取toString值
*
* @return toString值
*/
private String getToString() {
String attributes = Stream.of(annotationType().getDeclaredMethods())
.filter(AnnotationUtil::isAttributeMethod)
.map(method -> StrUtil.format("{}={}", method.getName(), syntheticAnnotation.getAttribute(method.getName(), method.getReturnType())))
.collect(Collectors.joining(", "));
return StrUtil.format("@{}({})", annotationType().getName(), attributes);
}
/**
* 获取hashcode值
*
* @return hashcode值
*/
private int getHashCode() {
return Objects.hash((Object)syntheticAnnotation.getAnnotations());
}
}
}

View File

@ -0,0 +1,90 @@
package cn.hutool.core.annotation.scanner;
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.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 注解扫描器用于从支持的可注解元素上获取所需注解
*
* @author huangchengxing
* @see TypeAnnotationScanner
* @see MethodAnnotationScanner
* @see FieldAnnotationScanner
* @see MateAnnotationScanner
*/
public interface AnnotationScanner {
/**
* 是否支持扫描该可注解元素
*
* @param annotatedElement 可注解元素
* @return 是否支持扫描该可注解元素
*/
default boolean support(AnnotatedElement annotatedElement) {
return false;
}
/**
* 获取可注解元素上的全部注解调用该方法前需要确保调用{@link #support(AnnotatedElement)}返回为true
*
* @param annotatedElement 可注解元素
* @return 元素上的注解
*/
List<Annotation> getAnnotations(AnnotatedElement annotatedElement);
/**
* {@link #support(AnnotatedElement)}返回{@code true}
* 则调用并返回{@link #getAnnotations(AnnotatedElement)}结果
* 否则返回{@link Collections#emptyList()}
*
* @param annotatedElement 元素
* @return 元素上的注解
*/
default List<Annotation> getIfSupport(AnnotatedElement annotatedElement) {
return support(annotatedElement) ? getAnnotations(annotatedElement) : Collections.emptyList();
}
/**
* 给定一组扫描器使用第一个支持处理该类型元素的扫描器获取元素上可能存在的注解
*
* @param annotatedElement 可注解元素
* @param scanners 注解扫描器
* @return 注解
*/
static List<Annotation> scanByAnySupported(AnnotatedElement annotatedElement, AnnotationScanner... scanners) {
if (ObjectUtil.isNull(annotatedElement) && ArrayUtil.isNotEmpty(scanners)) {
return Collections.emptyList();
}
return Stream.of(scanners)
.filter(scanner -> scanner.support(annotatedElement))
.findFirst()
.map(scanner -> scanner.getAnnotations(annotatedElement))
.orElseGet(Collections::emptyList);
}
/**
* 根据指定的扫描器扫描元素上可能存在的注解
*
* @param annotatedElement 可注解元素
* @param scanners 注解扫描器
* @return 注解
*/
static List<Annotation> scanByAllScanner(AnnotatedElement annotatedElement, AnnotationScanner... scanners) {
if (ObjectUtil.isNull(annotatedElement) && ArrayUtil.isNotEmpty(scanners)) {
return Collections.emptyList();
}
return Stream.of(scanners)
.map(scanner -> scanner.getIfSupport(annotatedElement))
.flatMap(Collection::stream)
.collect(Collectors.toList());
}
}

View File

@ -0,0 +1,27 @@
package cn.hutool.core.annotation.scanner;
import cn.hutool.core.collection.CollUtil;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.util.List;
/**
* 扫描{@link Field}上的注解
*
* @author huangchengxing
*/
public class FieldAnnotationScanner implements AnnotationScanner {
@Override
public boolean support(AnnotatedElement annotatedElement) {
return annotatedElement instanceof Field;
}
@Override
public List<Annotation> getAnnotations(AnnotatedElement annotatedElement) {
return CollUtil.newArrayList(annotatedElement.getAnnotations());
}
}

View File

@ -0,0 +1,95 @@
package cn.hutool.core.annotation.scanner;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjectUtil;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 扫描注解类上存在的注解支持处理枚举实例或枚举类型 <br />
* 需要注意当待解析是枚举类时有可能与{@link TypeAnnotationScanner}冲突
*
* @author huangchengxing
* @see TypeAnnotationScanner
*/
public class MateAnnotationScanner implements AnnotationScanner {
/**
* 获取当前注解的元注解后是否继续递归扫描的元注解的元注解
*/
private final boolean includeSupperMetaAnnotation;
/**
* 构造
*
* @param includeSupperMetaAnnotation 获取当前注解的元注解后是否继续递归扫描的元注解的元注解
*/
public MateAnnotationScanner(boolean includeSupperMetaAnnotation) {
this.includeSupperMetaAnnotation = includeSupperMetaAnnotation;
}
/**
* 构造一个元注解扫描器默认在扫描当前注解上的元注解后并继续递归扫描元注解
*/
public MateAnnotationScanner() {
this(true);
}
@Override
public boolean support(AnnotatedElement annotatedElement) {
return (annotatedElement instanceof Class && ClassUtil.isAssignable(Annotation.class, (Class<?>)annotatedElement));
}
/**
* 按广度优先扫描指定注解上的元注解对扫描到的注解与层级索引进行操作
*
* @param consumer 当前层级索引与操作
* @param source 源注解
* @param filter 过滤器
* @author huangchengxing
* @date 2022/6/14 13:28
*/
public void scan(BiConsumer<Integer, Annotation> consumer, Class<? extends Annotation> source, Predicate<Annotation> filter) {
filter = ObjectUtil.defaultIfNull(filter, t -> true);
Deque<List<Class<? extends Annotation>>> deque = CollUtil.newLinkedList(CollUtil.newArrayList(source));
int distance = 0;
do {
List<Class<? extends Annotation>> annotationTypes = deque.removeFirst();
for (Class<? extends Annotation> type : annotationTypes) {
List<Annotation> metaAnnotations = Stream.of(type.getAnnotations())
.filter(a -> !AnnotationUtil.isJdkMateAnnotation(a.annotationType()))
.filter(filter)
.collect(Collectors.toList());
for (Annotation metaAnnotation : metaAnnotations) {
consumer.accept(distance, metaAnnotation);
}
deque.addLast(CollStreamUtil.toList(metaAnnotations, Annotation::annotationType));
}
distance++;
} while (includeSupperMetaAnnotation && !deque.isEmpty());
}
@SuppressWarnings("unchecked")
@Override
public List<Annotation> getAnnotations(AnnotatedElement annotatedElement) {
List<Annotation> annotations = new ArrayList<>();
scan(
(index, annotation) -> annotations.add(annotation),
(Class<? extends Annotation>)annotatedElement,
annotation -> ObjectUtil.notEqual(annotation, annotatedElement)
);
return annotations;
}
}

View File

@ -0,0 +1,27 @@
package cn.hutool.core.annotation.scanner;
import cn.hutool.core.collection.CollUtil;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.List;
/**
* 扫描{@link Method}上的注解
*
* @author huangchengxing
*/
public class MethodAnnotationScanner implements AnnotationScanner {
@Override
public boolean support(AnnotatedElement annotatedElement) {
return annotatedElement instanceof Method;
}
@Override
public List<Annotation> getAnnotations(AnnotatedElement annotatedElement) {
return CollUtil.newArrayList(annotatedElement.getAnnotations());
}
}

View File

@ -0,0 +1,224 @@
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.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;
/**
* 构造一个类注解扫描器
*
* @param includeSupperClass 是否允许扫描父类
* @param includeInterfaces 是否允许扫描父接口
*/
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<>();
}
/**
* 构建一个类注解扫描器默认允许扫描指定元素的父类以及父接口
*/
public TypeAnnotationScanner() {
this(true, true, t -> true, CollUtil.newHashSet());
}
/**
* 是否允许扫描父类
*
* @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.isJdkMateAnnotation(a.annotationType()))
.collect(Collectors.toList());
}
private Class<?> convert(Class<?> target) {
if (hasConverters) {
converters.forEach(c -> c.apply(target));
}
return target;
}
/**
* 递归遍历当前类父类及其实现的父接口
*
* @param targetClass
*/
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);
// 扫描父类
if (includeSupperClass) {
Class<?> superClass = target.getSuperclass();
if (!ObjectUtil.equals(superClass, Object.class) && ObjectUtil.isNotNull(superClass)) {
classDeque.addLast(superClass);
}
}
// 扫描接口
if (includeInterfaces) {
Class<?>[] interfaces = target.getInterfaces();
if (ArrayUtil.isNotEmpty(interfaces)) {
CollUtil.addAll(classDeque, interfaces);
}
}
}
return accessedTypes;
}
/**
* 若类型为jdk代理类则尝试转换为原始被代理类
*/
public static class JdkProxyClassConverter implements UnaryOperator<Class<?>> {
@Override
public Class<?> apply(Class<?> sourceClass) {
return Proxy.isProxyClass(sourceClass) ? sourceClass.getSuperclass() : sourceClass;
}
}
}

View File

@ -0,0 +1,87 @@
package cn.hutool.core.annotation;
import org.junit.Assert;
import org.junit.Test;
import java.lang.annotation.*;
import java.util.Map;
/**
* 合成注解{@link SyntheticAnnotation}的测试用例
*
* @author huangchengxing
*/
public class SyntheticAnnotationTest {
@Test
public void testSynthesisAnnotation() {
ChildAnnotation rootAnnotation = AnnotatedClass.class.getAnnotation(ChildAnnotation.class);
SyntheticAnnotation<ChildAnnotation> syntheticAnnotation = SyntheticAnnotation.of(rootAnnotation);
Assert.assertEquals(syntheticAnnotation.getSource(), rootAnnotation);
Assert.assertEquals(syntheticAnnotation.annotationType(), rootAnnotation.annotationType());
Assert.assertEquals(1, syntheticAnnotation.getDeclaredAnnotations().length);
Assert.assertEquals(syntheticAnnotation.getDeclaredAnnotations()[0], rootAnnotation);
Assert.assertEquals(3, syntheticAnnotation.getAnnotations().length);
Assert.assertEquals(syntheticAnnotation.getAttribute("childValue", String.class), "Child!");
Assert.assertEquals(syntheticAnnotation.getAttribute("childValueAlias", String.class), "Child!");
Assert.assertEquals(syntheticAnnotation.getAttribute("parentValue", String.class), "Child's Parent!");
Assert.assertEquals(syntheticAnnotation.getAttribute("grandParentValue", String.class), "Child's GrandParent!");
Map<Class<? extends Annotation>, SyntheticAnnotation.MetaAnnotation> annotationMap = syntheticAnnotation.getMetaAnnotationMap();
ChildAnnotation childAnnotation = syntheticAnnotation.getAnnotation(ChildAnnotation.class);
Assert.assertTrue(syntheticAnnotation.isAnnotationPresent(ChildAnnotation.class));
Assert.assertNotNull(childAnnotation);
Assert.assertEquals(childAnnotation.childValue(), "Child!");
Assert.assertEquals(childAnnotation.childValueAlias(), "Child!");
Assert.assertEquals(childAnnotation.grandParentType(), Integer.class);
Assert.assertEquals(annotationMap, SyntheticAnnotation.of(childAnnotation).getMetaAnnotationMap());
ParentAnnotation parentAnnotation = syntheticAnnotation.getAnnotation(ParentAnnotation.class);
Assert.assertTrue(syntheticAnnotation.isAnnotationPresent(ParentAnnotation.class));
Assert.assertNotNull(parentAnnotation);
Assert.assertEquals(parentAnnotation.parentValue(), "Child's Parent!");
Assert.assertEquals(parentAnnotation.grandParentType(), "java.lang.Void");
Assert.assertEquals(annotationMap, SyntheticAnnotation.of(parentAnnotation).getMetaAnnotationMap());
GrandParentAnnotation grandParentAnnotation = syntheticAnnotation.getAnnotation(GrandParentAnnotation.class);
Assert.assertTrue(syntheticAnnotation.isAnnotationPresent(GrandParentAnnotation.class));
Assert.assertNotNull(grandParentAnnotation);
Assert.assertEquals(grandParentAnnotation.grandParentValue(), "Child's GrandParent!");
Assert.assertEquals(grandParentAnnotation.grandParentType(), Integer.class);
Assert.assertEquals(annotationMap, SyntheticAnnotation.of(grandParentAnnotation).getMetaAnnotationMap());
}
// 注解结构如下
// AnnotatedClass -> @ChildAnnotation -> @ParentAnnotation -> @GrandParentAnnotation
// -> @GrandParentAnnotation
@ChildAnnotation(childValueAlias = "Child!", grandParentType = Integer.class)
static class AnnotatedClass {}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.ANNOTATION_TYPE })
@interface GrandParentAnnotation {
String grandParentValue() default "";
Class<?> grandParentType() default Void.class;
}
@GrandParentAnnotation(grandParentValue = "Parent's GrandParent!") // 覆盖元注解@GrandParentAnnotation的属性
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
@interface ParentAnnotation {
String parentValue() default "";
String grandParentType() default "java.lang.Void";
}
@GrandParentAnnotation(grandParentValue = "Child's GrandParent!") // 重复的元注解靠近根注解的优先级高
@ParentAnnotation(parentValue = "Child's Parent!") // 覆盖元注解@ParentAnnotation的属性
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface ChildAnnotation {
String childValueAlias() default "";
@Alias("childValueAlias")
String childValue() default "";
Class<?> grandParentType() default Void.class;
}
}

View File

@ -0,0 +1,15 @@
package cn.hutool.core.annotation.scanner;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author huangchengxing
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.METHOD, ElementType.FIELD })
@interface AnnotationForScannerTest {
}

View File

@ -0,0 +1,33 @@
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.Annotation;
import java.lang.reflect.Field;
import java.util.List;
/**
* @author huangchengxing
*/
public class FieldAnnotationScannerTest {
@Test
public void testFieldAnnotationScanner() {
FieldAnnotationScanner scanner = new FieldAnnotationScanner();
Field field = ReflectUtil.getField(Example.class, "id");
Assert.assertNotNull(field);
Assert.assertTrue(scanner.support(field));
List<Annotation> annotations = scanner.getAnnotations(field);
Assert.assertEquals(1, annotations.size());
Assert.assertEquals(AnnotationForScannerTest.class, CollUtil.getFirst(annotations).annotationType());
}
public static class Example {
@AnnotationForScannerTest
private Integer id;
}
}

View File

@ -0,0 +1,55 @@
package cn.hutool.core.annotation.scanner;
import cn.hutool.core.collection.CollUtil;
import org.junit.Assert;
import org.junit.Test;
import java.lang.annotation.*;
import java.util.HashMap;
import java.util.Map;
/**
* @author huangchengxing
* @date 2022/06/10 16:51
*/
public class MateAnnotationScannerTest {
@Test
public void testMateAnnotationScanner() {
AnnotationScanner scanner = new MateAnnotationScanner();
Assert.assertTrue(scanner.support(AnnotationForScannerTest3.class));
Map<Class<? extends Annotation>, Annotation> annotations = CollUtil.toMap(scanner.getAnnotations(AnnotationForScannerTest3.class), new HashMap<>(), Annotation::annotationType);
Assert.assertEquals(3, annotations.size());
Assert.assertTrue(annotations.containsKey(AnnotationForScannerTest.class));
Assert.assertTrue(annotations.containsKey(AnnotationForScannerTest1.class));
Assert.assertTrue(annotations.containsKey(AnnotationForScannerTest2.class));
Assert.assertFalse(annotations.containsKey(AnnotationForScannerTest3.class));
scanner = new MateAnnotationScanner(false);
Assert.assertTrue(scanner.support(AnnotationForScannerTest3.class));
annotations = CollUtil.toMap(scanner.getAnnotations(AnnotationForScannerTest3.class), new HashMap<>(), Annotation::annotationType);
Assert.assertEquals(1, annotations.size());
Assert.assertTrue(annotations.containsKey(AnnotationForScannerTest2.class));
Assert.assertFalse(annotations.containsKey(AnnotationForScannerTest.class));
Assert.assertFalse(annotations.containsKey(AnnotationForScannerTest1.class));
Assert.assertFalse(annotations.containsKey(AnnotationForScannerTest3.class));
}
@AnnotationForScannerTest3
static class Example {}
@AnnotationForScannerTest
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.METHOD, ElementType.FIELD })
@interface AnnotationForScannerTest1 {}
@AnnotationForScannerTest1
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.METHOD, ElementType.FIELD })
@interface AnnotationForScannerTest2 {}
@AnnotationForScannerTest2
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.METHOD, ElementType.FIELD })
@interface AnnotationForScannerTest3 {}
}

View File

@ -0,0 +1,35 @@
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.Annotation;
import java.lang.reflect.Method;
import java.util.List;
/**
* @author huangchengxing
*/
public class MethodAnnotationScannerTest {
@Test
public void testMethodAnnotationScanner() {
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);
}
static class Example {
@AnnotationForScannerTest
public void test() {
}
}
}

View File

@ -0,0 +1,62 @@
package cn.hutool.core.annotation.scanner;
import cn.hutool.core.util.ClassUtil;
import org.junit.Assert;
import org.junit.Test;
import java.lang.annotation.Annotation;
import java.util.List;
/**
* @author huangchengxing
* @date 2022/06/10 16:51
*/
public class TypeAnnotationScannerTest {
@Test
public void testTypeAnnotationScanner() {
AnnotationScanner scanner = new TypeAnnotationScanner();
Assert.assertTrue(scanner.support(Example.class));
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
static class ExampleSupplerClass implements ExampleInterface {}
@AnnotationForScannerTest
interface ExampleInterface {}
@AnnotationForScannerTest
static class Example extends ExampleSupplerClass {}
}