!812 【6.x】AnnotatedElementUtil支持处理可重复注解

Merge pull request !812 from Createsequence/repeatable-annotation
This commit is contained in:
Looly 2022-09-22 09:00:21 +00:00 committed by Gitee
commit 6c6b339520
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
15 changed files with 1861 additions and 73 deletions

View File

@ -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}
*/

View File

@ -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();
}
}

View File

@ -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));
}
}
}

View File

@ -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));
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;

View File

@ -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>
* 接口获取方法和默认方法获取的方法包括

View File

@ -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 {}

View File

@ -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{

View File

@ -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 {

View File

@ -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 {}
}

View File

@ -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 {}
}

View File

@ -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() {