mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +08:00
!812 【6.x】AnnotatedElementUtil支持处理可重复注解
Merge pull request !812 from Createsequence/repeatable-annotation
This commit is contained in:
commit
6c6b339520
@ -93,11 +93,41 @@ import java.util.stream.Stream;
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
* <p><strong>可重复注解支持</strong>
|
||||
* <p>工具类中格式为<em>findAllXXX</em>或<em>getAllXXX</em>格式的方法,
|
||||
* 支持获得{@link AnnotatedElement}上的可重复注解。
|
||||
* 此处的可重复注解定义包括两方面:
|
||||
* <ul>
|
||||
* <li>
|
||||
* 若{@link AnnotatedElement}存在直接声明的注解,该注解有且仅有一个<em>value</em>属性,
|
||||
* 该属性类型为注解数组,且数组中注解被{@link java.lang.annotation.Repeatable}注解,
|
||||
* 则认为被包括的注解为可重复注解;<br>
|
||||
* eg:<br>
|
||||
* A上存在注解<em>X</em>,该注解是一个容器注解,内部包含可重复注解<em>Y</em>,
|
||||
* 解析<em>X</em>后,得到注解<em>X</em>与它包含的可重复注解<em>Y</em>;
|
||||
* </li>
|
||||
* <li>
|
||||
* 若{@link AnnotatedElement}存在直接声明的注解,该注解与其他根注解皆有相同的元注解,
|
||||
* 则获得元注解时,可以获得多个该相同的元注解。<br>
|
||||
* eg:<br>
|
||||
* A上存在注解<em>X</em>、<em>Y</em>,两者皆有元注解<em>Z</em>,
|
||||
* 则通过{@link AnnotatedElement}可以获得两个<em>Z</em>
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
* <p><strong>缓存</strong>
|
||||
* <p>为了避免注解以及{@link AnnotatedElement}层级结构解析过程中的大量反射调用,
|
||||
* 工具类为{@link AnnotatedElement}及其元注解信息进行了缓存。<br>
|
||||
* 缓存功能默认基于{@link WeakConcurrentMap}实现,会在gc时自动回收部分缓存数据。
|
||||
* 但是若有必要,也可以调用{@link #clearCaches()}方法主动清空缓存。
|
||||
*
|
||||
* @author huangchengxing
|
||||
* @see ResolvedAnnotationMapping
|
||||
* @see GenericAnnotationMapping
|
||||
* @see HierarchicalAnnotatedElements
|
||||
* @see RepeatableMetaAnnotatedElement
|
||||
* @see MetaAnnotatedElement
|
||||
* @see RepeatableAnnotationCollector
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public class AnnotatedElementUtil {
|
||||
@ -112,6 +142,16 @@ public class AnnotatedElementUtil {
|
||||
*/
|
||||
private static final Map<AnnotatedElement, MetaAnnotatedElement<GenericAnnotationMapping>> ELEMENT_CACHE = new WeakConcurrentMap<>();
|
||||
|
||||
/**
|
||||
* 不支持属性解析的{@link RepeatableMetaAnnotatedElement}缓存
|
||||
*/
|
||||
private static final Map<AnnotatedElement, RepeatableMetaAnnotatedElement<ResolvedAnnotationMapping>> RESOLVED_REPEATABLE_ELEMENT_CACHE = new WeakConcurrentMap<>();
|
||||
|
||||
/**
|
||||
* 不支持属性解析的{@link RepeatableMetaAnnotatedElement}缓存
|
||||
*/
|
||||
private static final Map<AnnotatedElement, RepeatableMetaAnnotatedElement<GenericAnnotationMapping>> REPEATABLE_ELEMENT_CACHE = new WeakConcurrentMap<>();
|
||||
|
||||
// region ========== find ==========
|
||||
|
||||
/**
|
||||
@ -140,7 +180,8 @@ public class AnnotatedElementUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 从{@code element}所处层级结构的所有{@link AnnotatedElement}上,获取所有该类型的注解或元注解
|
||||
* 从{@code element}所处层级结构的所有{@link AnnotatedElement}上直接声明的注解、
|
||||
* 这些注解包含的可重复注解,以及上述所有注解的元注解中获取指定类型注解
|
||||
*
|
||||
* @param element {@link AnnotatedElement}
|
||||
* @param annotationType 注解类型
|
||||
@ -148,7 +189,7 @@ public class AnnotatedElementUtil {
|
||||
* @return 注解对象
|
||||
*/
|
||||
public static <T extends Annotation> T[] findAllAnnotations(final AnnotatedElement element, final Class<T> annotationType) {
|
||||
return toHierarchyMetaElement(element, false)
|
||||
return toHierarchyRepeatableMetaElement(element, false)
|
||||
.getAnnotationsByType(annotationType);
|
||||
}
|
||||
|
||||
@ -190,7 +231,8 @@ public class AnnotatedElementUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 从{@code element}所处层级结构的所有{@link AnnotatedElement}上,获取所有该类型的注解或元注解。<br>
|
||||
* 从{@code element}所处层级结构的所有{@link AnnotatedElement}上直接声明的注解、
|
||||
* 这些注解包含的可重复注解,以及上述所有注解的元注解中获取指定类型注解。<br>
|
||||
* 得到的注解支持基于{@link Alias}的别名、及子注解对元注解中同名同类型属性进行覆写的特殊机制。
|
||||
*
|
||||
* @param element {@link AnnotatedElement}
|
||||
@ -199,7 +241,7 @@ public class AnnotatedElementUtil {
|
||||
* @return 注解对象
|
||||
*/
|
||||
public static <T extends Annotation> T[] findAllResolvedAnnotations(final AnnotatedElement element, final Class<T> annotationType) {
|
||||
return toHierarchyMetaElement(element, true)
|
||||
return toHierarchyRepeatableMetaElement(element, true)
|
||||
.getAnnotationsByType(annotationType);
|
||||
}
|
||||
|
||||
@ -221,7 +263,8 @@ public class AnnotatedElementUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 从{@code element}所处层级结构的所有{@link AnnotatedElement}上获取所有该类型的注解
|
||||
* 从{@code element}上直接声明的注解、这些注解包含的可重复注解,
|
||||
* 以及上述所有注解的元注解中获取指定类型注解。
|
||||
*
|
||||
* @param element {@link AnnotatedElement}
|
||||
* @param annotationType 注解类型
|
||||
@ -229,7 +272,7 @@ public class AnnotatedElementUtil {
|
||||
* @return 注解对象
|
||||
*/
|
||||
public static <T extends Annotation> T[] findAllDirectlyAnnotations(final AnnotatedElement element, final Class<T> annotationType) {
|
||||
return toHierarchyMetaElement(element, false)
|
||||
return toHierarchyRepeatableMetaElement(element, false)
|
||||
.getDeclaredAnnotationsByType(annotationType);
|
||||
}
|
||||
|
||||
@ -271,7 +314,8 @@ public class AnnotatedElementUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 从{@code element}所处层级结构的所有{@link AnnotatedElement}上获取所有该类型的注解。<br>
|
||||
* 从{@code element}所处层级结构的所有{@link AnnotatedElement}上直接声明的注解、
|
||||
* 这些注解包含的可重复注解,以及上述所有注解的元注解中获取指定类型注解。<br>
|
||||
* 得到的注解支持基于{@link Alias}的别名、及子注解对元注解中同名同类型属性进行覆写的特殊机制。
|
||||
*
|
||||
* @param element {@link AnnotatedElement}
|
||||
@ -280,7 +324,7 @@ public class AnnotatedElementUtil {
|
||||
* @return 注解对象
|
||||
*/
|
||||
public static <T extends Annotation> T[] findAllDirectlyResolvedAnnotations(final AnnotatedElement element, final Class<T> annotationType) {
|
||||
return toHierarchyMetaElement(element, true)
|
||||
return toHierarchyRepeatableMetaElement(element, true)
|
||||
.getDeclaredAnnotationsByType(annotationType);
|
||||
}
|
||||
|
||||
@ -324,6 +368,19 @@ public class AnnotatedElementUtil {
|
||||
.getAnnotations();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从{@code element}上直接声明的注解、这些注解包含的可重复注解,以及上述所有注解的元注解中获取指定类型注解
|
||||
*
|
||||
* @param element {@link AnnotatedElement}
|
||||
* @param annotationType 注解类型
|
||||
* @param <T> 注解类型
|
||||
* @return 注解对象
|
||||
*/
|
||||
public static <T extends Annotation> T[] getAllAnnotations(final AnnotatedElement element, final Class<T> annotationType) {
|
||||
return toRepeatableMetaElement(element, false)
|
||||
.getAnnotationsByType(annotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从{@code element}上,获取所有的注解或元注解。<br>
|
||||
* 得到的注解支持基于{@link Alias}的别名机制。
|
||||
@ -350,6 +407,20 @@ public class AnnotatedElementUtil {
|
||||
.getAnnotations();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从{@code element}上直接声明的注解、这些注解包含的可重复注解,以及上述所有注解的元注解中获取指定类型注解<br>
|
||||
* 得到的注解支持基于{@link Alias}的别名机制。
|
||||
*
|
||||
* @param element {@link AnnotatedElement}
|
||||
* @param annotationType 注解类型
|
||||
* @param <T> 注解类型
|
||||
* @return 注解对象
|
||||
*/
|
||||
public static <T extends Annotation> T[] getAllResolvedAnnotations(final AnnotatedElement element, final Class<T> annotationType) {
|
||||
return toRepeatableMetaElement(element, true)
|
||||
.getAnnotationsByType(annotationType);
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region ========== get & direct ==========
|
||||
@ -367,6 +438,19 @@ public class AnnotatedElementUtil {
|
||||
.getDeclaredAnnotation(annotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从{@code element}上直接声明的注解、这些注解包含的可重复注解中获取指定类型注解
|
||||
*
|
||||
* @param element {@link AnnotatedElement}
|
||||
* @param annotationType 注解类型
|
||||
* @param <T> 注解类型
|
||||
* @return 注解对象
|
||||
*/
|
||||
public static <T extends Annotation> T[] getAllDirectlyAnnotations(final AnnotatedElement element, final Class<T> annotationType) {
|
||||
return toRepeatableMetaElement(element, false)
|
||||
.getDeclaredAnnotationsByType(annotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从{@code element}上获取所有的注解
|
||||
*
|
||||
@ -404,6 +488,19 @@ public class AnnotatedElementUtil {
|
||||
.getDeclaredAnnotations();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从{@code element}上直接声明的注解、这些注解包含的可重复注解中获取指定类型注解
|
||||
*
|
||||
* @param element {@link AnnotatedElement}
|
||||
* @param annotationType 注解类型
|
||||
* @param <T> 注解类型
|
||||
* @return 注解对象
|
||||
*/
|
||||
public static <T extends Annotation> T[] getAllDirectlyResolvedAnnotations(final AnnotatedElement element, final Class<T> annotationType) {
|
||||
return toRepeatableMetaElement(element, true)
|
||||
.getDeclaredAnnotationsByType(annotationType);
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region ========== to element ==========
|
||||
@ -430,6 +527,29 @@ public class AnnotatedElementUtil {
|
||||
return HierarchicalAnnotatedElements.create(element, (es, e) -> getMetaElementCache(e));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>扫描{@code element}所处层级结构中的{@link AnnotatedElement},
|
||||
* 并将其全部转为{@link RepeatableMetaAnnotatedElement}后,
|
||||
* 再把所有对象合并为{@link HierarchicalAnnotatedElements}。<br>
|
||||
* 得到的对象可访问{@code element}所处层级结构中所有{@link AnnotatedElement}上的直接声明的注解,
|
||||
* 这些注解包含的可重复注解,以及上述注解的所有元注解。
|
||||
*
|
||||
* @param element 元素
|
||||
* @param resolved 是否解析注解属性,若为{@code true}则获得的注解将支持属性别名以及属性覆盖机制
|
||||
* @return {@link HierarchicalAnnotatedElements}实例
|
||||
* @see #getRepeatableMetaElementCache(AnnotatedElement)
|
||||
* @see #getResolvedRepeatableMetaElementCache(AnnotatedElement)
|
||||
*/
|
||||
public static AnnotatedElement toHierarchyRepeatableMetaElement(final AnnotatedElement element, final boolean resolved) {
|
||||
if (Objects.isNull(element)) {
|
||||
return emptyElement();
|
||||
}
|
||||
if (resolved) {
|
||||
return HierarchicalAnnotatedElements.create(element, (es, e) -> getResolvedRepeatableMetaElementCache(e));
|
||||
}
|
||||
return HierarchicalAnnotatedElements.create(element, (es, e) -> getRepeatableMetaElementCache(e));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>扫描{@code element}所处层级结构中的{@link AnnotatedElement},
|
||||
* 再把所有对象合并为{@link HierarchicalAnnotatedElements}
|
||||
@ -460,6 +580,49 @@ public class AnnotatedElementUtil {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将{@link AnnotatedElement}转为{@link RepeatableMetaAnnotatedElement},
|
||||
* 得到的对象可访问{@link AnnotatedElement}上的直接声明的注解,这些注解包含的可重复注解,以及上述注解的所有元注解。
|
||||
*
|
||||
* @param element 元素
|
||||
* @param resolved 是否解析注解属性,若为{@code true}则获得的注解将支持属性别名以及属性覆盖机制
|
||||
* @return {@link AnnotatedElement}实例
|
||||
* @see #getMetaElementCache(AnnotatedElement)
|
||||
* @see #getResolvedMetaElementCache(AnnotatedElement)
|
||||
*/
|
||||
public static AnnotatedElement toRepeatableMetaElement(final AnnotatedElement element, final boolean resolved) {
|
||||
return ObjUtil.defaultIfNull(
|
||||
element, e -> resolved ? getResolvedRepeatableMetaElementCache(e) : getRepeatableMetaElementCache(e), emptyElement()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>将{@link AnnotatedElement}转为{@link RepeatableMetaAnnotatedElement},
|
||||
* 得到的对象可访问{@link AnnotatedElement}上的直接声明的注解,
|
||||
* 通过{@code collector}从这些注解获得的可重复注解,以及上述注解的所有元注解。<br>
|
||||
* 注意:方法将不会通过缓存结果,因此每次调用都需要重新通过反射并获得相关注解。
|
||||
*
|
||||
* @param collector 可重复注解收集器,为{@code null}时等同于{@link RepeatableAnnotationCollector#none()}
|
||||
* @param element 元素
|
||||
* @param resolved 是否解析注解属性,若为{@code true}则获得的注解将支持属性别名以及属性覆盖机制
|
||||
* @return {@link AnnotatedElement}实例
|
||||
*/
|
||||
public static AnnotatedElement toRepeatableMetaElement(
|
||||
final AnnotatedElement element, RepeatableAnnotationCollector collector, final boolean resolved) {
|
||||
if (Objects.isNull(element)) {
|
||||
return emptyElement();
|
||||
}
|
||||
collector = ObjUtil.defaultIfNull(collector, RepeatableAnnotationCollector.none());
|
||||
if (resolved) {
|
||||
return RepeatableMetaAnnotatedElement.create(
|
||||
collector, element, (source, annotation) -> ResolvedAnnotationMapping.create((ResolvedAnnotationMapping)source, annotation, true)
|
||||
);
|
||||
}
|
||||
return RepeatableMetaAnnotatedElement.create(
|
||||
collector, element, (source, annotation) -> GenericAnnotationMapping.create(annotation, Objects.isNull(source))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将一组注解中的非{@code null}注解对象合并为一个{@link AnnotatedElement}
|
||||
*
|
||||
@ -493,26 +656,70 @@ public class AnnotatedElementUtil {
|
||||
* @param element {@link AnnotatedElement}
|
||||
* @return {@link MetaAnnotatedElement}实例
|
||||
*/
|
||||
private static MetaAnnotatedElement<ResolvedAnnotationMapping> getResolvedMetaElementCache(final AnnotatedElement element) {
|
||||
static MetaAnnotatedElement<ResolvedAnnotationMapping> getResolvedMetaElementCache(final AnnotatedElement element) {
|
||||
return RESOLVED_ELEMENT_CACHE.computeIfAbsent(element, ele -> MetaAnnotatedElement.create(
|
||||
element, (source, annotation) -> ResolvedAnnotationMapping.create(source, annotation, true)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个支持注解解析的{@link MetaAnnotatedElement}
|
||||
* 创建一个不支持注解解析的{@link MetaAnnotatedElement}
|
||||
*
|
||||
* @param element {@link AnnotatedElement}
|
||||
* @return {@link MetaAnnotatedElement}实例
|
||||
*/
|
||||
private static MetaAnnotatedElement<GenericAnnotationMapping> getMetaElementCache(final AnnotatedElement element) {
|
||||
static MetaAnnotatedElement<GenericAnnotationMapping> getMetaElementCache(final AnnotatedElement element) {
|
||||
return ELEMENT_CACHE.computeIfAbsent(element, ele -> MetaAnnotatedElement.create(
|
||||
element, (source, annotation) -> GenericAnnotationMapping.create(annotation, Objects.isNull(source))
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个支持注解解析的{@link RepeatableMetaAnnotatedElement}
|
||||
*
|
||||
* @param element {@link AnnotatedElement}
|
||||
* @return {@link MetaAnnotatedElement}实例
|
||||
*/
|
||||
static RepeatableMetaAnnotatedElement<ResolvedAnnotationMapping> getResolvedRepeatableMetaElementCache(final AnnotatedElement element) {
|
||||
return RESOLVED_REPEATABLE_ELEMENT_CACHE.computeIfAbsent(element, ele -> RepeatableMetaAnnotatedElement.create(
|
||||
element, (source, annotation) -> ResolvedAnnotationMapping.create(source, annotation, true)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个不支持注解解析的{@link RepeatableMetaAnnotatedElement}
|
||||
*
|
||||
* @param element {@link AnnotatedElement}
|
||||
* @return {@link MetaAnnotatedElement}实例
|
||||
*/
|
||||
static RepeatableMetaAnnotatedElement<GenericAnnotationMapping> getRepeatableMetaElementCache(final AnnotatedElement element) {
|
||||
return REPEATABLE_ELEMENT_CACHE.computeIfAbsent(element, ele -> RepeatableMetaAnnotatedElement.create(
|
||||
element, (source, annotation) -> GenericAnnotationMapping.create(annotation, Objects.isNull(source))
|
||||
));
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
/**
|
||||
* 清空相关缓存,包括:
|
||||
* <ul>
|
||||
* <li>{@link AnnotatedElementUtil}中的{@link AnnotatedElement}及{@link AnnotationMapping}缓存;</li>
|
||||
* <li>{@link AnnotationUtil}中的{@link AnnotatedElement}上直接声明的注解缓存;</li>
|
||||
* <li>{@link RepeatableAnnotationCollector}中单例的注解属性缓存;</li>
|
||||
* </ul>
|
||||
*
|
||||
* @see AnnotationUtil#clearCaches()
|
||||
* @see RepeatableAnnotationCollector#clearSingletonCaches()
|
||||
*/
|
||||
public static void clearCaches() {
|
||||
ELEMENT_CACHE.clear();
|
||||
RESOLVED_ELEMENT_CACHE.clear();
|
||||
REPEATABLE_ELEMENT_CACHE.clear();
|
||||
RESOLVED_REPEATABLE_ELEMENT_CACHE.clear();
|
||||
RepeatableAnnotationCollector.clearSingletonCaches();
|
||||
AnnotationUtil.clearCaches();
|
||||
}
|
||||
|
||||
/**
|
||||
* 由一组注解聚合来的{@link AnnotatedElement}
|
||||
*/
|
||||
|
@ -1,6 +1,8 @@
|
||||
package cn.hutool.core.annotation;
|
||||
|
||||
import cn.hutool.core.exceptions.UtilException;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.map.WeakConcurrentMap;
|
||||
import cn.hutool.core.reflect.FieldUtil;
|
||||
import cn.hutool.core.reflect.MethodUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
@ -13,7 +15,6 @@ import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@ -26,6 +27,22 @@ import java.util.stream.Stream;
|
||||
*/
|
||||
public class AnnotationUtil {
|
||||
|
||||
/**
|
||||
* 直接声明的注解缓存
|
||||
*/
|
||||
private static final Map<AnnotatedElement, Annotation[]> DECLARED_ANNOTATIONS_CACHE = new WeakConcurrentMap<>();
|
||||
|
||||
/**
|
||||
* 获取直接声明的注解,若已有缓存则从缓存中获取
|
||||
*
|
||||
* @param element {@link AnnotatedElement}
|
||||
* @return 注解
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public static Annotation[] getDeclaredAnnotations(AnnotatedElement element) {
|
||||
return MapUtil.computeIfAbsent(DECLARED_ANNOTATIONS_CACHE, element, AnnotatedElement::getDeclaredAnnotations);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将指定的被注解的元素转换为组合注解元素
|
||||
*
|
||||
@ -295,16 +312,14 @@ public class AnnotationUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取注解属性
|
||||
* 获取注解属性,若已有缓存则从缓存中获取
|
||||
*
|
||||
* @param annotationType 注解类型
|
||||
* @return 注解属性
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public static Method[] getAnnotationAttributes(final Class<? extends Annotation> annotationType) {
|
||||
// TODO 改为通过带缓存的反射工具类完成
|
||||
Objects.requireNonNull(annotationType);
|
||||
return Stream.of(annotationType.getDeclaredMethods())
|
||||
return Stream.of(MethodUtil.getDeclaredMethods(annotationType))
|
||||
.filter(AnnotationUtil::isAnnotationAttribute)
|
||||
.toArray(Method[]::new);
|
||||
}
|
||||
@ -339,4 +354,11 @@ public class AnnotationUtil {
|
||||
&& !attribute.isSynthetic();
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空相关缓存
|
||||
*/
|
||||
public static void clearCaches() {
|
||||
DECLARED_ANNOTATIONS_CACHE.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,11 +4,7 @@ import cn.hutool.core.collection.SetUtil;
|
||||
import cn.hutool.core.map.TableMap;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.annotation.*;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@ -115,7 +111,7 @@ public class CombinationAnnotationElement implements AnnotatedElement, Serializa
|
||||
* @param element 元素
|
||||
*/
|
||||
private void init(final AnnotatedElement element) {
|
||||
final Annotation[] declaredAnnotations = element.getDeclaredAnnotations();
|
||||
final Annotation[] declaredAnnotations = AnnotationUtil.getDeclaredAnnotations(element);
|
||||
this.declaredAnnotationMap = new TableMap<>();
|
||||
parseDeclared(declaredAnnotations);
|
||||
|
||||
@ -145,7 +141,7 @@ public class CombinationAnnotationElement implements AnnotatedElement, Serializa
|
||||
declaredAnnotationMap.put(annotationType, annotation);
|
||||
}
|
||||
// 测试不通过的注解,不影响继续递归
|
||||
parseDeclared(annotationType.getDeclaredAnnotations());
|
||||
parseDeclared(AnnotationUtil.getDeclaredAnnotations(annotationType));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package cn.hutool.core.annotation;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.reflect.ClassUtil;
|
||||
import cn.hutool.core.reflect.MethodUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
|
||||
@ -172,7 +173,7 @@ public class HierarchicalAnnotatedElements implements AnnotatedElement, Iterable
|
||||
@Override
|
||||
public Annotation[] getDeclaredAnnotations() {
|
||||
return getElementMappings().stream()
|
||||
.map(AnnotatedElement::getDeclaredAnnotations)
|
||||
.map(AnnotationUtil::getDeclaredAnnotations)
|
||||
.filter(ArrayUtil::isNotEmpty)
|
||||
.flatMap(Stream::of)
|
||||
.toArray(Annotation[]::new);
|
||||
@ -357,8 +358,7 @@ public class HierarchicalAnnotatedElements implements AnnotatedElement, Iterable
|
||||
if (!isMethod) {
|
||||
collectElement(mappings, type);
|
||||
} else {
|
||||
// TODO 改为通过带缓存的反射工具类完成
|
||||
Stream.of(type.getDeclaredMethods())
|
||||
Stream.of(MethodUtil.getDeclaredMethods(type))
|
||||
.filter(method -> isMatchMethod(methodSource, method))
|
||||
.forEach(method -> collectElement(mappings, method));
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ import java.lang.annotation.Inherited;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.util.*;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* <p>注解元素映射,用于包装一个{@link AnnotatedElement},然后将被包装的元素上,
|
||||
@ -21,8 +20,11 @@ import java.util.stream.Stream;
|
||||
* 并且在当前实例中,{@link Inherited}注解将不生效,
|
||||
* 即通过<em>directly</em>方法将无法获得父类上带有{@link Inherited}的注解。
|
||||
*
|
||||
* <p>当通过静态工厂方法创建时,该实例与关联的{@link ResolvedAnnotationMapping}都会针对{@link ResolvedAnnotationMapping}进行缓存,
|
||||
* 从而避免频繁的反射与代理造成不必要的性能损耗。
|
||||
* <p>在一个{@link MetaAnnotatedElement}中,
|
||||
* {@link AnnotatedElement}上同类型的注解或元注解只会被保留一个,
|
||||
* 即当出现两个根注解都具有相同元注解时,仅有第一个根注解上的元注解会被保留,
|
||||
* 因此当通过{@link #getAnnotationsByType(Class)}
|
||||
* 或{@link #getDeclaredAnnotationsByType(Class)}方法用于只能获得一个注解对象。
|
||||
*
|
||||
* @author huangchengxing
|
||||
* @see ResolvedAnnotationMapping
|
||||
@ -214,6 +216,34 @@ public class MetaAnnotatedElement<T extends AnnotationMapping<Annotation>> imple
|
||||
return getAnnotationMappings().values().iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较两个实例是否相等
|
||||
*
|
||||
* @param o 对象
|
||||
* @return 是否
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final MetaAnnotatedElement<?> that = (MetaAnnotatedElement<?>)o;
|
||||
return element.equals(that.element) && mappingFactory.equals(that.mappingFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实例的哈希值
|
||||
*
|
||||
* @return 哈希值
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(element, mappingFactory);
|
||||
}
|
||||
|
||||
// ========================= protected =========================
|
||||
|
||||
/**
|
||||
@ -270,7 +300,7 @@ public class MetaAnnotatedElement<T extends AnnotationMapping<Annotation>> imple
|
||||
*/
|
||||
private void initAnnotationMappings(final Map<Class<? extends Annotation>, T> mappings) {
|
||||
final Deque<T> deque = new LinkedList<>();
|
||||
Arrays.stream(element.getDeclaredAnnotations())
|
||||
Arrays.stream(AnnotationUtil.getDeclaredAnnotations(element))
|
||||
.filter(m -> isNeedMapping(mappings, m))
|
||||
.map(annotation -> createMapping(null, annotation))
|
||||
.filter(Objects::nonNull)
|
||||
@ -283,40 +313,15 @@ public class MetaAnnotatedElement<T extends AnnotationMapping<Annotation>> imple
|
||||
}
|
||||
// 保存该注解,并将其需要处理的元注解也加入队列
|
||||
mappings.put(mapping.annotationType(), mapping);
|
||||
Stream.of(mapping.annotationType().getDeclaredAnnotations())
|
||||
.map(annotation -> createMapping(mapping, annotation))
|
||||
.filter(Objects::nonNull)
|
||||
.filter(m -> isNeedMapping(mappings, m))
|
||||
.forEach(deque::addLast);
|
||||
for (final Annotation annotation : AnnotationUtil.getDeclaredAnnotations(mapping.annotationType())) {
|
||||
if (mappings.containsKey(annotation.annotationType())) {
|
||||
continue;
|
||||
}
|
||||
final T m = createMapping(mapping, annotation);
|
||||
if (Objects.nonNull(m) && isNeedMapping(mappings, m)) {
|
||||
deque.addLast(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较两个实例是否相等
|
||||
*
|
||||
* @param o 对象
|
||||
* @return 是否
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
MetaAnnotatedElement<?> that = (MetaAnnotatedElement<?>)o;
|
||||
return element.equals(that.element) && mappingFactory.equals(that.mappingFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实例的哈希值
|
||||
*
|
||||
* @return 哈希值
|
||||
*/
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(element, mappingFactory);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,494 @@
|
||||
package cn.hutool.core.annotation;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.map.WeakConcurrentMap;
|
||||
import cn.hutool.core.reflect.MethodUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.Repeatable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 可重复注解收集器,用于从一个注解获得被它包含的可重复注解
|
||||
*
|
||||
* @author huangchengxing
|
||||
*/
|
||||
public interface RepeatableAnnotationCollector {
|
||||
|
||||
/**
|
||||
* 空实现
|
||||
*
|
||||
* @return {@link RepeatableAnnotationCollector}实例
|
||||
*/
|
||||
static RepeatableAnnotationCollector none() {
|
||||
return None.INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>当注解中有且仅有一个名为{@code value}的属性时,
|
||||
* 若该属性类型为注解数组,且该数组对应的注解类型被{@link Repeatable}注解,
|
||||
* 则收集器将返回该属性中包括的可重复注解。<br>
|
||||
* eg:
|
||||
* <pre><code>
|
||||
* // 容器注解
|
||||
* {@literal @}interface Annotation {
|
||||
* Item[] value() default {};
|
||||
* }
|
||||
* // 可重复注解
|
||||
* {@literal @}Repeatable(Annotation.class)
|
||||
* {@literal @}interface Item {}
|
||||
* </code></pre>
|
||||
* 解析任意{@code Annotation}注解对象,则可以获得{@code value}属性中的{@code Item}注解对象
|
||||
*
|
||||
* @return {@link RepeatableAnnotationCollector}实例
|
||||
* @see Standard
|
||||
*/
|
||||
static RepeatableAnnotationCollector standard() {
|
||||
return Standard.INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 当解析注解属性时,将根据给定的判断条件,确定该属性中是否含有可重复注解。<br>
|
||||
* 收集器将返回所有匹配的属性中的可重复注解。
|
||||
*
|
||||
* @param predicate 是否为容纳可重复注解的属性的判断条件
|
||||
* @return {@link RepeatableAnnotationCollector}实例
|
||||
*/
|
||||
static RepeatableAnnotationCollector condition(final BiPredicate<Annotation, Method> predicate) {
|
||||
return new Condition(predicate);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>当注解中存在有属性为注解数组,且该数组对应的注解类型被{@link Repeatable}注解时,
|
||||
* 认为该属性包含可重复注解。<br>
|
||||
* 收集器将返回所有符合上述条件的属性中的可重复注解。<br>
|
||||
* eg:
|
||||
* <pre><code>
|
||||
* {@literal @}interface Annotation {
|
||||
* Item1[] items1() default {};
|
||||
* Item2[] items2() default {};
|
||||
* }
|
||||
* </code></pre>
|
||||
* 解析任意{@code Annotation}注解对象,
|
||||
* 则可以获得{@code items1}属性中的{@code Item1}注解对象,
|
||||
* 以及{@code items2}属性中的{@code Item2}注解对象,
|
||||
*
|
||||
* @return {@link RepeatableAnnotationCollector}实例
|
||||
*/
|
||||
static RepeatableAnnotationCollector full() {
|
||||
return Full.INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空单例缓存
|
||||
*/
|
||||
static void clearSingletonCaches() {
|
||||
Standard.INSTANCE.repeatableMethodCache.clear();
|
||||
Full.INSTANCE.repeatableMethodCache.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>若一个注解是可重复注解的容器注解,则尝试通过其属性获得获得包含的注解对象。
|
||||
* 若包含的注解对象也是可重复注解的容器注解,则继续解析直到获得所有非容器注解为止。<br>
|
||||
* eg:
|
||||
* 若存在嵌套关系{@code a -> b -> c},
|
||||
* 则解析注解<em>a</em>,则将得到全部<em>c</em>注解。<br>
|
||||
* 如果注解不包含可重复注解,则返回<em>a</em>本身。
|
||||
*
|
||||
* @param annotation 容器注解
|
||||
* @return 容器注解中的可重复注解,若{@code annotation}不为容器注解,则数组中仅有其本身一个对象
|
||||
*/
|
||||
List<Annotation> getFinalRepeatableAnnotations(final Annotation annotation);
|
||||
|
||||
/**
|
||||
* <p>若一个注解是可重复注解的容器注解,则尝试通过其属性获得获得包含的注解对象。
|
||||
* 若包含的注解对象也是可重复注解的容器注解,则继续解析直到获得所有非容器注解为止。<br>
|
||||
* eg:
|
||||
* 若存在嵌套关系{@code a -> b -> c},则解析注解<em>a</em>,
|
||||
* 将获得全部<em>a</em>、<em>b</em>、<em>c</em>注解。<br>
|
||||
* 如果注解不包含可重复注解,则返回<em>a</em>本身。
|
||||
*
|
||||
* @param annotation 容器注解
|
||||
* @return 容器注解中的可重复注解,若{@code annotation}不为容器注解,则数组中仅有其本身一个对象
|
||||
*/
|
||||
List<Annotation> getAllRepeatableAnnotations(final Annotation annotation);
|
||||
|
||||
/**
|
||||
* <p>若一个注解是可重复注解的容器注解,则尝试通过其属性获得获得包含的指定类型注解对象。<br>
|
||||
* eg:
|
||||
* 若存在嵌套关系{@code a -> b -> c},则:
|
||||
* <ul>
|
||||
* <li>解析注解<em>a</em>:可获得<em>a</em>、<em>b</em>、<em>c</em>;</li>
|
||||
* <li>解析注解<em>b</em>:可获得<em>b</em>、<em>c</em>;</li>
|
||||
* <li>解析注解<em>c</em>:只可获得<em>c</em>;</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param annotation 容器注解
|
||||
* @param annotationType 注解类型
|
||||
* @param <T> 注解类型
|
||||
* @return 容器注解中的可重复注解
|
||||
*/
|
||||
<T extends Annotation> List<T> getRepeatableAnnotations(final Annotation annotation, final Class<T> annotationType);
|
||||
|
||||
/**
|
||||
* 空实现
|
||||
*/
|
||||
class None implements RepeatableAnnotationCollector {
|
||||
|
||||
/**
|
||||
* 默认实例
|
||||
*/
|
||||
private static final None INSTANCE = new None();
|
||||
|
||||
/**
|
||||
* 默认返回空集合
|
||||
*
|
||||
* @param annotation 注解
|
||||
* @return 空集合
|
||||
*/
|
||||
@Override
|
||||
public List<Annotation> getFinalRepeatableAnnotations(final Annotation annotation) {
|
||||
return Objects.isNull(annotation) ?
|
||||
Collections.emptyList() : Collections.singletonList(annotation);
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认返回空集合
|
||||
*
|
||||
* @param annotation 注解
|
||||
* @return 空集合
|
||||
*/
|
||||
@Override
|
||||
public List<Annotation> getAllRepeatableAnnotations(final Annotation annotation) {
|
||||
return Objects.isNull(annotation) ?
|
||||
Collections.emptyList() : Collections.singletonList(annotation);
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认返回空集合
|
||||
*
|
||||
* @param annotation 注解
|
||||
* @return 空集合
|
||||
*/
|
||||
@Override
|
||||
public <T extends Annotation> List<T> getRepeatableAnnotations(final Annotation annotation, final Class<T> annotationType) {
|
||||
if (Objects.isNull(annotation)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Objects.equals(annotation.annotationType(), annotationType) ?
|
||||
Collections.singletonList(annotationType.cast(annotation)) : Collections.emptyList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link RepeatableAnnotationCollector}的基本实现
|
||||
*/
|
||||
abstract class AbstractCollector implements RepeatableAnnotationCollector {
|
||||
|
||||
/**
|
||||
* <p>若一个注解是可重复注解的容器注解,则尝试通过其属性获得获得包含的注解对象。
|
||||
* 若包含的注解对象也是可重复注解的容器注解,则继续解析直到获得所有非容器注解为止。
|
||||
*
|
||||
* @param annotation 容器注解
|
||||
* @return 容器注解中的可重复注解,若{@code annotation}不为容器注解,则数组中仅有其本身一个对象
|
||||
*/
|
||||
@Override
|
||||
public final List<Annotation> getFinalRepeatableAnnotations(final Annotation annotation) {
|
||||
return find(annotation, null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>若一个注解是可重复注解的容器注解,则尝试通过其属性获得获得包含的注解对象。
|
||||
* 若包含的注解对象也是可重复注解的容器注解,则继续解析直到获得所有非容器注解为止。<br>
|
||||
* 当{@code accumulate}为{@code true}时,返回结果为全量的注解。
|
||||
* eg:
|
||||
* 若存在嵌套关系{@code a -> b -> c},则解析注解<em>a</em>,
|
||||
* 将获得全部<em>a</em>、<em>b</em>、<em>c</em>注解。
|
||||
* 如果注解不包含可重复注解,则返回其本身。
|
||||
*
|
||||
* @param annotation 容器注解
|
||||
* @return 容器注解中的可重复注解,若{@code annotation}不为容器注解,则数组中仅有其本身一个对象
|
||||
*/
|
||||
@Override
|
||||
public List<Annotation> getAllRepeatableAnnotations(Annotation annotation) {
|
||||
return find(annotation, null, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 若一个注解是可重复注解的容器注解,则尝试通过其属性获得获得包含的指定类型注解对象
|
||||
*
|
||||
* @param annotation 容器注解
|
||||
* @param annotationType 注解类型
|
||||
* @param <T> 注解类型
|
||||
* @return 容器注解中的可重复注解
|
||||
*/
|
||||
@Override
|
||||
public <T extends Annotation> List<T> getRepeatableAnnotations(
|
||||
final Annotation annotation, final Class<T> annotationType) {
|
||||
final List<Annotation> annotations = find(annotation, t -> Objects.equals(t.annotationType(), annotationType), false);
|
||||
return annotations.stream()
|
||||
.map(annotationType::cast)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归遍历注解,将其平铺
|
||||
*/
|
||||
private List<Annotation> find(
|
||||
final Annotation annotation, final java.util.function.Predicate<Annotation> condition, final boolean accumulate) {
|
||||
if (Objects.isNull(annotation)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final boolean hasCondition = Objects.nonNull(condition);
|
||||
final List<Annotation> results = new ArrayList<>();
|
||||
final Deque<Annotation> deque = new LinkedList<>();
|
||||
deque.add(annotation);
|
||||
while (!deque.isEmpty()) {
|
||||
final Annotation source = deque.removeFirst();
|
||||
final List<Method> repeatableMethods = resolveRepeatableMethod(source);
|
||||
// 若是累加的,则记录每一个注解
|
||||
if (accumulate) {
|
||||
results.add(source);
|
||||
}
|
||||
final boolean isTarget = hasCondition && condition.test(source);
|
||||
if (CollUtil.isEmpty(repeatableMethods) || isTarget) {
|
||||
// 不是累加的,则仅当正在处理的注解不为可重复注解时才记录
|
||||
boolean shouldProcess = !accumulate && (!hasCondition || isTarget);
|
||||
if (shouldProcess) {
|
||||
results.add(source);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
final Annotation[] repeatableAnnotation = repeatableMethods.stream()
|
||||
.map(method -> getRepeatableAnnotationsFormAttribute(source, method))
|
||||
.filter(ArrayUtil::isNotEmpty)
|
||||
.flatMap(Stream::of)
|
||||
.toArray(Annotation[]::new);
|
||||
if (ArrayUtil.isNotEmpty(repeatableAnnotation)) {
|
||||
CollUtil.addAll(deque, repeatableAnnotation);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用{@code value}方法,获得嵌套的可重复注解
|
||||
*
|
||||
* @param annotation 注解对象
|
||||
* @param method 容纳可重复注解的方法
|
||||
* @return 可重复注解
|
||||
* @throws ClassCastException 当{@code method}调用结果无法正确转为{@link Annotation[]}类型时抛出
|
||||
*/
|
||||
protected Annotation[] getRepeatableAnnotationsFormAttribute(final Annotation annotation, final Method method) {
|
||||
return MethodUtil.invoke(annotation, method);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析获得注解中存放可重复注解的属性
|
||||
*
|
||||
* @param annotation 注解
|
||||
* @return 属性
|
||||
*/
|
||||
protected abstract List<Method> resolveRepeatableMethod(final Annotation annotation);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 标准实现,当注解中有且仅有一个名为{@code value}的属性时,
|
||||
* 若该属性类型为注解数组,且该数组对应的注解类型被{@link Repeatable}注解,
|
||||
* 则收集器将返回该属性中包括的可重复注解。
|
||||
*/
|
||||
class Standard extends AbstractCollector {
|
||||
|
||||
/**
|
||||
* 默认的value属性
|
||||
*/
|
||||
private static final String VALUE = "value";
|
||||
|
||||
/**
|
||||
* 默认实例
|
||||
*/
|
||||
private static final Standard INSTANCE = new Standard();
|
||||
|
||||
/**
|
||||
* 空方法缓存
|
||||
*/
|
||||
private static final Object NONE = new Object();
|
||||
|
||||
/**
|
||||
* 可重复注解对应的方法缓存
|
||||
*/
|
||||
private final Map<Class<? extends Annotation>, Object> repeatableMethodCache = new WeakConcurrentMap<>();
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
Standard() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析获得注解中存放可重复注解的属性
|
||||
*
|
||||
* @param annotation 注解
|
||||
* @return 属性
|
||||
*/
|
||||
@Override
|
||||
protected List<Method> resolveRepeatableMethod(final Annotation annotation) {
|
||||
final Object cache = MapUtil.computeIfAbsent(
|
||||
repeatableMethodCache, annotation.annotationType(), this::resolveRepeatableMethodFromType
|
||||
);
|
||||
return (cache == NONE) ? null : Collections.singletonList((Method)cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从缓存中获得存放可重复注解的属性
|
||||
*/
|
||||
private Object resolveRepeatableMethodFromType(final Class<? extends Annotation> annotationType) {
|
||||
final Method[] attributes = AnnotationUtil.getAnnotationAttributes(annotationType);
|
||||
if (attributes.length != 1) {
|
||||
return NONE;
|
||||
}
|
||||
return isRepeatableMethod(attributes[0]) ? attributes[0] : NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断方法是否为容器注解的{@code value}方法
|
||||
*
|
||||
* @param attribute 注解的属性
|
||||
* @return 该属性是否为注解存放可重复注解的方法
|
||||
*/
|
||||
protected boolean isRepeatableMethod(final Method attribute) {
|
||||
// 属性名需为“value”
|
||||
if (!CharSequenceUtil.equals(VALUE, attribute.getName())) {
|
||||
return false;
|
||||
}
|
||||
final Class<?> attributeType = attribute.getReturnType();
|
||||
// 返回值类型需为数组
|
||||
return attributeType.isArray()
|
||||
// 且数组元素需为注解
|
||||
&& attributeType.getComponentType()
|
||||
.isAnnotation()
|
||||
// 该注解类必须被@Repeatable注解,但不要求与当前属性的声明方法一致
|
||||
&& attributeType.getComponentType()
|
||||
.isAnnotationPresent(Repeatable.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义判断条件的实现,当解析注解属性时,将根据给定的判断条件,
|
||||
* 确定该属性中是否含有可重复注解,收集器将返回所有匹配的属性中的可重复注解。
|
||||
*/
|
||||
class Condition extends AbstractCollector {
|
||||
|
||||
/**
|
||||
* 是否为容纳可重复注解的属性的判断条件
|
||||
*/
|
||||
private final BiPredicate<Annotation, Method> predicate;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param predicate 是否为容纳可重复注解的属性的判断条件
|
||||
*/
|
||||
Condition(final BiPredicate<Annotation, Method> predicate) {
|
||||
this.predicate = Objects.requireNonNull(predicate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析获得注解中存放可重复注解的属性
|
||||
*
|
||||
* @param annotation 注解
|
||||
* @return 属性
|
||||
*/
|
||||
@Override
|
||||
protected List<Method> resolveRepeatableMethod(final Annotation annotation) {
|
||||
return Stream.of(AnnotationUtil.getAnnotationAttributes(annotation.annotationType()))
|
||||
.filter(method -> predicate.test(annotation, method))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 全量实现,当注解中存在有属性为注解数组,且该数组对应的注解类型被{@link Repeatable}注解时,
|
||||
* 认为该属性包含可重复注解。<br>
|
||||
* 收集器将返回所有符合上述条件的属性中的可重复注解。
|
||||
*/
|
||||
class Full extends AbstractCollector {
|
||||
|
||||
/**
|
||||
* 默认实例
|
||||
*/
|
||||
private static final Full INSTANCE = new Full();
|
||||
|
||||
/**
|
||||
* 空方法缓存
|
||||
*/
|
||||
private static final Object NONE = new Object();
|
||||
|
||||
/**
|
||||
* 可重复注解对应的方法缓存
|
||||
*/
|
||||
private final Map<Class<? extends Annotation>, Object> repeatableMethodCache = new WeakConcurrentMap<>();
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
Full() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析获得注解中存放可重复注解的属性
|
||||
*
|
||||
* @param annotation 注解
|
||||
* @return 属性
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected List<Method> resolveRepeatableMethod(final Annotation annotation) {
|
||||
final Object cache = MapUtil.computeIfAbsent(
|
||||
repeatableMethodCache, annotation.annotationType(), this::resolveRepeatableMethodFromType
|
||||
);
|
||||
return (cache == NONE) ? null : (List<Method>)cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从缓存中获得存放可重复注解的属性
|
||||
*/
|
||||
private Object resolveRepeatableMethodFromType(final Class<? extends Annotation> annotationType) {
|
||||
final List<Method> methods = Stream.of(AnnotationUtil.getAnnotationAttributes(annotationType))
|
||||
.filter(this::isRepeatableMethod)
|
||||
.collect(Collectors.toList());
|
||||
return methods.isEmpty() ? NONE : methods;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断方法是否为容器注解的{@code value}方法
|
||||
*
|
||||
* @param attribute 注解的属性
|
||||
* @return 该属性是否为注解存放可重复注解的方法
|
||||
*/
|
||||
protected boolean isRepeatableMethod(final Method attribute) {
|
||||
final Class<?> attributeType = attribute.getReturnType();
|
||||
// 返回值类型需为数组
|
||||
return attributeType.isArray()
|
||||
// 且数组元素需为注解
|
||||
&& attributeType.getComponentType()
|
||||
.isAnnotation()
|
||||
// 该注解类必须被@Repeatable注解,但不要求与当前属性的声明方法一致
|
||||
&& attributeType.getComponentType()
|
||||
.isAnnotationPresent(Repeatable.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,389 @@
|
||||
package cn.hutool.core.annotation;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.util.*;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* <p>支持可重复注解的增强{@link AnnotatedElement},
|
||||
* 功能与{@link MetaAnnotatedElement}类似,但是存在下述差异:
|
||||
* <ul>
|
||||
* <li>
|
||||
* 限制以同一根注解延伸出的树结构上——而不是{@link AnnotatedElement}上——每种类型注解只能保留一个,
|
||||
* 即当{@link AnnotatedElement}存在多个根注解有相同的元注解时,这些元注解会都会被扫描到;
|
||||
* </li>
|
||||
* <li>
|
||||
* 支持扫描{@link AnnotatedElement}可重复注解,即当当前实例指定的{@link RepeatableAnnotationCollector}
|
||||
* 支持从{@link AnnotatedElement}上直接声明的注解中获得可重复注解时,
|
||||
* 则将会自动将其展开直到不为容器注解为止。<br>
|
||||
* eg:<br>
|
||||
* A上存在注解<em>X</em>,该注解是一个容器注解,内部可重复注解<em>Y</em>,
|
||||
* 包含解析后,得到注解<em>X</em>与可重复注解<em>Y</em>,<br>
|
||||
* 同理,若存在<em>X</em>、<em>Y</em>、<em>X</em>的嵌套关系,则解析后获得全部三者;
|
||||
* </li>
|
||||
* </ul>
|
||||
* 由于上述机制,当通过实例的{@link #getAnnotation(Class)}或{@link #getDeclaredAnnotation(Class)}
|
||||
* 方法获得指定类型注解时,若该类型注解存在多个,仅能尽可能获得最先被扫描到的那一个。
|
||||
*
|
||||
* @author huangchengxing
|
||||
* @since 6.0.0
|
||||
* @see RepeatableAnnotationCollector
|
||||
*/
|
||||
public class RepeatableMetaAnnotatedElement<T extends AnnotationMapping<Annotation>> implements AnnotatedElement, Iterable<T> {
|
||||
|
||||
/**
|
||||
* 包装的{@link AnnotatedElement}对象
|
||||
*/
|
||||
private final AnnotatedElement element;
|
||||
|
||||
/**
|
||||
* 创建{@link AnnotationMapping}的工厂方法
|
||||
*/
|
||||
private final BiFunction<T, Annotation, T> mappingFactory;
|
||||
|
||||
/**
|
||||
* 解析得到的根注解与元注解的聚合体
|
||||
*/
|
||||
private final List<Aggregation> aggregations;
|
||||
|
||||
/**
|
||||
* 可重复注解收集器
|
||||
*/
|
||||
private final RepeatableAnnotationCollector repeatableCollector;
|
||||
|
||||
/**
|
||||
* 获取{@link AnnotatedElement}上的注解结构,该方法会针对相同的{@link AnnotatedElement}缓存映射对象
|
||||
*
|
||||
* @param element 被注解元素
|
||||
* @param mappingFactory 创建{@link AnnotationMapping}的工厂方法,返回值为{@code null}时将忽略该注解
|
||||
* @param <A> {@link AnnotationMapping}类型
|
||||
* @return {@link AnnotatedElement}上的注解结构
|
||||
*/
|
||||
public static <A extends AnnotationMapping<Annotation>> RepeatableMetaAnnotatedElement<A> create(
|
||||
final AnnotatedElement element, final BiFunction<A, Annotation, A> mappingFactory) {
|
||||
return create(RepeatableAnnotationCollector.standard(), element, mappingFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取{@link AnnotatedElement}上的注解结构,该方法会针对相同的{@link AnnotatedElement}缓存映射对象
|
||||
*
|
||||
* @param collector 可重复注解收集器
|
||||
* @param element 被注解元素
|
||||
* @param mappingFactory 创建{@link AnnotationMapping}的工厂方法,返回值为{@code null}时将忽略该注解
|
||||
* @param <A> {@link AnnotationMapping}类型
|
||||
* @return {@link AnnotatedElement}上的注解结构
|
||||
*/
|
||||
public static <A extends AnnotationMapping<Annotation>> RepeatableMetaAnnotatedElement<A> create(
|
||||
final RepeatableAnnotationCollector collector,
|
||||
final AnnotatedElement element,
|
||||
final BiFunction<A, Annotation, A> mappingFactory) {
|
||||
return new RepeatableMetaAnnotatedElement<>(collector, element, mappingFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个支持可重复注解的增强{@link AnnotatedElement}
|
||||
*
|
||||
* @param element 包装的{@link AnnotatedElement}对象
|
||||
* @param mappingFactory 创建{@link AnnotationMapping}的工厂方法
|
||||
*/
|
||||
RepeatableMetaAnnotatedElement(
|
||||
final RepeatableAnnotationCollector repeatableCollector, final AnnotatedElement element, final BiFunction<T, Annotation, T> mappingFactory) {
|
||||
this.element = Objects.requireNonNull(element);
|
||||
this.mappingFactory = Objects.requireNonNull(mappingFactory);
|
||||
this.repeatableCollector = repeatableCollector;
|
||||
this.aggregations = Collections.unmodifiableList(initAggregations(element));
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定注解是否在{@link #element}上直接声明的注解、直接声明的注解包含的可重复注解,
|
||||
* 以及他们的元注解中存在
|
||||
*
|
||||
* @param annotationType 注解类型
|
||||
* @return 是否
|
||||
*/
|
||||
@Override
|
||||
public boolean isAnnotationPresent(final Class<? extends Annotation> annotationType) {
|
||||
return aggregations.stream()
|
||||
.anyMatch(aggregation -> aggregation.getMappings().containsKey(annotationType));
|
||||
}
|
||||
|
||||
/**
|
||||
* 从{@link #element}上直接声明的注解、直接声明的注解包含的可重复注解,以及它们的元注解中获得指定类型的注解
|
||||
*
|
||||
* @param annotationType 注解类型
|
||||
* @param <A> 注解类型
|
||||
* @return 注解
|
||||
*/
|
||||
@Override
|
||||
public <A extends Annotation> A getAnnotation(final Class<A> annotationType) {
|
||||
return aggregations.stream()
|
||||
.map(Aggregation::getMappings)
|
||||
.map(annotations -> annotations.get(annotationType))
|
||||
.filter(Objects::nonNull)
|
||||
.findFirst()
|
||||
.map(T::getResolvedAnnotation)
|
||||
.map(annotationType::cast)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取{@link #element}上直接声明的注解、直接声明的注解包含的可重复注解,以及它们的元注解
|
||||
*
|
||||
* @return 注解
|
||||
*/
|
||||
@Override
|
||||
public Annotation[] getAnnotations() {
|
||||
return aggregations.stream()
|
||||
.map(aggregation -> aggregation.getMappings().values())
|
||||
.flatMap(Collection::stream)
|
||||
.map(T::getResolvedAnnotation)
|
||||
.toArray(Annotation[]::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从{@link #element}上直接声明的注解、直接声明的注解包含的可重复注解,以及它们的元注解中获得指定类型的注解
|
||||
*
|
||||
* @param annotationType 注解类型
|
||||
* @param <A> 注解类型
|
||||
* @return 注解
|
||||
*/
|
||||
@Override
|
||||
public <A extends Annotation> A[] getAnnotationsByType(final Class<A> annotationType) {
|
||||
return aggregations.stream()
|
||||
.map(aggregation -> aggregation.getMappings().get(annotationType))
|
||||
.filter(Objects::nonNull)
|
||||
.map(T::getResolvedAnnotation)
|
||||
.map(annotationType::cast)
|
||||
.toArray(size -> ArrayUtil.newArray(annotationType, size));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取由{@link #element}直接声明的注解,不包含被直接声明的容器注解包括的可重复注解
|
||||
*
|
||||
* @return 注解
|
||||
*/
|
||||
@Override
|
||||
public Annotation[] getDeclaredAnnotations() {
|
||||
return aggregations.stream()
|
||||
.filter(Aggregation::isDirect)
|
||||
.map(Aggregation::getRoot)
|
||||
.map(T::getResolvedAnnotation)
|
||||
.toArray(Annotation[]::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取由{@link #element}直接声明的注解,不包含被直接声明的容器注解包括的可重复注解
|
||||
*
|
||||
* @param annotationType 注解类型
|
||||
* @param <A> 注解类型
|
||||
* @return 注解
|
||||
*/
|
||||
@Override
|
||||
public <A extends Annotation> A getDeclaredAnnotation(final Class<A> annotationType) {
|
||||
return aggregations.stream()
|
||||
.filter(Aggregation::isDirect)
|
||||
.map(Aggregation::getRoot)
|
||||
.filter(annotation -> Objects.equals(annotationType, annotation.annotationType()))
|
||||
.findFirst()
|
||||
.map(T::getResolvedAnnotation)
|
||||
.map(annotationType::cast)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取由{@link #element}直接声明的注解,不包含被直接声明的容器注解包括的可重复注解
|
||||
*
|
||||
* @param annotationType 注解类型
|
||||
* @param <A> 注解类型
|
||||
* @return 注解
|
||||
*/
|
||||
@Override
|
||||
public <A extends Annotation> A[] getDeclaredAnnotationsByType(final Class<A> annotationType) {
|
||||
return aggregations.stream()
|
||||
.filter(Aggregation::isDirect)
|
||||
.map(Aggregation::getRoot)
|
||||
.filter(annotation -> Objects.equals(annotationType, annotation.annotationType()))
|
||||
.map(T::getResolvedAnnotation)
|
||||
.map(annotationType::cast)
|
||||
.toArray(size -> ArrayUtil.newArray(annotationType, size));
|
||||
}
|
||||
|
||||
/**
|
||||
* 注解对象
|
||||
*
|
||||
* @return 被包装的原始元素
|
||||
*/
|
||||
public AnnotatedElement getElement() {
|
||||
return element;
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较两个实例是否相等
|
||||
*
|
||||
* @param o 对象
|
||||
* @return 是否
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final RepeatableMetaAnnotatedElement<?> that = (RepeatableMetaAnnotatedElement<?>)o;
|
||||
return element.equals(that.element) && mappingFactory.equals(that.mappingFactory) && repeatableCollector.equals(that.repeatableCollector);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实例的哈希值
|
||||
*
|
||||
* @return 哈希值
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(element, mappingFactory, repeatableCollector);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取迭代器
|
||||
*
|
||||
* @return 迭代器
|
||||
*/
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return aggregations.stream()
|
||||
.map(Aggregation::getMappings)
|
||||
.map(Map::values)
|
||||
.flatMap(Collection::stream)
|
||||
.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
private List<Aggregation> initAggregations(final AnnotatedElement element) {
|
||||
// TODO 若有可能,一并支持处理元注解中的可重复注解
|
||||
List<Aggregation> result = new ArrayList<>();
|
||||
for (final Annotation declaredAnnotation : AnnotationUtil.getDeclaredAnnotations(element)) {
|
||||
List<Aggregation> repeatableAnnotations = collectRepeatable(declaredAnnotation);
|
||||
if (CollUtil.isNotEmpty(repeatableAnnotations)) {
|
||||
result.addAll(repeatableAnnotations);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 若当前注解是可重复注解的容器,则将其平铺后把可重复注解加入{@link #aggregations}
|
||||
*/
|
||||
private List<Aggregation> collectRepeatable(final Annotation annotation) {
|
||||
return repeatableCollector.getAllRepeatableAnnotations(annotation)
|
||||
.stream()
|
||||
.map(a -> new Aggregation(a, Objects.equals(a, annotation)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 由根注解与其元注解构成的注解聚合
|
||||
*/
|
||||
class Aggregation {
|
||||
|
||||
/**
|
||||
* 根注解
|
||||
*/
|
||||
private final T root;
|
||||
|
||||
/**
|
||||
* 注解
|
||||
*/
|
||||
private volatile Map<Class<? extends Annotation>, T> mappings;
|
||||
|
||||
/**
|
||||
* 是否是由{@link #element}直接声明的注解
|
||||
*/
|
||||
private final boolean isDirect;
|
||||
|
||||
/**
|
||||
* 创建一个合并聚合
|
||||
*/
|
||||
public Aggregation(final Annotation root, final boolean isDirect) {
|
||||
this.root = mappingFactory.apply(null, root);
|
||||
this.isDirect = isDirect;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得收集到的注解
|
||||
*/
|
||||
private Map<Class<? extends Annotation>, T> getMappings() {
|
||||
if (Objects.isNull(mappings)) {
|
||||
synchronized (this) {
|
||||
if (Objects.isNull(mappings)) {
|
||||
mappings = Collections.unmodifiableMap(initMetaAnnotations());
|
||||
}
|
||||
}
|
||||
}
|
||||
return mappings;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得注解及其元注解
|
||||
*/
|
||||
private Map<Class<? extends Annotation>, T> initMetaAnnotations() {
|
||||
final Map<Class<? extends Annotation>, T> collectedMappings = new LinkedHashMap<>();
|
||||
final Deque<T> deque = new LinkedList<>();
|
||||
deque.add(root);
|
||||
while (!deque.isEmpty()) {
|
||||
final T source = deque.removeFirst();
|
||||
if (!isNeedMapping(collectedMappings, source)) {
|
||||
continue;
|
||||
}
|
||||
collectedMappings.put(source.annotationType(), source);
|
||||
for (final Annotation annotation : AnnotationUtil.getDeclaredAnnotations(source.annotationType())) {
|
||||
if (collectedMappings.containsKey(annotation.annotationType())) {
|
||||
continue;
|
||||
}
|
||||
final T mapping = mappingFactory.apply(source, annotation);
|
||||
if (Objects.nonNull(mapping) && isNeedMapping(collectedMappings, mapping)) {
|
||||
deque.addLast(mapping);
|
||||
}
|
||||
}
|
||||
}
|
||||
return collectedMappings;
|
||||
}
|
||||
|
||||
/**
|
||||
* 该注解是否需要映射 <br>
|
||||
* 默认情况下,已经处理过、或在{@link java.lang}包下的注解不会被处理
|
||||
*/
|
||||
private boolean isNeedMapping(final Map<Class<? extends Annotation>, T> mappings, final Annotation annotation) {
|
||||
return !CharSequenceUtil.startWith(annotation.annotationType().getName(), "java.lang.")
|
||||
&& !mappings.containsKey(annotation.annotationType());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根注解是否由{@link #element}直接声明
|
||||
*
|
||||
* @return 是否
|
||||
*/
|
||||
public boolean isDirect() {
|
||||
return isDirect;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取根注解
|
||||
*
|
||||
* @return 根注解
|
||||
*/
|
||||
public T getRoot() {
|
||||
return root;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -170,7 +170,7 @@ public class ResolvedAnnotationMapping implements AnnotationMapping<Annotation>
|
||||
Arrays.fill(this.resolvedAttributes, NOT_FOUND_INDEX);
|
||||
|
||||
// 若有必要,解析属性
|
||||
// TODO 可能的改进:flag改为枚举,使得可以自行选择:1.只支持属性别名,2.只支持属性覆盖,3.两个都支持,4.两个都不支持
|
||||
// TODO flag改为枚举,使得可以自行选择:1.只支持属性别名,2.只支持属性覆盖,3.两个都支持,4.两个都不支持
|
||||
this.resolved = resolveAttribute && resolveAttributes();
|
||||
}
|
||||
|
||||
@ -178,6 +178,7 @@ public class ResolvedAnnotationMapping implements AnnotationMapping<Annotation>
|
||||
* 解析属性
|
||||
*/
|
||||
private boolean resolveAttributes() {
|
||||
// TODO 支持处理@PropIgnore,被标记的属性无法被覆写,也不会被别名关联
|
||||
// 解析同一注解中的别名
|
||||
resolveAliasAttributes();
|
||||
// 使用子注解覆写当前注解中的属性
|
||||
@ -525,8 +526,8 @@ public class ResolvedAnnotationMapping implements AnnotationMapping<Annotation>
|
||||
|
||||
// 根据AliasSet更新关联的属性
|
||||
Stream.of(aliasSets).filter(Objects::nonNull).forEach(set -> {
|
||||
final int resolvedIndex = set.resolve();
|
||||
set.forEach(index -> resolvedAttributes[index] = resolvedIndex);
|
||||
final int effectiveAttributeIndex = set.determineEffectiveAttribute();
|
||||
set.forEach(index -> resolvedAttributes[index] = effectiveAttributeIndex);
|
||||
});
|
||||
}
|
||||
|
||||
@ -616,7 +617,7 @@ public class ResolvedAnnotationMapping implements AnnotationMapping<Annotation>
|
||||
* <li>若有多个属性具有非默认值,则要求所有的非默认值都必须相等,若符合并返回该首个具有非默认值的属性,否则报错;</li>
|
||||
* </ul>
|
||||
*/
|
||||
private int resolve() {
|
||||
private int determineEffectiveAttribute() {
|
||||
int resolvedIndex = NOT_FOUND_INDEX;
|
||||
boolean hasNotDef = false;
|
||||
Object lastValue = null;
|
||||
|
@ -15,11 +15,7 @@ import cn.hutool.core.util.ArrayUtil;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
@ -33,6 +29,10 @@ public class MethodUtil {
|
||||
* 方法缓存
|
||||
*/
|
||||
private static final WeakConcurrentMap<Class<?>, Method[]> METHODS_CACHE = new WeakConcurrentMap<>();
|
||||
/**
|
||||
* 直接声明的方法缓存
|
||||
*/
|
||||
private static final WeakConcurrentMap<Class<?>, Method[]> DECLARED_METHODS_CACHE = new WeakConcurrentMap<>();
|
||||
|
||||
// --------------------------------------------------------------------------------------------------------- method
|
||||
|
||||
@ -323,6 +323,19 @@ public class MethodUtil {
|
||||
(key) -> getMethodsDirectly(beanClass, true, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得类中所有直接声明方法,不包括其父类中的方法
|
||||
*
|
||||
* @param beanClass 类,非{@code null}
|
||||
* @return 方法列表
|
||||
* @throws SecurityException 安全检查异常
|
||||
*/
|
||||
public static Method[] getDeclaredMethods(final Class<?> beanClass) throws SecurityException {
|
||||
Assert.notNull(beanClass);
|
||||
return DECLARED_METHODS_CACHE.computeIfAbsent(beanClass,
|
||||
key -> getMethodsDirectly(beanClass, false, Objects.equals(Object.class, beanClass)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得一个类中所有方法列表,直接反射获取,无缓存<br>
|
||||
* 接口获取方法和默认方法,获取的方法包括:
|
||||
|
@ -37,6 +37,31 @@ public class AnnotatedElementUtilTest {
|
||||
ANNOTATION6, ANNOTATION5 // Interface.class's annotations
|
||||
};
|
||||
|
||||
@Test
|
||||
public void testClearCaches() {
|
||||
AnnotatedElement type = Foo.class;
|
||||
|
||||
AnnotatedElement element = AnnotatedElementUtil.getResolvedMetaElementCache(type);
|
||||
Assert.assertSame(element, AnnotatedElementUtil.getResolvedMetaElementCache(type));
|
||||
AnnotatedElementUtil.clearCaches();
|
||||
Assert.assertNotSame(element, AnnotatedElementUtil.getResolvedMetaElementCache(type));
|
||||
|
||||
element = AnnotatedElementUtil.getMetaElementCache(type);
|
||||
Assert.assertSame(element, AnnotatedElementUtil.getMetaElementCache(type));
|
||||
AnnotatedElementUtil.clearCaches();
|
||||
Assert.assertNotSame(element, AnnotatedElementUtil.getMetaElementCache(type));
|
||||
|
||||
element = AnnotatedElementUtil.getResolvedRepeatableMetaElementCache(type);
|
||||
Assert.assertSame(element, AnnotatedElementUtil.getResolvedRepeatableMetaElementCache(type));
|
||||
AnnotatedElementUtil.clearCaches();
|
||||
Assert.assertNotSame(element, AnnotatedElementUtil.getResolvedRepeatableMetaElementCache(type));
|
||||
|
||||
element = AnnotatedElementUtil.getRepeatableMetaElementCache(type);
|
||||
Assert.assertSame(element, AnnotatedElementUtil.getRepeatableMetaElementCache(type));
|
||||
AnnotatedElementUtil.clearCaches();
|
||||
Assert.assertNotSame(element, AnnotatedElementUtil.getRepeatableMetaElementCache(type));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsAnnotated() {
|
||||
Assert.assertTrue(AnnotatedElementUtil.isAnnotated(Foo.class, Annotation1.class));
|
||||
@ -243,6 +268,25 @@ public class AnnotatedElementUtilTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAllAnnotations() {
|
||||
Annotation3[] resolvedAnnotation3s = AnnotatedElementUtil.getAllAnnotations(Foo.class, Annotation3.class);
|
||||
Assert.assertEquals(1, resolvedAnnotation3s.length);
|
||||
Assert.assertEquals(ANNOTATION3, resolvedAnnotation3s[0]); // value与alias互为别名
|
||||
|
||||
Annotation2[] resolvedAnnotation2s = AnnotatedElementUtil.getAllAnnotations(Foo.class, Annotation2.class);
|
||||
Assert.assertEquals(1, resolvedAnnotation2s.length);
|
||||
Assert.assertEquals(ANNOTATION2, resolvedAnnotation2s[0]); // value与alias互为别名
|
||||
|
||||
Annotation1[] resolvedAnnotation1s = AnnotatedElementUtil.getAllAnnotations(Foo.class, Annotation1.class);
|
||||
Assert.assertEquals(1, resolvedAnnotation1s.length);
|
||||
Assert.assertEquals(ANNOTATION1, resolvedAnnotation1s[0]);
|
||||
|
||||
Assert.assertEquals(0, AnnotatedElementUtil.getAllAnnotations(Foo.class, Annotation4.class).length);
|
||||
Assert.assertEquals(0, AnnotatedElementUtil.getAllAnnotations(Foo.class, Annotation5.class).length);
|
||||
Assert.assertEquals(0, AnnotatedElementUtil.getAllAnnotations(Foo.class, Annotation6.class).length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetResolvedAnnotation() {
|
||||
Annotation3 resolvedAnnotation3 = AnnotatedElementUtil.getResolvedAnnotation(Foo.class, Annotation3.class);
|
||||
@ -264,6 +308,33 @@ public class AnnotatedElementUtilTest {
|
||||
Assert.assertNull(AnnotatedElementUtil.getResolvedAnnotation(Foo.class, Annotation6.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAllResolvedAnnotations() {
|
||||
Annotation3[] resolvedAnnotation3s = AnnotatedElementUtil.getAllResolvedAnnotations(Foo.class, Annotation3.class);
|
||||
Assert.assertEquals(1, resolvedAnnotation3s.length);
|
||||
Annotation3 resolvedAnnotation3 = resolvedAnnotation3s[0];
|
||||
Assert.assertNotNull(resolvedAnnotation3);
|
||||
Assert.assertEquals(resolvedAnnotation3.alias(), ANNOTATION3.value());
|
||||
Assert.assertEquals(resolvedAnnotation3.alias(), resolvedAnnotation3.value()); // value与alias互为别名
|
||||
|
||||
Annotation2[] resolvedAnnotation2s = AnnotatedElementUtil.getAllResolvedAnnotations(Foo.class, Annotation2.class);
|
||||
Assert.assertEquals(1, resolvedAnnotation2s.length);
|
||||
Annotation2 resolvedAnnotation2 = resolvedAnnotation2s[0];
|
||||
Assert.assertNotNull(resolvedAnnotation2);
|
||||
Assert.assertEquals(resolvedAnnotation2.num(), ANNOTATION3.num()); // num属性被Annotation3.num覆盖
|
||||
|
||||
Annotation1[] resolvedAnnotation1s = AnnotatedElementUtil.getAllResolvedAnnotations(Foo.class, Annotation1.class);
|
||||
Assert.assertEquals(1, resolvedAnnotation1s.length);
|
||||
Annotation1 resolvedAnnotation1 = resolvedAnnotation1s[0];
|
||||
Assert.assertNotNull(resolvedAnnotation1);
|
||||
Assert.assertEquals(ANNOTATION3.value(), resolvedAnnotation1.value()); // value属性被Annotation3.value覆盖
|
||||
Assert.assertEquals(resolvedAnnotation1.value(), resolvedAnnotation1.alias()); // value与alias互为别名
|
||||
|
||||
Assert.assertEquals(0, AnnotatedElementUtil.getAllResolvedAnnotations(Foo.class, Annotation4.class).length);
|
||||
Assert.assertEquals(0, AnnotatedElementUtil.getAllResolvedAnnotations(Foo.class, Annotation5.class).length);
|
||||
Assert.assertEquals(0, AnnotatedElementUtil.getAllResolvedAnnotations(Foo.class, Annotation6.class).length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetResolvedAnnotations() {
|
||||
Map<Class<?>, Annotation> annotationMap = Stream.of(AnnotatedElementUtil.getResolvedAnnotations(Foo.class))
|
||||
@ -299,6 +370,20 @@ public class AnnotatedElementUtilTest {
|
||||
Assert.assertNull(AnnotatedElementUtil.getDirectlyAnnotation(Foo.class, Annotation6.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAllDirectlyAnnotations() {
|
||||
Annotation3[] resolvedAnnotation3s = AnnotatedElementUtil.getAllDirectlyAnnotations(Foo.class, Annotation3.class);
|
||||
Assert.assertEquals(1, resolvedAnnotation3s.length);
|
||||
Annotation3 resolvedAnnotation3 = resolvedAnnotation3s[0];
|
||||
Assert.assertEquals(ANNOTATION3, resolvedAnnotation3);
|
||||
|
||||
Assert.assertEquals(0, AnnotatedElementUtil.getAllDirectlyResolvedAnnotations(Foo.class, Annotation2.class).length);
|
||||
Assert.assertEquals(0, AnnotatedElementUtil.getAllDirectlyResolvedAnnotations(Foo.class, Annotation1.class).length);
|
||||
Assert.assertEquals(0, AnnotatedElementUtil.getAllDirectlyResolvedAnnotations(Foo.class, Annotation4.class).length);
|
||||
Assert.assertEquals(0, AnnotatedElementUtil.getAllDirectlyResolvedAnnotations(Foo.class, Annotation5.class).length);
|
||||
Assert.assertEquals(0, AnnotatedElementUtil.getAllDirectlyResolvedAnnotations(Foo.class, Annotation6.class).length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDirectlyAnnotations() {
|
||||
Annotation[] annotations = AnnotatedElementUtil.getDirectlyAnnotations(Foo.class);
|
||||
@ -320,6 +405,22 @@ public class AnnotatedElementUtilTest {
|
||||
Assert.assertNull(AnnotatedElementUtil.getDirectlyResolvedAnnotation(Foo.class, Annotation6.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDirectlyAllResolvedAnnotations() {
|
||||
Annotation3[] resolvedAnnotation3s = AnnotatedElementUtil.getAllDirectlyResolvedAnnotations(Foo.class, Annotation3.class);
|
||||
Assert.assertEquals(1, resolvedAnnotation3s.length);
|
||||
Annotation3 resolvedAnnotation3 = resolvedAnnotation3s[0];
|
||||
Assert.assertNotNull(resolvedAnnotation3);
|
||||
Assert.assertEquals(resolvedAnnotation3.alias(), ANNOTATION3.value());
|
||||
Assert.assertEquals(resolvedAnnotation3.alias(), resolvedAnnotation3.value()); // value与alias互为别名
|
||||
|
||||
Assert.assertEquals(0, AnnotatedElementUtil.getAllDirectlyResolvedAnnotations(Foo.class, Annotation2.class).length);
|
||||
Assert.assertEquals(0, AnnotatedElementUtil.getAllDirectlyResolvedAnnotations(Foo.class, Annotation1.class).length);
|
||||
Assert.assertEquals(0, AnnotatedElementUtil.getAllDirectlyResolvedAnnotations(Foo.class, Annotation4.class).length);
|
||||
Assert.assertEquals(0, AnnotatedElementUtil.getAllDirectlyResolvedAnnotations(Foo.class, Annotation5.class).length);
|
||||
Assert.assertEquals(0, AnnotatedElementUtil.getAllDirectlyResolvedAnnotations(Foo.class, Annotation6.class).length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDirectlyResolvedAnnotations() {
|
||||
Annotation[] annotations = AnnotatedElementUtil.getDirectlyResolvedAnnotations(Foo.class);
|
||||
@ -364,6 +465,39 @@ public class AnnotatedElementUtilTest {
|
||||
Assert.assertEquals(ANNOTATION5, resolvedElement.getAnnotation(Annotation5.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToHierarchyRepeatableMetaElement() {
|
||||
Assert.assertNotNull(AnnotatedElementUtil.toHierarchyRepeatableMetaElement(null, false));
|
||||
Assert.assertNotNull(AnnotatedElementUtil.toHierarchyRepeatableMetaElement(null, true));
|
||||
AnnotatedElement element = AnnotatedElementUtil.toHierarchyRepeatableMetaElement(Foo.class, false);
|
||||
|
||||
// 带有元注解
|
||||
Assert.assertArrayEquals(ANNOTATIONS, element.getAnnotations());
|
||||
|
||||
// 不带元注解
|
||||
Assert.assertArrayEquals(DECLARED_ANNOTATIONS, element.getDeclaredAnnotations());
|
||||
|
||||
// 解析注解属性
|
||||
AnnotatedElement resolvedElement = AnnotatedElementUtil.toHierarchyRepeatableMetaElement(Foo.class, true);
|
||||
Annotation3 resolvedAnnotation3 = resolvedElement.getAnnotation(Annotation3.class);
|
||||
Assert.assertNotNull(resolvedAnnotation3);
|
||||
Assert.assertEquals(resolvedAnnotation3.alias(), ANNOTATION3.value());
|
||||
Assert.assertEquals(resolvedAnnotation3.alias(), resolvedAnnotation3.value()); // value与alias互为别名
|
||||
|
||||
Annotation2 resolvedAnnotation2 = resolvedElement.getAnnotation(Annotation2.class);
|
||||
Assert.assertNotNull(resolvedAnnotation2);
|
||||
Assert.assertEquals(resolvedAnnotation2.num(), ANNOTATION3.num()); // num属性被Annotation3.num覆盖
|
||||
|
||||
Annotation1 resolvedAnnotation1 = resolvedElement.getAnnotation(Annotation1.class);
|
||||
Assert.assertNotNull(resolvedAnnotation1);
|
||||
Assert.assertEquals(ANNOTATION3.value(), resolvedAnnotation1.value()); // value属性被Annotation3.value覆盖
|
||||
Assert.assertEquals(resolvedAnnotation1.value(), resolvedAnnotation1.alias()); // value与alias互为别名
|
||||
|
||||
Assert.assertEquals(ANNOTATION4, resolvedElement.getAnnotation(Annotation4.class));
|
||||
Assert.assertEquals(ANNOTATION6, resolvedElement.getAnnotation(Annotation6.class));
|
||||
Assert.assertEquals(ANNOTATION5, resolvedElement.getAnnotation(Annotation5.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToHierarchyElement() {
|
||||
Assert.assertNotNull(AnnotatedElementUtil.toHierarchyElement(Foo.class));
|
||||
@ -401,6 +535,36 @@ public class AnnotatedElementUtilTest {
|
||||
Assert.assertEquals(resolvedAnnotation1.value(), resolvedAnnotation1.alias()); // value与alias互为别名
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToRepeatableMetaElement() {
|
||||
Assert.assertNotNull(AnnotatedElementUtil.toRepeatableMetaElement(null, RepeatableAnnotationCollector.none(), false));
|
||||
Assert.assertNotNull(AnnotatedElementUtil.toRepeatableMetaElement(null, RepeatableAnnotationCollector.none(), true));
|
||||
|
||||
// 不解析注解属性
|
||||
AnnotatedElement element = AnnotatedElementUtil.toRepeatableMetaElement(Foo.class, RepeatableAnnotationCollector.none(), false);
|
||||
Assert.assertEquals(element, AnnotatedElementUtil.toRepeatableMetaElement(Foo.class, RepeatableAnnotationCollector.none(), false)); // 第二次获取时从缓存中获取
|
||||
Assert.assertArrayEquals(new Annotation[]{ANNOTATION3, ANNOTATION2, ANNOTATION1}, element.getAnnotations());
|
||||
|
||||
// 解析注解属性
|
||||
element = AnnotatedElementUtil.toRepeatableMetaElement(Foo.class, RepeatableAnnotationCollector.none(), true);
|
||||
Assert.assertEquals(element, AnnotatedElementUtil.toRepeatableMetaElement(Foo.class, RepeatableAnnotationCollector.none(), true)); // 第二次获取时从缓存中获取
|
||||
Assert.assertEquals(3, element.getAnnotations().length);
|
||||
|
||||
Annotation3 resolvedAnnotation3 = element.getAnnotation(Annotation3.class);
|
||||
Assert.assertNotNull(resolvedAnnotation3);
|
||||
Assert.assertEquals(resolvedAnnotation3.alias(), ANNOTATION3.value());
|
||||
Assert.assertEquals(resolvedAnnotation3.alias(), resolvedAnnotation3.value()); // value与alias互为别名
|
||||
|
||||
Annotation2 resolvedAnnotation2 = element.getAnnotation(Annotation2.class);
|
||||
Assert.assertNotNull(resolvedAnnotation2);
|
||||
Assert.assertEquals(resolvedAnnotation2.num(), ANNOTATION3.num()); // num属性被Annotation3.num覆盖
|
||||
|
||||
Annotation1 resolvedAnnotation1 = element.getAnnotation(Annotation1.class);
|
||||
Assert.assertNotNull(resolvedAnnotation1);
|
||||
Assert.assertEquals(ANNOTATION3.value(), resolvedAnnotation1.value()); // value属性被Annotation3.value覆盖
|
||||
Assert.assertEquals(resolvedAnnotation1.value(), resolvedAnnotation1.alias()); // value与alias互为别名
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsElement() {
|
||||
Annotation[] annotations = new Annotation[]{ANNOTATION1, ANNOTATION2};
|
||||
@ -433,6 +597,7 @@ public class AnnotatedElementUtilTest {
|
||||
|
||||
// ================= interface =================
|
||||
|
||||
@Annotation6
|
||||
@Target(ElementType.TYPE_USE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
private @interface Annotation5 {}
|
||||
@ -474,6 +639,22 @@ public class AnnotatedElementUtilTest {
|
||||
int num() default Integer.MIN_VALUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Foo.class上注解情况如下:
|
||||
* <pre>{@code
|
||||
* annotation3 => annotation2 => annotation1
|
||||
* }</pre>
|
||||
*
|
||||
* <p>Super.class上注解情况如下:
|
||||
* <pre>{@code
|
||||
* annotation4
|
||||
* }</pre>
|
||||
*
|
||||
* <p>Interface.class上注解情况如下:
|
||||
* <pre>{@code
|
||||
* annotation6 => annotation5 => annotation6【循环引用】
|
||||
* }</pre>
|
||||
*/
|
||||
@Annotation3(value = "foo", num = Integer.MAX_VALUE)
|
||||
private static class Foo extends Super implements Interface {}
|
||||
|
||||
|
@ -1,10 +1,12 @@
|
||||
package cn.hutool.core.annotation;
|
||||
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import lombok.SneakyThrows;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@ -12,6 +14,16 @@ import java.util.Map;
|
||||
*/
|
||||
public class AnnotationUtilTest {
|
||||
|
||||
@Test
|
||||
public void testGetDeclaredAnnotations() {
|
||||
Annotation[] annotations = AnnotationUtil.getDeclaredAnnotations(ClassForTest.class);
|
||||
Assert.assertArrayEquals(annotations, ClassForTest.class.getDeclaredAnnotations());
|
||||
Assert.assertSame(annotations, AnnotationUtil.getDeclaredAnnotations(ClassForTest.class));
|
||||
|
||||
AnnotationUtil.clearCaches();
|
||||
Assert.assertNotSame(annotations, AnnotationUtil.getDeclaredAnnotations(ClassForTest.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToCombination() {
|
||||
final CombinationAnnotationElement element = AnnotationUtil.toCombination(ClassForTest.class);
|
||||
@ -115,6 +127,21 @@ public class AnnotationUtilTest {
|
||||
Assert.assertEquals(MetaAnnotationForTest.class, annotation.annotationType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAnnotationAttributes() {
|
||||
Method[] methods = AnnotationUtil.getAnnotationAttributes(AnnotationForTest.class);
|
||||
Assert.assertArrayEquals(methods, AnnotationUtil.getAnnotationAttributes(AnnotationForTest.class));
|
||||
Assert.assertEquals(1, methods.length);
|
||||
Assert.assertArrayEquals(AnnotationForTest.class.getDeclaredMethods(), methods);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Test
|
||||
public void testIsAnnotationAttribute() {
|
||||
Assert.assertFalse(AnnotationUtil.isAnnotationAttribute(AnnotationForTest.class.getMethod("equals", Object.class)));
|
||||
Assert.assertTrue(AnnotationUtil.isAnnotationAttribute(AnnotationForTest.class.getMethod("value")));
|
||||
}
|
||||
|
||||
@Target(ElementType.TYPE_USE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
private @interface MetaAnnotationForTest{
|
||||
|
@ -111,6 +111,7 @@ public class MetaAnnotatedElementTest {
|
||||
new Annotation[]{ annotation4 },
|
||||
element.getAnnotationsByType(Annotation4.class)
|
||||
);
|
||||
Assert.assertEquals(0, element.getAnnotationsByType(None.class).length);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -121,6 +122,7 @@ public class MetaAnnotatedElementTest {
|
||||
new Annotation[]{ annotation4 },
|
||||
element.getDeclaredAnnotationsByType(Annotation4.class)
|
||||
);
|
||||
Assert.assertEquals(0, element.getDeclaredAnnotationsByType(None.class).length);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -168,6 +170,12 @@ public class MetaAnnotatedElementTest {
|
||||
Assert.assertSame(source, element.getElement());
|
||||
}
|
||||
|
||||
@Annotation4 // 循环引用
|
||||
@Target(ElementType.TYPE_USE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
private @interface None { }
|
||||
|
||||
@Annotation4 // 循环引用
|
||||
@Target(ElementType.TYPE_USE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
private @interface Annotation1 {
|
||||
|
@ -0,0 +1,220 @@
|
||||
package cn.hutool.core.annotation;
|
||||
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* test for {@link RepeatableAnnotationCollector}
|
||||
*
|
||||
* @author huangchengxing
|
||||
*/
|
||||
public class RepeatableAnnotationCollectorTest {
|
||||
|
||||
private static final Annotation1 ANNOTATION1 = Foo.class.getAnnotation(Annotation1.class);
|
||||
private static final List<Annotation2> ANNOTATION2S = Arrays.asList(ANNOTATION1.value());
|
||||
private static final List<Annotation3> ANNOTATION3S = ANNOTATION2S.stream()
|
||||
.map(Annotation2::value)
|
||||
.flatMap(Stream::of)
|
||||
.collect(Collectors.toList());
|
||||
private static final List<Annotation> ANNOTATIONS = new ArrayList<>();
|
||||
static {
|
||||
ANNOTATIONS.add(ANNOTATION1);
|
||||
ANNOTATIONS.addAll(ANNOTATION2S);
|
||||
ANNOTATIONS.addAll(ANNOTATION3S);
|
||||
}
|
||||
|
||||
private static final BiPredicate<Annotation, Method> PREDICATE = (annotation, attribute) -> {
|
||||
// 属性名需为“value”
|
||||
if (!CharSequenceUtil.equals("value", attribute.getName())) {
|
||||
return false;
|
||||
}
|
||||
final Class<?> attributeType = attribute.getReturnType();
|
||||
// 返回值类型需为数组
|
||||
return attributeType.isArray()
|
||||
// 且数组元素需为注解
|
||||
&& attributeType.getComponentType()
|
||||
.isAnnotation()
|
||||
// 该注解类必须被@Repeatable注解,但不要求与当前属性的声明方法一致
|
||||
&& attributeType.getComponentType()
|
||||
.isAnnotationPresent(Repeatable.class);
|
||||
};
|
||||
|
||||
@Test
|
||||
public void testNone() {
|
||||
RepeatableAnnotationCollector collector = RepeatableAnnotationCollector.none();
|
||||
Assert.assertSame(collector, RepeatableAnnotationCollector.none());
|
||||
|
||||
Assert.assertEquals(0, collector.getFinalRepeatableAnnotations(null).size());
|
||||
|
||||
Annotation1 annotation = Foo.class.getAnnotation(Annotation1.class);
|
||||
Assert.assertEquals(Collections.singletonList(annotation), collector.getFinalRepeatableAnnotations(annotation));
|
||||
|
||||
Annotation3 annotation3 = Foo.class.getAnnotation(Annotation3.class);
|
||||
Assert.assertEquals(Collections.singletonList(annotation3), collector.getFinalRepeatableAnnotations(annotation3));
|
||||
|
||||
Assert.assertEquals(Collections.singletonList(annotation3), collector.getRepeatableAnnotations(annotation3, Annotation3.class));
|
||||
Assert.assertTrue(collector.getRepeatableAnnotations(annotation3, Annotation1.class).isEmpty());
|
||||
Assert.assertTrue(collector.getRepeatableAnnotations(null, Annotation1.class).isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoneWhenAccumulate() {
|
||||
RepeatableAnnotationCollector collector = RepeatableAnnotationCollector.none();
|
||||
Assert.assertSame(collector, RepeatableAnnotationCollector.none());
|
||||
|
||||
Assert.assertEquals(0, collector.getAllRepeatableAnnotations(null).size());
|
||||
|
||||
Annotation1 annotation = Foo.class.getAnnotation(Annotation1.class);
|
||||
Assert.assertEquals(Collections.singletonList(annotation), collector.getAllRepeatableAnnotations(annotation));
|
||||
|
||||
Annotation3 annotation3 = Foo.class.getAnnotation(Annotation3.class);
|
||||
Assert.assertEquals(Collections.singletonList(annotation3), collector.getAllRepeatableAnnotations(annotation3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenericCollector() {
|
||||
RepeatableAnnotationCollector collector = RepeatableAnnotationCollector.standard();
|
||||
Assert.assertSame(collector, RepeatableAnnotationCollector.standard());
|
||||
|
||||
Annotation1 annotation = Foo.class.getAnnotation(Annotation1.class);
|
||||
List<Annotation3> annotations = Stream.of(annotation.value())
|
||||
.map(Annotation2::value)
|
||||
.flatMap(Stream::of)
|
||||
.collect(Collectors.toList());
|
||||
Assert.assertEquals(annotations, collector.getFinalRepeatableAnnotations(annotation));
|
||||
Assert.assertEquals(ANNOTATION3S, collector.getFinalRepeatableAnnotations(ANNOTATION1));
|
||||
|
||||
Annotation3 annotation3 = Foo.class.getAnnotation(Annotation3.class);
|
||||
Assert.assertEquals(Collections.singletonList(annotation3), collector.getFinalRepeatableAnnotations(annotation3));
|
||||
|
||||
Assert.assertEquals(Collections.singletonList(ANNOTATION1), collector.getRepeatableAnnotations(ANNOTATION1, Annotation1.class));
|
||||
Assert.assertEquals(ANNOTATION2S, collector.getRepeatableAnnotations(ANNOTATION1, Annotation2.class));
|
||||
Assert.assertEquals(ANNOTATION3S, collector.getRepeatableAnnotations(ANNOTATION1, Annotation3.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenericCollectorWhenAccumulate() {
|
||||
RepeatableAnnotationCollector collector = RepeatableAnnotationCollector.standard();
|
||||
Assert.assertSame(collector, RepeatableAnnotationCollector.standard());
|
||||
|
||||
List<Annotation> annotations = new ArrayList<>();
|
||||
Annotation1 annotation = Foo.class.getAnnotation(Annotation1.class);
|
||||
annotations.add(annotation);
|
||||
annotations.addAll(Arrays.asList(annotation.value()));
|
||||
Stream.of(annotation.value())
|
||||
.map(Annotation2::value)
|
||||
.flatMap(Stream::of)
|
||||
.forEach(annotations::add);
|
||||
Assert.assertEquals(annotations, collector.getAllRepeatableAnnotations(annotation));
|
||||
|
||||
Assert.assertEquals(ANNOTATIONS, collector.getAllRepeatableAnnotations(ANNOTATION1));
|
||||
|
||||
Annotation3 annotation3 = Foo.class.getAnnotation(Annotation3.class);
|
||||
Assert.assertEquals(Collections.singletonList(annotation3), collector.getAllRepeatableAnnotations(annotation3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConditionCollector() {
|
||||
RepeatableAnnotationCollector collector = RepeatableAnnotationCollector.condition(PREDICATE);
|
||||
Annotation1 annotation = Foo.class.getAnnotation(Annotation1.class);
|
||||
List<Annotation3> annotations = Stream.of(annotation.value())
|
||||
.map(Annotation2::value)
|
||||
.flatMap(Stream::of)
|
||||
.collect(Collectors.toList());
|
||||
Assert.assertEquals(annotations, collector.getFinalRepeatableAnnotations(annotation));
|
||||
|
||||
Assert.assertEquals(ANNOTATION3S, collector.getFinalRepeatableAnnotations(ANNOTATION1));
|
||||
|
||||
Annotation3 annotation3 = Foo.class.getAnnotation(Annotation3.class);
|
||||
Assert.assertEquals(Collections.singletonList(annotation3), collector.getFinalRepeatableAnnotations(annotation3));
|
||||
|
||||
Assert.assertEquals(Collections.singletonList(ANNOTATION1), collector.getRepeatableAnnotations(ANNOTATION1, Annotation1.class));
|
||||
Assert.assertEquals(ANNOTATION2S, collector.getRepeatableAnnotations(ANNOTATION1, Annotation2.class));
|
||||
Assert.assertEquals(ANNOTATION3S, collector.getRepeatableAnnotations(ANNOTATION1, Annotation3.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConditionCollectorWhenAccumulate() {
|
||||
RepeatableAnnotationCollector collector = RepeatableAnnotationCollector.condition(PREDICATE);
|
||||
|
||||
List<Annotation> annotations = new ArrayList<>();
|
||||
Annotation1 annotation = Foo.class.getAnnotation(Annotation1.class);
|
||||
annotations.add(annotation);
|
||||
annotations.addAll(Arrays.asList(annotation.value()));
|
||||
Stream.of(annotation.value())
|
||||
.map(Annotation2::value)
|
||||
.flatMap(Stream::of)
|
||||
.forEach(annotations::add);
|
||||
Assert.assertEquals(annotations, collector.getAllRepeatableAnnotations(annotation));
|
||||
Assert.assertEquals(ANNOTATIONS, collector.getAllRepeatableAnnotations(ANNOTATION1));
|
||||
|
||||
Annotation3 annotation3 = Foo.class.getAnnotation(Annotation3.class);
|
||||
Assert.assertEquals(Collections.singletonList(annotation3), collector.getAllRepeatableAnnotations((annotation3)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFullCollector() {
|
||||
RepeatableAnnotationCollector collector = RepeatableAnnotationCollector.full();
|
||||
Assert.assertSame(collector, RepeatableAnnotationCollector.full());
|
||||
|
||||
Assert.assertEquals(ANNOTATION3S, collector.getFinalRepeatableAnnotations(ANNOTATION1));
|
||||
|
||||
Annotation3 annotation3 = Foo.class.getAnnotation(Annotation3.class);
|
||||
Assert.assertEquals(Collections.singletonList(annotation3), collector.getFinalRepeatableAnnotations(annotation3));
|
||||
|
||||
Assert.assertEquals(Collections.singletonList(ANNOTATION1), collector.getRepeatableAnnotations(ANNOTATION1, Annotation1.class));
|
||||
Assert.assertEquals(ANNOTATION2S, collector.getRepeatableAnnotations(ANNOTATION1, Annotation2.class));
|
||||
Assert.assertEquals(ANNOTATION3S, collector.getRepeatableAnnotations(ANNOTATION1, Annotation3.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFullCollectorWhenAccumulate() {
|
||||
RepeatableAnnotationCollector collector = RepeatableAnnotationCollector.full();
|
||||
Assert.assertSame(collector, RepeatableAnnotationCollector.full());
|
||||
|
||||
Assert.assertEquals(ANNOTATIONS, collector.getAllRepeatableAnnotations(ANNOTATION1));
|
||||
|
||||
Annotation3 annotation3 = Foo.class.getAnnotation(Annotation3.class);
|
||||
Assert.assertEquals(Collections.singletonList(annotation3), collector.getAllRepeatableAnnotations(annotation3));
|
||||
}
|
||||
|
||||
@Target(ElementType.TYPE_USE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
private @interface Annotation1 {
|
||||
Annotation2[] value() default {};
|
||||
}
|
||||
|
||||
@Repeatable(Annotation1.class)
|
||||
@Target(ElementType.TYPE_USE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
private @interface Annotation2 {
|
||||
Annotation3[] value() default {};
|
||||
}
|
||||
|
||||
@Repeatable(Annotation2.class)
|
||||
@Target(ElementType.TYPE_USE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
private @interface Annotation3 {
|
||||
int value();
|
||||
String name() default "";
|
||||
}
|
||||
|
||||
@Annotation3(Integer.MIN_VALUE)
|
||||
@Annotation1({
|
||||
@Annotation2({@Annotation3(1), @Annotation3(2)}),
|
||||
@Annotation2({@Annotation3(3), @Annotation3(4)})
|
||||
})
|
||||
private static class Foo {}
|
||||
|
||||
}
|
@ -0,0 +1,213 @@
|
||||
package cn.hutool.core.annotation;
|
||||
|
||||
import cn.hutool.core.collection.iter.IterUtil;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* test for {@link RepeatableMetaAnnotatedElement}
|
||||
*
|
||||
* @author huangchengxing
|
||||
*/
|
||||
public class RepeatableMetaAnnotatedElementTest {
|
||||
|
||||
private static final BiFunction<ResolvedAnnotationMapping, Annotation, ResolvedAnnotationMapping> RESOLVED_MAPPING_FACTORY =
|
||||
(source, annotation) -> new ResolvedAnnotationMapping(source, annotation, true);
|
||||
|
||||
private static final BiFunction<ResolvedAnnotationMapping, Annotation, ResolvedAnnotationMapping> MAPPING_FACTORY =
|
||||
(source, annotation) -> new ResolvedAnnotationMapping(source, annotation, false);
|
||||
|
||||
@Test
|
||||
public void testEquals() {
|
||||
AnnotatedElement element = RepeatableMetaAnnotatedElement.create(Foo.class, RESOLVED_MAPPING_FACTORY);
|
||||
Assert.assertEquals(element, element);
|
||||
Assert.assertNotEquals(element, null);
|
||||
Assert.assertEquals(element, RepeatableMetaAnnotatedElement.create(Foo.class, RESOLVED_MAPPING_FACTORY));
|
||||
Assert.assertNotEquals(element, RepeatableMetaAnnotatedElement.create(Foo.class, MAPPING_FACTORY));
|
||||
Assert.assertNotEquals(element, RepeatableMetaAnnotatedElement.create(Annotation1.class, MAPPING_FACTORY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHashCode() {
|
||||
int hashCode = RepeatableMetaAnnotatedElement.create(Foo.class, RESOLVED_MAPPING_FACTORY).hashCode();
|
||||
Assert.assertEquals(hashCode, RepeatableMetaAnnotatedElement.create(Foo.class, RESOLVED_MAPPING_FACTORY).hashCode());
|
||||
Assert.assertNotEquals(hashCode, RepeatableMetaAnnotatedElement.create(Foo.class, MAPPING_FACTORY).hashCode());
|
||||
Assert.assertNotEquals(hashCode, RepeatableMetaAnnotatedElement.create(Annotation1.class, MAPPING_FACTORY).hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsAnnotationPresent() {
|
||||
AnnotatedElement element = RepeatableMetaAnnotatedElement.create(
|
||||
RepeatableAnnotationCollector.standard(), Foo.class, (s, a) -> new GenericAnnotationMapping(a, Objects.isNull(s))
|
||||
);
|
||||
Assert.assertTrue(element.isAnnotationPresent(Annotation1.class));
|
||||
Assert.assertTrue(element.isAnnotationPresent(Annotation2.class));
|
||||
Assert.assertTrue(element.isAnnotationPresent(Annotation3.class));
|
||||
Assert.assertTrue(element.isAnnotationPresent(Annotation4.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAnnotations() {
|
||||
AnnotatedElement element = RepeatableMetaAnnotatedElement.create(
|
||||
RepeatableAnnotationCollector.standard(), Foo.class, (s, a) -> new GenericAnnotationMapping(a, Objects.isNull(s))
|
||||
);
|
||||
List<Class<? extends Annotation>> annotationTypes = Arrays.stream(element.getAnnotations())
|
||||
.map(Annotation::annotationType)
|
||||
.collect(Collectors.toList());
|
||||
Map<Class<? extends Annotation>, Integer> countMap = IterUtil.countMap(annotationTypes.iterator());
|
||||
|
||||
Assert.assertEquals((Integer)1, countMap.get(Annotation1.class));
|
||||
Assert.assertEquals((Integer)2, countMap.get(Annotation2.class));
|
||||
Assert.assertEquals((Integer)4, countMap.get(Annotation3.class));
|
||||
Assert.assertEquals((Integer)7, countMap.get(Annotation4.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAnnotation() {
|
||||
AnnotatedElement element = RepeatableMetaAnnotatedElement.create(
|
||||
RepeatableAnnotationCollector.standard(), Foo.class, (s, a) -> new GenericAnnotationMapping(a, Objects.isNull(s))
|
||||
);
|
||||
|
||||
Annotation1 annotation1 = Foo.class.getAnnotation(Annotation1.class);
|
||||
Assert.assertEquals(annotation1, element.getAnnotation(Annotation1.class));
|
||||
|
||||
Annotation4 annotation4 = Annotation1.class.getAnnotation(Annotation4.class);
|
||||
Assert.assertEquals(annotation4, element.getAnnotation(Annotation4.class));
|
||||
|
||||
Annotation2 annotation2 = annotation1.value()[0];
|
||||
Assert.assertEquals(annotation2, element.getAnnotation(Annotation2.class));
|
||||
|
||||
Annotation3 annotation3 = annotation2.value()[0];
|
||||
Assert.assertEquals(annotation3, element.getAnnotation(Annotation3.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAnnotationsByType() {
|
||||
AnnotatedElement element = RepeatableMetaAnnotatedElement.create(
|
||||
RepeatableAnnotationCollector.standard(), Foo.class, (s, a) -> new GenericAnnotationMapping(a, Objects.isNull(s))
|
||||
);
|
||||
|
||||
Annotation[] annotations = element.getAnnotationsByType(Annotation1.class);
|
||||
Assert.assertEquals(1, annotations.length);
|
||||
|
||||
annotations = element.getAnnotationsByType(Annotation2.class);
|
||||
Assert.assertEquals(2, annotations.length);
|
||||
|
||||
annotations = element.getAnnotationsByType(Annotation3.class);
|
||||
Assert.assertEquals(4, annotations.length);
|
||||
|
||||
annotations = element.getAnnotationsByType(Annotation4.class);
|
||||
Assert.assertEquals(7, annotations.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDeclaredAnnotations() {
|
||||
AnnotatedElement element = RepeatableMetaAnnotatedElement.create(
|
||||
RepeatableAnnotationCollector.standard(), Foo.class, (s, a) -> new GenericAnnotationMapping(a, Objects.isNull(s))
|
||||
);
|
||||
List<Class<? extends Annotation>> annotationTypes = Arrays.stream(element.getDeclaredAnnotations())
|
||||
.map(Annotation::annotationType)
|
||||
.collect(Collectors.toList());
|
||||
Map<Class<? extends Annotation>, Integer> countMap = IterUtil.countMap(annotationTypes.iterator());
|
||||
|
||||
Assert.assertEquals((Integer)1, countMap.get(Annotation1.class));
|
||||
Assert.assertFalse(countMap.containsKey(Annotation2.class));
|
||||
Assert.assertFalse(countMap.containsKey(Annotation3.class));
|
||||
Assert.assertFalse(countMap.containsKey(Annotation4.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDeclaredAnnotation() {
|
||||
AnnotatedElement element = RepeatableMetaAnnotatedElement.create(
|
||||
RepeatableAnnotationCollector.standard(), Foo.class, (s, a) -> new GenericAnnotationMapping(a, Objects.isNull(s))
|
||||
);
|
||||
|
||||
Annotation1 annotation1 = Foo.class.getDeclaredAnnotation(Annotation1.class);
|
||||
Assert.assertEquals(annotation1, element.getDeclaredAnnotation(Annotation1.class));
|
||||
Assert.assertNull(element.getDeclaredAnnotation(Annotation3.class));
|
||||
Assert.assertNull(element.getDeclaredAnnotation(Annotation4.class));
|
||||
Assert.assertNull(element.getDeclaredAnnotation(Annotation2.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDeclaredAnnotationsByType() {
|
||||
AnnotatedElement element = RepeatableMetaAnnotatedElement.create(
|
||||
RepeatableAnnotationCollector.standard(), Foo.class, (s, a) -> new GenericAnnotationMapping(a, Objects.isNull(s))
|
||||
);
|
||||
|
||||
Annotation[] annotations = element.getDeclaredAnnotationsByType(Annotation1.class);
|
||||
Assert.assertEquals(1, annotations.length);
|
||||
|
||||
annotations = element.getDeclaredAnnotationsByType(Annotation2.class);
|
||||
Assert.assertEquals(0, annotations.length);
|
||||
|
||||
annotations = element.getDeclaredAnnotationsByType(Annotation3.class);
|
||||
Assert.assertEquals(0, annotations.length);
|
||||
|
||||
annotations = element.getDeclaredAnnotationsByType(Annotation4.class);
|
||||
Assert.assertEquals(0, annotations.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetElement() {
|
||||
AnnotatedElement element = Foo.class;
|
||||
RepeatableMetaAnnotatedElement<GenericAnnotationMapping> repeatableMetaAnnotatedElement = RepeatableMetaAnnotatedElement.create(
|
||||
RepeatableAnnotationCollector.standard(), element, (s, a) -> new GenericAnnotationMapping(a, Objects.isNull(s))
|
||||
);
|
||||
Assert.assertSame(element, repeatableMetaAnnotatedElement.getElement());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIterator() {
|
||||
RepeatableMetaAnnotatedElement<GenericAnnotationMapping> element = RepeatableMetaAnnotatedElement.create(
|
||||
RepeatableAnnotationCollector.standard(), Foo.class, (s, a) -> new GenericAnnotationMapping(a, Objects.isNull(s))
|
||||
);
|
||||
int count = 0;
|
||||
for (GenericAnnotationMapping mapping : element) {
|
||||
count++;
|
||||
}
|
||||
Assert.assertEquals(14, count);
|
||||
}
|
||||
|
||||
@Annotation4
|
||||
@Target(ElementType.TYPE_USE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
private @interface Annotation1 {
|
||||
Annotation2[] value() default {};
|
||||
}
|
||||
|
||||
@Annotation4
|
||||
@Repeatable(Annotation1.class)
|
||||
@Target(ElementType.TYPE_USE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
private @interface Annotation2 {
|
||||
Annotation3[] value() default {};
|
||||
}
|
||||
|
||||
@Annotation4
|
||||
@Repeatable(Annotation2.class)
|
||||
@Target(ElementType.TYPE_USE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
private @interface Annotation3 { }
|
||||
|
||||
@Annotation4 // 循环引用
|
||||
@Target(ElementType.TYPE_USE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
private @interface Annotation4 { }
|
||||
|
||||
@Annotation1({
|
||||
@Annotation2({@Annotation3, @Annotation3}),
|
||||
@Annotation2({@Annotation3, @Annotation3})
|
||||
})
|
||||
private static class Foo {}
|
||||
|
||||
}
|
@ -81,6 +81,18 @@ public class MethodUtilTest {
|
||||
Assert.assertEquals(10, testClass.getA());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDeclaredMethodsTest() {
|
||||
Class<?> type = ReflectUtilTest.TestBenchClass.class;
|
||||
Method[] methods = type.getDeclaredMethods();
|
||||
Assert.assertArrayEquals(methods, MethodUtil.getDeclaredMethods(type));
|
||||
Assert.assertSame(MethodUtil.getDeclaredMethods(type), MethodUtil.getDeclaredMethods(type));
|
||||
|
||||
type = Object.class;
|
||||
methods = type.getDeclaredMethods();
|
||||
Assert.assertArrayEquals(methods, MethodUtil.getDeclaredMethods(type));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void getMethodBenchTest() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user