Merge branch 'v5-dev' of https://gitee.com/hellozrh/hutool into v5-dev

This commit is contained in:
zhangrenhua
2022-07-19 10:20:47 +08:00
110 changed files with 5174 additions and 1112 deletions

View File

@@ -0,0 +1,168 @@
package cn.hutool.core.annotation;
import cn.hutool.core.annotation.scanner.AnnotationScanner;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* {@link AnnotationSynthesizer}的基本实现
*
* @author huangchengxing
*/
public abstract class AbstractAnnotationSynthesizer<T> implements AnnotationSynthesizer {
/**
* 合成注解来源最初来源
*/
protected final T source;
/**
* 包含根注解以及其元注解在内的全部注解实例
*/
protected final Map<Class<? extends Annotation>, SynthesizedAnnotation> synthesizedAnnotationMap;
/**
* 已经合成过的注解对象
*/
private final Map<Class<? extends Annotation>, Annotation> synthesizedProxyAnnotations;
/**
* 合成注解选择器
*/
protected final SynthesizedAnnotationSelector annotationSelector;
/**
* 合成注解属性处理器
*/
protected final Collection<SynthesizedAnnotationPostProcessor> postProcessors;
/**
* 注解扫描器
*/
protected final AnnotationScanner annotationScanner;
/**
* 构造一个注解合成器
*
* @param source 当前查找的注解对象
* @param annotationSelector 合成注解选择器
* @param annotationPostProcessors 注解后置处理器
* @param annotationScanner 注解扫描器,该扫描器需要支持扫描注解类
*/
protected AbstractAnnotationSynthesizer(
T source,
SynthesizedAnnotationSelector annotationSelector,
Collection<SynthesizedAnnotationPostProcessor> annotationPostProcessors,
AnnotationScanner annotationScanner) {
Assert.notNull(source, "source must not null");
Assert.notNull(annotationSelector, "annotationSelector must not null");
Assert.notNull(annotationPostProcessors, "annotationPostProcessors must not null");
Assert.notNull(annotationPostProcessors, "annotationScanner must not null");
this.source = source;
this.annotationSelector = annotationSelector;
this.annotationScanner = annotationScanner;
this.postProcessors = CollUtil.unmodifiable(
CollUtil.sort(annotationPostProcessors, Comparator.comparing(SynthesizedAnnotationPostProcessor::order))
);
this.synthesizedProxyAnnotations = new LinkedHashMap<>();
this.synthesizedAnnotationMap = MapUtil.unmodifiable(loadAnnotations());
annotationPostProcessors.forEach(processor ->
synthesizedAnnotationMap.values().forEach(synthesized -> processor.process(synthesized, this))
);
}
/**
* 加载合成注解的必要属性
*
* @return 合成注解
*/
protected abstract Map<Class<? extends Annotation>, SynthesizedAnnotation> loadAnnotations();
/**
* 根据指定的注解类型和对应注解对象,合成最终所需的合成注解
*
* @param annotationType 注解类型
* @param annotation 合成注解对象
* @param <A> 注解类型
* @return 最终所需的合成注解
*/
protected abstract <A extends Annotation> A synthesize(Class<A> annotationType, SynthesizedAnnotation annotation);
/**
* 获取合成注解来源最初来源
*
* @return 合成注解来源最初来源
*/
@Override
public T getSource() {
return source;
}
/**
* 合成注解选择器
*
* @return 注解选择器
*/
@Override
public SynthesizedAnnotationSelector getAnnotationSelector() {
return annotationSelector;
}
/**
* 获取合成注解后置处理器
*
* @return 合成注解后置处理器
*/
@Override
public Collection<SynthesizedAnnotationPostProcessor> getAnnotationPostProcessors() {
return postProcessors;
}
/**
* 获取已合成的注解
*
* @param annotationType 注解类型
* @return 已合成的注解
*/
@Override
public SynthesizedAnnotation getSynthesizedAnnotation(Class<?> annotationType) {
return synthesizedAnnotationMap.get(annotationType);
}
/**
* 获取全部的合成注解
*
* @return 合成注解
*/
@Override
public Map<Class<? extends Annotation>, SynthesizedAnnotation> getAllSynthesizedAnnotation() {
return synthesizedAnnotationMap;
}
/**
* 获取合成注解
*
* @param annotationType 注解类型
* @param <A> 注解类型
* @return 类型
*/
@SuppressWarnings("unchecked")
@Override
public <A extends Annotation> A synthesize(Class<A> annotationType) {
return (A)synthesizedProxyAnnotations.computeIfAbsent(annotationType, type -> {
final SynthesizedAnnotation synthesizedAnnotation = synthesizedAnnotationMap.get(annotationType);
return ObjectUtil.isNull(synthesizedAnnotation) ?
null : synthesize(annotationType, synthesizedAnnotation);
});
}
}

View File

@@ -0,0 +1,163 @@
package cn.hutool.core.annotation;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.Map;
/**
* {@link SynthesizedAnnotationPostProcessor}的基本实现,
* 用于处理注解中带有{@link Link}注解的属性。
*
* @author huangchengxing
* @see MirrorLinkAnnotationPostProcessor
* @see AliasLinkAnnotationPostProcessor
*/
public abstract class AbstractLinkAnnotationPostProcessor implements SynthesizedAnnotationPostProcessor {
/**
* 若一个注解属性上存在{@link Link}注解,注解的{@link Link#type()}返回值在{@link #processTypes()}中存在,
* 且此{@link Link}指定的注解对象在当前的{@link SynthesizedAggregateAnnotation}中存在,
* 则从聚合器中获取类型对应的合成注解对象,与该对象中的指定属性,然后将全部关联数据交给
* {@link #processLinkedAttribute}处理。
*
* @param synthesizedAnnotation 合成的注解
* @param synthesizer 合成注解聚合器
*/
@Override
public void process(SynthesizedAnnotation synthesizedAnnotation, AnnotationSynthesizer synthesizer) {
final Map<String, AnnotationAttribute> attributeMap = new HashMap<>(synthesizedAnnotation.getAttributes());
attributeMap.forEach((originalAttributeName, originalAttribute) -> {
// 获取注解
final Link link = getLinkAnnotation(originalAttribute, processTypes());
if (ObjectUtil.isNull(link)) {
return;
}
// 获取注解属性
final SynthesizedAnnotation linkedAnnotation = getLinkedAnnotation(link, synthesizer, synthesizedAnnotation.annotationType());
if (ObjectUtil.isNull(linkedAnnotation)) {
return;
}
final AnnotationAttribute linkedAttribute = linkedAnnotation.getAttributes().get(link.attribute());
// 处理
processLinkedAttribute(
synthesizer, link,
synthesizedAnnotation, synthesizedAnnotation.getAttributes().get(originalAttributeName),
linkedAnnotation, linkedAttribute
);
});
}
// =========================== 抽象方法 ===========================
/**
* 当属性上存在{@link Link}注解时,仅当{@link Link#type()}在本方法返回值内存在时才进行处理
*
* @return 支持处理的{@link RelationType}类型
*/
protected abstract RelationType[] processTypes();
/**
* 对关联的合成注解对象及其关联属性的处理
*
* @param synthesizer 注解合成器
* @param annotation {@code originalAttribute}上的{@link Link}注解对象
* @param originalAnnotation 当前正在处理的{@link SynthesizedAnnotation}对象
* @param originalAttribute {@code originalAnnotation}上的待处理的属性
* @param linkedAnnotation {@link Link}指向的关联注解对象
* @param linkedAttribute {@link Link}指向的{@code originalAnnotation}中的关联属性,该参数可能为空
*/
protected abstract void processLinkedAttribute(
AnnotationSynthesizer synthesizer, Link annotation,
SynthesizedAnnotation originalAnnotation, AnnotationAttribute originalAttribute,
SynthesizedAnnotation linkedAnnotation, AnnotationAttribute linkedAttribute
);
// =========================== @Link注解的处理 ===========================
/**
* 从注解属性上获取指定类型的{@link Link}注解
*
* @param attribute 注解属性
* @param relationTypes 类型
* @return 注解
*/
protected Link getLinkAnnotation(AnnotationAttribute attribute, RelationType... relationTypes) {
return Opt.ofNullable(attribute)
.map(t -> AnnotationUtil.getSynthesizedAnnotation(attribute.getAttribute(), Link.class))
.filter(a -> ArrayUtil.contains(relationTypes, a.type()))
.get();
}
/**
* 从合成注解中获取{@link Link#type()}指定的注解对象
*
* @param annotation {@link Link}注解
* @param synthesizer 注解合成器
* @param defaultType 默认类型
* @return {@link SynthesizedAnnotation}
*/
protected SynthesizedAnnotation getLinkedAnnotation(Link annotation, AnnotationSynthesizer synthesizer, Class<? extends Annotation> defaultType) {
final Class<?> targetAnnotationType = getLinkedAnnotationType(annotation, defaultType);
return synthesizer.getSynthesizedAnnotation(targetAnnotationType);
}
/**
* 若{@link Link#annotation()}获取的类型{@code Annotation#getClass()},则返回{@code defaultType}
* 否则返回{@link Link#annotation()}指定的类型
*
* @param annotation {@link Link}注解
* @param defaultType 默认注解类型
* @return 注解类型
*/
protected Class<?> getLinkedAnnotationType(Link annotation, Class<?> defaultType) {
return ObjectUtil.equals(annotation.annotation(), Annotation.class) ?
defaultType : annotation.annotation();
}
// =========================== 注解属性的校验 ===========================
/**
* 校验两个注解属性的返回值类型是否一致
*
* @param original 原属性
* @param alias 别名属性
*/
protected void checkAttributeType(AnnotationAttribute original, AnnotationAttribute alias) {
Assert.equals(
original.getAttributeType(), alias.getAttributeType(),
"return type of the linked attribute [{}] is inconsistent with the original [{}]",
original.getAttribute(), alias.getAttribute()
);
}
/**
* 检查{@link Link}指向的注解属性是否就是本身
*
* @param original {@link Link}注解的属性
* @param linked {@link Link}指向的注解属性
*/
protected void checkLinkedSelf(AnnotationAttribute original, AnnotationAttribute linked) {
boolean linkSelf = (original == linked) || ObjectUtil.equals(original.getAttribute(), linked.getAttribute());
Assert.isFalse(linkSelf, "cannot link self [{}]", original.getAttribute());
}
/**
* 检查{@link Link}指向的注解属性是否存在
*
* @param original {@link Link}注解的属性
* @param linkedAttribute {@link Link}指向的注解属性
* @param annotation {@link Link}注解
*/
protected void checkLinkedAttributeNotNull(AnnotationAttribute original, AnnotationAttribute linkedAttribute, Link annotation) {
Assert.notNull(linkedAttribute, "cannot find linked attribute [{}] of original [{}] in [{}]",
original.getAttribute(), annotation.attribute(),
getLinkedAnnotationType(annotation, original.getAnnotationType())
);
}
}

View File

@@ -0,0 +1,71 @@
package cn.hutool.core.annotation;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* {@link WrappedAnnotationAttribute}的基本实现
*
* @author huangchengxing
* @see ForceAliasedAnnotationAttribute
* @see AliasedAnnotationAttribute
* @see MirroredAnnotationAttribute
*/
public abstract class AbstractWrappedAnnotationAttribute implements WrappedAnnotationAttribute {
protected final AnnotationAttribute original;
protected final AnnotationAttribute linked;
protected AbstractWrappedAnnotationAttribute(AnnotationAttribute original, AnnotationAttribute linked) {
Assert.notNull(original, "target must not null");
Assert.notNull(linked, "linked must not null");
this.original = original;
this.linked = linked;
}
@Override
public AnnotationAttribute getOriginal() {
return original;
}
@Override
public AnnotationAttribute getLinked() {
return linked;
}
@Override
public AnnotationAttribute getNonWrappedOriginal() {
AnnotationAttribute curr = null;
AnnotationAttribute next = original;
while (next != null) {
curr = next;
next = next.isWrapped() ? ((WrappedAnnotationAttribute)curr).getOriginal() : null;
}
return curr;
}
@Override
public Collection<AnnotationAttribute> getAllLinkedNonWrappedAttributes() {
List<AnnotationAttribute> leafAttributes = new ArrayList<>();
collectLeafAttribute(this, leafAttributes);
return leafAttributes;
}
private void collectLeafAttribute(AnnotationAttribute curr, List<AnnotationAttribute> leafAttributes) {
if (ObjectUtil.isNull(curr)) {
return;
}
if (!curr.isWrapped()) {
leafAttributes.add(curr);
return;
}
WrappedAnnotationAttribute wrappedAttribute = (WrappedAnnotationAttribute)curr;
collectLeafAttribute(wrappedAttribute.getOriginal(), leafAttributes);
collectLeafAttribute(wrappedAttribute.getLinked(), leafAttributes);
}
}

View File

@@ -0,0 +1,27 @@
package cn.hutool.core.annotation;
import java.lang.annotation.Annotation;
/**
* 表示一组被聚合在一起的注解对象
*
* @author huangchengxing
*/
public interface AggregateAnnotation extends Annotation {
/**
* 在聚合中是否存在的指定类型注解对象
*
* @param annotationType 注解类型
* @return 是否
*/
boolean isAnnotationPresent(Class<? extends Annotation> annotationType);
/**
* 获取聚合中的全部注解对象
*
* @return 注解对象
*/
Annotation[] getAnnotations();
}

View File

@@ -0,0 +1,66 @@
package cn.hutool.core.annotation;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.map.ForestMap;
import cn.hutool.core.map.LinkedForestMap;
import cn.hutool.core.map.TreeEntry;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjectUtil;
import java.util.Map;
/**
* <p>用于处理注解对象中带有{@link Alias}注解的属性。<br>
* 当该处理器执行完毕后,{@link Alias}注解指向的目标注解的属性将会被包装并替换为
* {@link ForceAliasedAnnotationAttribute}。
*
* @author huangchengxing
* @see Alias
* @see ForceAliasedAnnotationAttribute
*/
public class AliasAnnotationPostProcessor implements SynthesizedAnnotationPostProcessor {
@Override
public int order() {
return Integer.MIN_VALUE;
}
@Override
public void process(SynthesizedAnnotation synthesizedAnnotation, AnnotationSynthesizer synthesizer) {
final Map<String, AnnotationAttribute> attributeMap = synthesizedAnnotation.getAttributes();
// 记录别名与属性的关系
final ForestMap<String, AnnotationAttribute> attributeAliasMappings = new LinkedForestMap<>(false);
attributeMap.forEach((attributeName, attribute) -> {
final String alias = Opt.ofNullable(attribute.getAnnotation(Alias.class))
.map(Alias::value)
.orElse(null);
if (ObjectUtil.isNull(alias)) {
return;
}
final AnnotationAttribute aliasAttribute = attributeMap.get(alias);
Assert.notNull(aliasAttribute, "no method for alias: [{}]", alias);
attributeAliasMappings.putLinkedNodes(alias, aliasAttribute, attributeName, attribute);
});
// 处理别名
attributeMap.forEach((attributeName, attribute) -> {
final AnnotationAttribute resolvedAttribute = Opt.ofNullable(attributeName)
.map(attributeAliasMappings::getRootNode)
.map(TreeEntry::getValue)
.orElse(attribute);
Assert.isTrue(
ObjectUtil.isNull(resolvedAttribute)
|| ClassUtil.isAssignable(attribute.getAttributeType(), resolvedAttribute.getAttributeType()),
"return type of the root alias method [{}] is inconsistent with the original [{}]",
resolvedAttribute.getClass(), attribute.getAttributeType()
);
if (attribute != resolvedAttribute) {
attributeMap.put(attributeName, new ForceAliasedAnnotationAttribute(attribute, resolvedAttribute));
}
});
synthesizedAnnotation.setAttributes(attributeMap);
}
}

View File

@@ -0,0 +1,39 @@
package cn.hutool.core.annotation;
import java.lang.annotation.*;
/**
* <p>{@link Link}的子注解。表示“原始属性”将作为“关联属性”的别名。
* <ul>
* <li>当“原始属性”为默认值时,获取“关联属性”将返回“关联属性”本身的值;</li>
* <li>当“原始属性”不为默认值时,获取“关联属性”将返回“原始属性”的值;</li>
* </ul>
* <b>注意,该注解与{@link Link}、{@link ForceAliasFor}或{@link MirrorFor}一起使用时,将只有被声明在最上面的注解会生效</b>
*
* @author huangchengxing
* @see Link
* @see RelationType#ALIAS_FOR
*/
@Link(type = RelationType.ALIAS_FOR)
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
public @interface AliasFor {
/**
* 产生关联的注解类型,当不指定时,默认指注释的属性所在的类
*
* @return 注解类型
*/
@Link(annotation = Link.class, attribute = "annotation", type = RelationType.FORCE_ALIAS_FOR)
Class<? extends Annotation> annotation() default Annotation.class;
/**
* {@link #annotation()}指定注解中关联的属性
*
* @return 关联属性
*/
@Link(annotation = Link.class, attribute = "attribute", type = RelationType.FORCE_ALIAS_FOR)
String attribute() default "";
}

View File

@@ -0,0 +1,126 @@
package cn.hutool.core.annotation;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.util.ObjectUtil;
import java.util.function.BinaryOperator;
/**
* <p>用于处理注解对象中带有{@link Link}注解,且{@link Link#type()}为
* {@link RelationType#ALIAS_FOR}或{@link RelationType#FORCE_ALIAS_FOR}的属性。<br>
* 当该处理器执行完毕后,{@link Link}注解指向的目标注解的属性将会被包装并替换为
* {@link AliasedAnnotationAttribute}或{@link ForceAliasedAnnotationAttribute}。
*
* @author huangchengxing
* @see RelationType#ALIAS_FOR
* @see AliasedAnnotationAttribute
* @see RelationType#FORCE_ALIAS_FOR
* @see ForceAliasedAnnotationAttribute
*/
public class AliasLinkAnnotationPostProcessor extends AbstractLinkAnnotationPostProcessor {
private static final RelationType[] PROCESSED_RELATION_TYPES = new RelationType[]{ RelationType.ALIAS_FOR, RelationType.FORCE_ALIAS_FOR };
@Override
public int order() {
return Integer.MIN_VALUE + 2;
}
/**
* 该处理器只处理{@link Link#type()}类型为{@link RelationType#ALIAS_FOR}和{@link RelationType#FORCE_ALIAS_FOR}的注解属性
*
* @return 含有{@link RelationType#ALIAS_FOR}和{@link RelationType#FORCE_ALIAS_FOR}的数组
*/
@Override
protected RelationType[] processTypes() {
return PROCESSED_RELATION_TYPES;
}
/**
* 获取{@link Link}指向的目标注解属性,并根据{@link Link#type()}的类型是
* {@link RelationType#ALIAS_FOR}或{@link RelationType#FORCE_ALIAS_FOR}
* 将目标注解属性包装为{@link AliasedAnnotationAttribute}或{@link ForceAliasedAnnotationAttribute}
* 然后用包装后注解属性在对应的合成注解中替换原始的目标注解属性
*
* @param synthesizer 注解合成器
* @param annotation {@code originalAttribute}上的{@link Link}注解对象
* @param originalAnnotation 当前正在处理的{@link SynthesizedAnnotation}对象
* @param originalAttribute {@code originalAnnotation}上的待处理的属性
* @param linkedAnnotation {@link Link}指向的关联注解对象
* @param linkedAttribute {@link Link}指向的{@code originalAnnotation}中的关联属性,该参数可能为空
*/
@Override
protected void processLinkedAttribute(
AnnotationSynthesizer synthesizer, Link annotation,
SynthesizedAnnotation originalAnnotation, AnnotationAttribute originalAttribute,
SynthesizedAnnotation linkedAnnotation, AnnotationAttribute linkedAttribute) {
// 校验别名关系
checkAliasRelation(annotation, originalAttribute, linkedAttribute);
// 处理aliasFor类型的关系
if (RelationType.ALIAS_FOR.equals(annotation.type())) {
wrappingLinkedAttribute(synthesizer, originalAttribute, linkedAttribute, AliasedAnnotationAttribute::new);
return;
}
// 处理forceAliasFor类型的关系
wrappingLinkedAttribute(synthesizer, originalAttribute, linkedAttribute, ForceAliasedAnnotationAttribute::new);
}
/**
* 对指定注解属性进行包装,若该属性已被包装过,则递归以其为根节点的树结构,对树上全部的叶子节点进行包装
*/
private void wrappingLinkedAttribute(
AnnotationSynthesizer synthesizer, AnnotationAttribute originalAttribute, AnnotationAttribute aliasAttribute, BinaryOperator<AnnotationAttribute> wrapping) {
// 不是包装属性
if (!aliasAttribute.isWrapped()) {
processAttribute(synthesizer, originalAttribute, aliasAttribute, wrapping);
return;
}
// 是包装属性
final AbstractWrappedAnnotationAttribute wrapper = (AbstractWrappedAnnotationAttribute)aliasAttribute;
wrapper.getAllLinkedNonWrappedAttributes().forEach(
t -> processAttribute(synthesizer, originalAttribute, t, wrapping)
);
}
/**
* 获取指定注解属性,然后将其再进行一层包装
*/
private void processAttribute(
AnnotationSynthesizer synthesizer, AnnotationAttribute originalAttribute,
AnnotationAttribute target, BinaryOperator<AnnotationAttribute> wrapping) {
Opt.ofNullable(target.getAnnotationType())
.map(synthesizer::getSynthesizedAnnotation)
.ifPresent(t -> t.replaceAttribute(target.getAttributeName(), old -> wrapping.apply(old, originalAttribute)));
}
/**
* 基本校验
*/
private void checkAliasRelation(Link annotation, AnnotationAttribute originalAttribute, AnnotationAttribute linkedAttribute) {
checkLinkedAttributeNotNull(originalAttribute, linkedAttribute, annotation);
checkAttributeType(originalAttribute, linkedAttribute);
checkCircularDependency(originalAttribute, linkedAttribute);
}
/**
* 检查两个属性是否互为别名
*/
private void checkCircularDependency(AnnotationAttribute original, AnnotationAttribute alias) {
checkLinkedSelf(original, alias);
Link annotation = getLinkAnnotation(alias, RelationType.ALIAS_FOR, RelationType.FORCE_ALIAS_FOR);
if (ObjectUtil.isNull(annotation)) {
return;
}
final Class<?> aliasAnnotationType = getLinkedAnnotationType(annotation, alias.getAnnotationType());
if (ObjectUtil.notEqual(aliasAnnotationType, original.getAnnotationType())) {
return;
}
Assert.notEquals(
annotation.attribute(), original.getAttributeName(),
"circular reference between the alias attribute [{}] and the original attribute [{}]",
alias.getAttribute(), original.getAttribute()
);
}
}

View File

@@ -0,0 +1,36 @@
package cn.hutool.core.annotation;
/**
* <p>表示一个具有别名的属性。
* 当别名属性值为默认值时,优先返回原属性的值,当别名属性不为默认值时,优先返回别名属性的值
*
* @author huangchengxing
* @see AliasLinkAnnotationPostProcessor
* @see RelationType#ALIAS_FOR
*/
public class AliasedAnnotationAttribute extends AbstractWrappedAnnotationAttribute {
protected AliasedAnnotationAttribute(AnnotationAttribute origin, AnnotationAttribute linked) {
super(origin, linked);
}
/**
* 若{@link #linked}为默认值,则返回{@link #original}的值,否则返回{@link #linked}的值
*
* @return 属性值
*/
@Override
public Object getValue() {
return linked.isValueEquivalentToDefaultValue() ? super.getValue() : linked.getValue();
}
/**
* 当{@link #original}与{@link #linked}都为默认值时返回{@code true}
*
* @return 是否
*/
@Override
public boolean isValueEquivalentToDefaultValue() {
return linked.isValueEquivalentToDefaultValue() && original.isValueEquivalentToDefaultValue();
}
}

View File

@@ -0,0 +1,104 @@
package cn.hutool.core.annotation;
import cn.hutool.core.util.ReflectUtil;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
/**
* <p>表示注解的某个属性,等同于绑定的调用对象的{@link Method}方法。<br>
* 在{@link SynthesizedAggregateAnnotation}的解析以及取值过程中,
* 可以通过设置{@link SynthesizedAnnotation}的注解属性,
* 从而使得可以从一个注解对象中属性获取另一个注解对象的属性值
*
* <p>一般情况下,注解属性的处理会发生在{@link SynthesizedAnnotationPostProcessor}调用时
*
* @author huangchengxing
* @see SynthesizedAnnotationPostProcessor
* @see WrappedAnnotationAttribute
* @see CacheableAnnotationAttribute
* @see AbstractWrappedAnnotationAttribute
* @see ForceAliasedAnnotationAttribute
* @see AliasedAnnotationAttribute
* @see MirroredAnnotationAttribute
*/
public interface AnnotationAttribute {
/**
* 获取注解对象
*
* @return 注解对象
*/
Annotation getAnnotation();
/**
* 获取注解属性对应的方法
*
* @return 注解属性对应的方法
*/
Method getAttribute();
/**
* 获取声明属性的注解类
*
* @return 声明注解的注解类
*/
default Class<?> getAnnotationType() {
return getAttribute().getDeclaringClass();
}
/**
* 获取属性名称
*
* @return 属性名称
*/
default String getAttributeName() {
return getAttribute().getName();
}
/**
* 获取注解属性
*
* @return 注解属性
*/
default Object getValue() {
return ReflectUtil.invoke(getAnnotation(), getAttribute());
}
/**
* 该注解属性的值是否等于默认值
*
* @return 该注解属性的值是否等于默认值
*/
boolean isValueEquivalentToDefaultValue();
/**
* 获取属性类型
*
* @return 属性类型
*/
default Class<?> getAttributeType() {
return getAttribute().getReturnType();
}
/**
* 获取属性上的注解
*
* @param <T> 注解类型
* @param annotationType 注解类型
* @return 注解对象
*/
default <T extends Annotation> T getAnnotation(Class<T> annotationType) {
return getAttribute().getAnnotation(annotationType);
}
/**
* 当前注解属性是否已经被{@link WrappedAnnotationAttribute}包装
*
* @return boolean
*/
default boolean isWrapped() {
return false;
}
}

View File

@@ -0,0 +1,18 @@
package cn.hutool.core.annotation;
/**
* 表示一个可以从当前接口的实现类中,获得特定的属性值
*/
@FunctionalInterface
public interface AnnotationAttributeValueProvider {
/**
* 获取注解属性值
*
* @param attributeName 属性名称
* @param attributeType 属性类型
* @return 注解属性值
*/
Object getAttributeValue(String attributeName, Class<?> attributeType);
}

View File

@@ -0,0 +1,77 @@
package cn.hutool.core.annotation;
import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.Map;
/**
* <p>注解合成器,用于处理一组给定的与{@link #getSource()}具有直接或间接联系的注解对象,
* 并返回与原始注解对象具有不同属性的“合成”注解。
*
* <p>合成注解一般被用于处理类层级结果中具有直接或间接关联的注解对象,
* 当实例被创建时,会获取到这些注解对象,并使用{@link SynthesizedAnnotationSelector}对类型相同的注解进行过滤,
* 并最终得到类型不重复的有效注解对象。这些有效注解将被包装为{@link SynthesizedAnnotation}
* 然后最终用于“合成”一个{@link SynthesizedAggregateAnnotation}。<br>
* {@link SynthesizedAnnotationSelector}是合成注解生命周期中的第一个钩子,
* 自定义选择器以拦截原始注解被扫描的过程。
*
* <p>当合成注解完成对待合成注解的扫描,并完成了必要属性的加载后,
* 将会按顺序依次调用{@link SynthesizedAnnotationPostProcessor}
* 注解后置处理器允许用于对完成注解的待合成注解进行二次调整,
* 该钩子一般用于根据{@link Link}注解对属性进行调整。<br>
* {@link SynthesizedAnnotationPostProcessor}是合成注解生命周期中的第二个钩子,
* 自定义后置处理器以拦截原始在转为待合成注解后的初始化过程。
*
* <p>使用{@link #synthesize(Class)}用于获取“合成”后的注解,
* 该注解对象的属性可能会与原始的对象属性不同。
*
* @author huangchengxing
*/
public interface AnnotationSynthesizer {
/**
* 获取合成注解来源最初来源
*
* @return 合成注解来源最初来源
*/
Object getSource();
/**
* 合成注解选择器
*
* @return 注解选择器
*/
SynthesizedAnnotationSelector getAnnotationSelector();
/**
* 获取合成注解后置处理器
*
* @return 合成注解后置处理器
*/
Collection<SynthesizedAnnotationPostProcessor> getAnnotationPostProcessors();
/**
* 获取已合成的注解
*
* @param annotationType 注解类型
* @return 已合成的注解
*/
SynthesizedAnnotation getSynthesizedAnnotation(Class<?> annotationType);
/**
* 获取全部的合成注解
*
* @return 合成注解
*/
Map<Class<? extends Annotation>, SynthesizedAnnotation> getAllSynthesizedAnnotation();
/**
* 获取合成注解
*
* @param annotationType 注解类型
* @param <T> 注解类型
* @return 类型
*/
<T extends Annotation> T synthesize(Class<T> annotationType);
}

View File

@@ -3,10 +3,8 @@ package cn.hutool.core.annotation;
import cn.hutool.core.annotation.scanner.*;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
@@ -15,10 +13,8 @@ import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 注解工具类<br>
@@ -320,75 +316,19 @@ public class AnnotationUtil {
return annotationType.isAnnotationPresent(Inherited.class);
}
/**
* 设置新的注解的属性(字段)值
*
* @param annotation 注解对象
* @param annotationField 注解属性(字段)名称
* @param value 要更新的属性值
* @since 5.5.2
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public static void setValue(Annotation annotation, String annotationField, Object value) {
final Map memberValues = (Map) ReflectUtil.getFieldValue(Proxy.getInvocationHandler(annotation), "memberValues");
memberValues.put(annotationField, value);
}
/**
* 获取别名支持后的注解
*
* @param annotationEle 被注解的类
* @param annotationType 注解类型Class
* @param <T> 注解类型
* @return 别名支持后的注解
* @since 5.7.23
*/
@SuppressWarnings("unchecked")
public static <T extends Annotation> T getAnnotationAlias(AnnotatedElement annotationEle, Class<T> annotationType) {
final T annotation = getAnnotation(annotationEle, annotationType);
return (T) Proxy.newProxyInstance(annotationType.getClassLoader(), new Class[]{annotationType}, new AnnotationProxy<>(annotation));
}
/**
* 将指定注解实例与其元注解转为合成注解
*
* @param annotation 注解对象
* @param annotationType 注解类
* @param <T> 注解类型
* @return 合成注解
* @see SyntheticAnnotation
*/
public static <T extends Annotation> T getSynthesisAnnotation(Annotation annotation, Class<T> annotationType) {
return SyntheticAnnotation.of(annotation).getAnnotation(annotationType);
}
/**
* 获取元素上所有指定注解
* <ul>
* <li>若元素是类,则递归解析全部父类和全部父接口上的注解;</li>
* <li>若元素是方法、属性或注解,则只解析其直接声明的注解;</li>
* </ul>
*
* @param annotatedEle {@link AnnotatedElement}可以是Class、Method、Field、Constructor、ReflectPermission
* @param annotationType 注解类
* @param <T> 注解类型
* @return 合成注解
* @see SyntheticAnnotation
*/
public static <T extends Annotation> List<T> getAllSynthesisAnnotations(AnnotatedElement annotatedEle, Class<T> annotationType) {
AnnotationScanner[] scanners = new AnnotationScanner[]{
new MetaAnnotationScanner(), new TypeAnnotationScanner(), new MethodAnnotationScanner(), new FieldAnnotationScanner()
};
return AnnotationScanner.scanByAnySupported(annotatedEle, scanners).stream()
.map(annotation -> getSynthesisAnnotation(annotation, annotationType))
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
/**
* 扫描注解类,以及注解类的{@link Class}层级结构中的注解,将返回除了{@link #META_ANNOTATIONS}中指定的JDK默认注解外
* 按元注解对象与{@code annotationType}的距离和{@link Class#getAnnotations()}顺序排序的注解对象集合
*
* <p>比如:<br>
* 若{@code annotationType}为 A且A存在元注解BB又存在元注解C和D则有
* <pre>
* |-&gt; C.class [@a, @b]
* A.class -&gt; B.class [@a] -|
* |-&gt; D.class [@a, @c]
* </pre>
* 扫描A则该方法最终将返回 {@code [@a, @a, @b, @a, @c]}
*
* @param annotationType 注解类
* @return 注解对象集合
* @see MetaAnnotationScanner
@@ -408,6 +348,16 @@ public class AnnotationUtil {
* </ul>
* 注解根据其声明类/接口被扫描的顺序排序,若注解都在同一个{@link Class}中被声明,则还会遵循{@link Class#getAnnotations()}的顺序。
*
* <p>比如:<br>
* 若{@code targetClass}为{@code A.class},且{@code A.class}存在父类{@code B.class}、父接口{@code C.class}
* 三个类的注解声明情况如下:
* <pre>
* |-&gt; B.class [@a, @b]
* A.class [@a] -|
* |-&gt; C.class [@a, @c]
* </pre>
* 则该方法最终将返回 {@code [@a, @a, @b, @a, @c]}
*
* @param targetClass 类
* @return 注解对象集合
* @see TypeAnnotationScanner
@@ -428,6 +378,14 @@ public class AnnotationUtil {
* </ul>
* 方法上的注解根据方法的声明类/接口被扫描的顺序排序,若注解都在同一个类的同一个方法中被声明,则还会遵循{@link Method#getAnnotations()}的顺序。
*
* <p>比如:<br>
* 若方法X声明于{@code A.class},且重载/重写自父类{@code B.class}并且父类中的方法X由重写至其实现的接口{@code C.class}
* 三个类的注解声明情况如下:
* <pre>
* A#X()[@a] -&gt; B#X()[@b] -&gt; C#X()[@c]
* </pre>
* 则该方法最终将返回 {@code [@a, @b, @c]}
*
* @param method 方法
* @return 注解对象集合
* @see MethodAnnotationScanner
@@ -436,6 +394,152 @@ public class AnnotationUtil {
return new MethodAnnotationScanner(true).getIfSupport(method);
}
/**
* 设置新的注解的属性(字段)值
*
* @param annotation 注解对象
* @param annotationField 注解属性(字段)名称
* @param value 要更新的属性值
* @since 5.5.2
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public static void setValue(Annotation annotation, String annotationField, Object value) {
final Map memberValues = (Map) ReflectUtil.getFieldValue(Proxy.getInvocationHandler(annotation), "memberValues");
memberValues.put(annotationField, value);
}
/**
* 该注解对象是否为通过代理类生成的合成注解
*
* @param annotation 注解对象
* @return 是否
* @see SynthesizedAnnotationProxy#isProxyAnnotation(Class)
*/
public static boolean isSynthesizedAnnotation(Annotation annotation) {
return SynthesizedAnnotationProxy.isProxyAnnotation(annotation.getClass());
}
/**
* 获取别名支持后的注解
*
* @param annotationEle 被注解的类
* @param annotationType 注解类型Class
* @param <T> 注解类型
* @return 别名支持后的注解
* @since 5.7.23
*/
public static <T extends Annotation> T getAnnotationAlias(AnnotatedElement annotationEle, Class<T> annotationType) {
final T annotation = getAnnotation(annotationEle, annotationType);
return aggregatingFromAnnotation(annotation).synthesize(annotationType);
}
/**
* 将指定注解实例与其元注解转为合成注解
*
* @param annotationType 注解类
* @param annotations 注解对象
* @param <T> 注解类型
* @return 合成注解
* @see SynthesizedAggregateAnnotation
*/
public static <T extends Annotation> T getSynthesizedAnnotation(Class<T> annotationType, Annotation... annotations) {
// TODO 缓存合成注解信息,避免重复解析
return Opt.ofNullable(annotations)
.filter(ArrayUtil::isNotEmpty)
.map(AnnotationUtil::aggregatingFromAnnotationWithMeta)
.map(a -> a.synthesize(annotationType))
.get();
}
/**
* <p>获取元素上距离指定元素最接近的合成注解
* <ul>
* <li>若元素是类,则递归解析全部父类和全部父接口上的注解;</li>
* <li>若元素是方法、属性或注解,则只解析其直接声明的注解;</li>
* </ul>
*
* <p>注解合成规则如下:
* 若{@code AnnotatedEle}按顺序从上到下声明了ABC三个注解且三注解存在元注解如下
* <pre>
* A -&gt; M3
* B -&gt; M1 -&gt; M2 -&gt; M3
* C -&gt; M2 -&gt; M3
* </pre>
* 此时入参{@code annotationType}类型为{@code M2}则最终将优先返回基于根注解B合成的合成注解
*
* @param annotatedEle {@link AnnotatedElement}可以是Class、Method、Field、Constructor、ReflectPermission
* @param annotationType 注解类
* @param <T> 注解类型
* @return 合成注解
* @see SynthesizedAggregateAnnotation
*/
public static <T extends Annotation> T getSynthesizedAnnotation(AnnotatedElement annotatedEle, Class<T> annotationType) {
T target = annotatedEle.getAnnotation(annotationType);
if (ObjectUtil.isNotNull(target)) {
return target;
}
AnnotationScanner[] scanners = new AnnotationScanner[]{
new MetaAnnotationScanner(), new TypeAnnotationScanner(), new MethodAnnotationScanner(), new FieldAnnotationScanner()
};
return AnnotationScanner.scanByAnySupported(annotatedEle, scanners).stream()
.map(annotation -> getSynthesizedAnnotation(annotationType, annotation))
.filter(Objects::nonNull)
.findFirst()
.orElse(null);
}
/**
* 获取元素上所有指定注解
* <ul>
* <li>若元素是类,则递归解析全部父类和全部父接口上的注解;</li>
* <li>若元素是方法、属性或注解,则只解析其直接声明的注解;</li>
* </ul>
*
* <p>注解合成规则如下:
* 若{@code AnnotatedEle}按顺序从上到下声明了ABC三个注解且三注解存在元注解如下
* <pre>
* A -&gt; M1 -&gt; M2
* B -&gt; M3 -&gt; M1 -&gt; M2
* C -&gt; M2
* </pre>
* 此时入参{@code annotationType}类型为{@code M1}则最终将返回基于根注解A与根注解B合成的合成注解。
*
* @param annotatedEle {@link AnnotatedElement}可以是Class、Method、Field、Constructor、ReflectPermission
* @param annotationType 注解类
* @param <T> 注解类型
* @return 合成注解
* @see SynthesizedAggregateAnnotation
*/
public static <T extends Annotation> List<T> getAllSynthesizedAnnotations(AnnotatedElement annotatedEle, Class<T> annotationType) {
AnnotationScanner[] scanners = new AnnotationScanner[]{
new MetaAnnotationScanner(), new TypeAnnotationScanner(), new MethodAnnotationScanner(), new FieldAnnotationScanner()
};
return AnnotationScanner.scanByAnySupported(annotatedEle, scanners).stream()
.map(annotation -> getSynthesizedAnnotation(annotationType, annotation))
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
/**
* 对指定注解对象进行聚合
*
* @param annotations 注解对象
* @return 聚合注解
*/
public static SynthesizedAggregateAnnotation aggregatingFromAnnotation(Annotation... annotations) {
return new GenericSynthesizedAggregateAnnotation(Arrays.asList(annotations), EmptyAnnotationScanner.INSTANCE);
}
/**
* 对指定注解对象及其元注解进行聚合
*
* @param annotations 注解对象
* @return 聚合注解
*/
public static SynthesizedAggregateAnnotation aggregatingFromAnnotationWithMeta(Annotation... annotations) {
return new GenericSynthesizedAggregateAnnotation(Arrays.asList(annotations), new MetaAnnotationScanner());
}
/**
* 方法是否为注解属性方法。 <br>
* 方法无参数,且有返回值的方法认为是注解属性的方法。
@@ -446,36 +550,4 @@ public class AnnotationUtil {
return method.getParameterCount() == 0 && method.getReturnType() != void.class;
}
/**
* 获取注解的全部属性值获取方法
*
* @param annotationType 注解
* @return 注解的全部属性值
* @throws IllegalArgumentException 当别名属性在注解中不存在,或别名属性的值与原属性的值类型不一致时抛出
*/
static Map<String, Method> getAttributeMethods(Class<? extends Annotation> annotationType) {
// 获取全部注解属性值
Map<String, Method> attributeMethods = Stream.of(annotationType.getDeclaredMethods())
.filter(AnnotationUtil::isAttributeMethod)
.collect(Collectors.toMap(Method::getName, Function.identity()));
// 处理别名
attributeMethods.forEach((methodName, method) -> {
String alias = Opt.ofNullable(method.getAnnotation(Alias.class))
.map(Alias::value)
.orElse(null);
if (ObjectUtil.isNull(alias)) {
return;
}
// 存在别名,则将原本的值替换为别名对应的值
Assert.isTrue(attributeMethods.containsKey(alias), "No method for alias: [{}]", alias);
Method aliasAttributeMethod = attributeMethods.get(alias);
Assert.isTrue(
ObjectUtil.isNull(aliasAttributeMethod) || ClassUtil.isAssignable(method.getReturnType(), aliasAttributeMethod.getReturnType()),
"Return type of the alias method [{}] is inconsistent with the original [{}]",
aliasAttributeMethod.getClass(), method.getParameterTypes()
);
attributeMethods.put(methodName, aliasAttributeMethod);
});
return attributeMethods;
}
}

View File

@@ -0,0 +1,63 @@
package cn.hutool.core.annotation;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
/**
* {@link AnnotationAttribute}的基本实现
*
* @author huangchengxing
*/
public class CacheableAnnotationAttribute implements AnnotationAttribute {
private boolean valueInvoked;
private Object value;
private boolean defaultValueInvoked;
private Object defaultValue;
private final Annotation annotation;
private final Method attribute;
public CacheableAnnotationAttribute(Annotation annotation, Method attribute) {
Assert.notNull(annotation, "annotation must not null");
Assert.notNull(attribute, "attribute must not null");
this.annotation = annotation;
this.attribute = attribute;
this.valueInvoked = false;
this.defaultValueInvoked = false;
}
@Override
public Annotation getAnnotation() {
return this.annotation;
}
@Override
public Method getAttribute() {
return this.attribute;
}
@Override
public Object getValue() {
if (!valueInvoked) {
valueInvoked = true;
value = ReflectUtil.invoke(annotation, attribute);
}
return value;
}
@Override
public boolean isValueEquivalentToDefaultValue() {
if (!defaultValueInvoked) {
defaultValue = attribute.getDefaultValue();
defaultValueInvoked = true;
}
return ObjectUtil.equals(getValue(), defaultValue);
}
}

View File

@@ -1,5 +1,6 @@
package cn.hutool.core.annotation;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.multi.RowKeyTable;
import cn.hutool.core.map.multi.Table;
import cn.hutool.core.util.ObjectUtil;
@@ -8,26 +9,40 @@ import java.util.Collection;
import java.util.Comparator;
/**
* 带缓存功能的{@link SynthesizedAnnotationAttributeProcessor}实现,
* <p>带缓存功能的{@link SynthesizedAnnotationAttributeProcessor}实现,
* 构建时需要传入比较器,获取属性值时将根据比较器对合成注解进行排序,
* 然后选择具有所需属性的,排序最靠前的注解用于获取属性值
*
* <p>通过该处理器获取合成注解属性值时会出现隐式别名,
* 即子注解和元注解中同时存在类型和名称皆相同的属性时,元注解中属性总是会被该属性覆盖,
* 并且该覆盖关系并不会通过{@link Alias}或{@link Link}被传递到关联的属性中。
*
* @author huangchengxing
*/
public class CacheableSynthesizedAnnotationAttributeProcessor implements SynthesizedAnnotationAttributeProcessor {
private final Table<String, Class<?>, Object> valueCaches = new RowKeyTable<>();
private final Comparator<SynthesizedAnnotation> annotationComparator;
private final Comparator<Hierarchical> annotationComparator;
/**
* 创建一个带缓存的注解值选择器
*
* @param annotationComparator 注解比较器,排序更靠前的注解将被优先用于获取值
*/
public CacheableSynthesizedAnnotationAttributeProcessor(Comparator<SynthesizedAnnotation> annotationComparator) {
public CacheableSynthesizedAnnotationAttributeProcessor(Comparator<Hierarchical> annotationComparator) {
Assert.notNull(annotationComparator, "annotationComparator must not null");
this.annotationComparator = annotationComparator;
}
/**
* 创建一个带缓存的注解值选择器,
* 默认按{@link SynthesizedAnnotation#getVerticalDistance()}和{@link SynthesizedAnnotation#getHorizontalDistance()}排序,
* 越靠前的越优先被取值。
*/
public CacheableSynthesizedAnnotationAttributeProcessor() {
this(Hierarchical.DEFAULT_HIERARCHICAL_COMPARATOR);
}
@SuppressWarnings("unchecked")
@Override
public <T> T getAttributeValue(String attributeName, Class<T> attributeType, Collection<? extends SynthesizedAnnotation> synthesizedAnnotations) {
@@ -39,7 +54,7 @@ public class CacheableSynthesizedAnnotationAttributeProcessor implements Synthes
value = synthesizedAnnotations.stream()
.filter(ma -> ma.hasAttribute(attributeName, attributeType))
.min(annotationComparator)
.map(ma -> ma.getAttribute(attributeName))
.map(ma -> ma.getAttributeValue(attributeName))
.orElse(null);
valueCaches.put(attributeName, attributeType, value);
return (T)value;

View File

@@ -1,19 +1,13 @@
package cn.hutool.core.annotation;
import cn.hutool.core.collection.CollUtil;
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.reflect.AnnotatedElement;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
/**
@@ -126,7 +120,9 @@ public class CombinationAnnotationElement implements AnnotatedElement, Serializa
// 直接注解
for (Annotation annotation : annotations) {
annotationType = annotation.annotationType();
if (AnnotationUtil.isNotJdkMateAnnotation(annotationType)) {
// issue#I5FQGW@Gitee跳过元注解和已经处理过的注解防止递归调用
if (AnnotationUtil.isNotJdkMateAnnotation(annotationType)
&& false == declaredAnnotationMap.containsKey(annotationType)) {
if(test(annotation)){
declaredAnnotationMap.put(annotationType, annotation);
}
@@ -145,7 +141,9 @@ public class CombinationAnnotationElement implements AnnotatedElement, Serializa
Class<? extends Annotation> annotationType;
for (Annotation annotation : annotations) {
annotationType = annotation.annotationType();
if (AnnotationUtil.isNotJdkMateAnnotation(annotationType)) {
// issue#I5FQGW@Gitee跳过元注解和已经处理过的注解防止递归调用
if (AnnotationUtil.isNotJdkMateAnnotation(annotationType)
&& false == declaredAnnotationMap.containsKey(annotationType)) {
if(test(annotation)){
annotationMap.put(annotationType, annotation);
}

View File

@@ -0,0 +1,35 @@
package cn.hutool.core.annotation;
import java.lang.annotation.*;
/**
* <p>{@link Link}的子注解。表示“原始属性”将强制作为“关联属性”的别名。效果等同于在“原始属性”上添加{@link Alias}注解,
* 任何情况下,获取“关联属性”的值都将直接返回“原始属性”的值
* <b>注意,该注解与{@link Link}、{@link AliasFor}或{@link MirrorFor}一起使用时,将只有被声明在最上面的注解会生效</b>
*
* @author huangchengxing
* @see Link
* @see RelationType#FORCE_ALIAS_FOR
*/
@Link(type = RelationType.FORCE_ALIAS_FOR)
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
public @interface ForceAliasFor {
/**
* 产生关联的注解类型,当不指定时,默认指注释的属性所在的类
*
* @return 关联注解类型
*/
@Link(annotation = Link.class, attribute = "annotation", type = RelationType.FORCE_ALIAS_FOR)
Class<? extends Annotation> annotation() default Annotation.class;
/**
* {@link #annotation()}指定注解中关联的属性
*
* @return 关联的属性
*/
@Link(annotation = Link.class, attribute = "attribute", type = RelationType.FORCE_ALIAS_FOR)
String attribute() default "";
}

View File

@@ -0,0 +1,49 @@
package cn.hutool.core.annotation;
/**
* 表示一个被指定了强制别名的注解属性。
* 当调用{@link #getValue()}时,总是返回{@link #linked}的值
*
* @author huangchengxing
* @see AliasAnnotationPostProcessor
* @see AliasLinkAnnotationPostProcessor
* @see RelationType#ALIAS_FOR
* @see RelationType#FORCE_ALIAS_FOR
*/
public class ForceAliasedAnnotationAttribute extends AbstractWrappedAnnotationAttribute {
protected ForceAliasedAnnotationAttribute(AnnotationAttribute origin, AnnotationAttribute linked) {
super(origin, linked);
}
/**
* 总是返回{@link #linked}的{@link AnnotationAttribute#getValue()}的返回值
*
* @return {@link #linked}的{@link AnnotationAttribute#getValue()}的返回值
*/
@Override
public Object getValue() {
return linked.getValue();
}
/**
* 总是返回{@link #linked}的{@link AnnotationAttribute#isValueEquivalentToDefaultValue()}的返回值
*
* @return {@link #linked}的{@link AnnotationAttribute#isValueEquivalentToDefaultValue()}的返回值
*/
@Override
public boolean isValueEquivalentToDefaultValue() {
return linked.isValueEquivalentToDefaultValue();
}
/**
* 总是返回{@link #linked}的{@link AnnotationAttribute#getAttributeType()}的返回值
*
* @return {@link #linked}的{@link AnnotationAttribute#getAttributeType()}的返回值
*/
@Override
public Class<?> getAttributeType() {
return linked.getAttributeType();
}
}

View File

@@ -0,0 +1,318 @@
package cn.hutool.core.annotation;
import cn.hutool.core.annotation.scanner.AnnotationScanner;
import cn.hutool.core.annotation.scanner.MetaAnnotationScanner;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.util.ObjectUtil;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.util.*;
/**
* {@link SynthesizedAggregateAnnotation}的基本实现,表示基于多个注解对象,
* 或多个根注解对象与他们的多层元注解对象的聚合得到的注解。
*
* <p>假设现有注解A若指定的{@link #annotationScanner}支持扫描注解A的元注解
* 且A上存在元注解BB上存在元注解C则对注解A进行解析将得到包含根注解A以及其元注解B、C在内的合成元注解聚合{@link GenericSynthesizedAggregateAnnotation}。
* 从{@link AnnotatedElement}的角度来说得到的合成注解是一个同时承载有ABC三个注解对象的被注解元素
* 因此通过调用{@link AnnotatedElement}的相关方法将返回对应符合语义的注解对象。
*
* <p>在扫描指定根注解及其元注解时,若在不同的层级出现了类型相同的注解实例,
* 将会根据实例化时指定的{@link SynthesizedAnnotationSelector}选择最优的注解,
* 完成对根注解及其元注解的扫描后,合成注解中每种类型的注解对象都将有且仅有一个。<br>
* 默认情况下,将使用{@link SynthesizedAnnotationSelector#NEAREST_AND_OLDEST_PRIORITY}作为选择器,
* 此时若出现扫描时得到了多个同类型的注解对象,有且仅有最接近根注解的注解对象会被作为有效注解。
*
* <p>当扫描的注解对象经过{@link SynthesizedAnnotationSelector}处理后,
* 将会被转为{@link MetaAnnotation},并使用在实例化时指定的{@link AliasAnnotationPostProcessor}
* 进行后置处理。<br>
* 默认情况下,将注册以下后置处理器以对{@link Alias}与{@link Link}和其扩展注解提供支持:
* <ul>
* <li>{@link AliasAnnotationPostProcessor}</li>
* <li>{@link MirrorLinkAnnotationPostProcessor}</li>
* <li>{@link AliasLinkAnnotationPostProcessor}</li>
* </ul>
* 若用户需要自行扩展,则需要保证上述三个处理器被正确注入当前实例。
*
* <p>{@link GenericSynthesizedAggregateAnnotation}支持通过{@link #getAttributeValue(String, Class)}
* 或通过{@link #synthesize(Class)}获得注解代理对象后获取指定类型的注解属性值,
* 返回的属性值将根据合成注解中对应原始注解属性上的{@link Alias}与{@link Link}注解而有所变化。
* 通过当前实例获取属性值时,将经过{@link SynthesizedAnnotationAttributeProcessor}的处理。<br>
* 默认情况下,实例将会注册{@link CacheableSynthesizedAnnotationAttributeProcessor}
* 该处理器将令元注解中与子注解类型与名称皆一致的属性被子注解的属性覆盖,并且缓存最终获取到的属性值。
*
* @author huangchengxing
* @see AnnotationUtil
* @see SynthesizedAnnotationProxy
* @see SynthesizedAnnotationSelector
* @see SynthesizedAnnotationAttributeProcessor
* @see SynthesizedAnnotationPostProcessor
* @see AnnotationSynthesizer
* @see AnnotationScanner
*/
public class GenericSynthesizedAggregateAnnotation
extends AbstractAnnotationSynthesizer<List<Annotation>>
implements SynthesizedAggregateAnnotation {
/**
* 根对象
*/
private final Object root;
/**
* 距离根对象的垂直距离
*/
private final int verticalDistance;
/**
* 距离根对象的水平距离
*/
private final int horizontalDistance;
/**
* 合成注解属性处理器
*/
private final SynthesizedAnnotationAttributeProcessor attributeProcessor;
/**
* 基于指定根注解,为其与其元注解的层级结构中的全部注解构造一个合成注解。
* 当层级结构中出现了相同的注解对象时,将优先选择以距离根注解最近,且优先被扫描的注解对象,
* 当获取值时,同样遵循该规则。
*
* @param source 源注解
*/
public GenericSynthesizedAggregateAnnotation(Annotation... source) {
this(Arrays.asList(source), new MetaAnnotationScanner());
}
/**
* 基于指定根注解,为其层级结构中的全部注解构造一个合成注解。
* 若扫描器支持对注解的层级结构进行扫描,则若层级结构中出现了相同的注解对象时,
* 将优先选择以距离根注解最近,且优先被扫描的注解对象,并且当获取注解属性值时同样遵循该规则。
*
* @param source 源注解
* @param annotationScanner 注解扫描器,该扫描器必须支持扫描注解类
*/
public GenericSynthesizedAggregateAnnotation(List<Annotation> source, AnnotationScanner annotationScanner) {
this(
source, SynthesizedAnnotationSelector.NEAREST_AND_OLDEST_PRIORITY,
new CacheableSynthesizedAnnotationAttributeProcessor(),
Arrays.asList(
SynthesizedAnnotationPostProcessor.ALIAS_ANNOTATION_POST_PROCESSOR,
SynthesizedAnnotationPostProcessor.MIRROR_LINK_ANNOTATION_POST_PROCESSOR,
SynthesizedAnnotationPostProcessor.ALIAS_LINK_ANNOTATION_POST_PROCESSOR
),
annotationScanner
);
}
/**
* 基于指定根注解,为其层级结构中的全部注解构造一个合成注解
*
* @param source 当前查找的注解对象
* @param annotationSelector 合成注解选择器
* @param attributeProcessor 注解属性处理器
* @param annotationPostProcessors 注解后置处理器
* @param annotationScanner 注解扫描器,该扫描器必须支持扫描注解类
*/
public GenericSynthesizedAggregateAnnotation(
List<Annotation> source,
SynthesizedAnnotationSelector annotationSelector,
SynthesizedAnnotationAttributeProcessor attributeProcessor,
Collection<SynthesizedAnnotationPostProcessor> annotationPostProcessors,
AnnotationScanner annotationScanner) {
this(
null, 0, 0,
source, annotationSelector, attributeProcessor, annotationPostProcessors, annotationScanner
);
}
/**
* 基于指定根注解,为其层级结构中的全部注解构造一个合成注解
*
* @param root 根对象
* @param verticalDistance 距离根对象的水平距离
* @param horizontalDistance 距离根对象的垂直距离
* @param source 当前查找的注解对象
* @param annotationSelector 合成注解选择器
* @param attributeProcessor 注解属性处理器
* @param annotationPostProcessors 注解后置处理器
* @param annotationScanner 注解扫描器,该扫描器必须支持扫描注解类
*/
GenericSynthesizedAggregateAnnotation(
Object root, int verticalDistance, int horizontalDistance,
List<Annotation> source,
SynthesizedAnnotationSelector annotationSelector,
SynthesizedAnnotationAttributeProcessor attributeProcessor,
Collection<SynthesizedAnnotationPostProcessor> annotationPostProcessors,
AnnotationScanner annotationScanner) {
super(source, annotationSelector, annotationPostProcessors, annotationScanner);
Assert.notNull(attributeProcessor, "attributeProcessor must not null");
this.root = ObjectUtil.defaultIfNull(root, this);
this.verticalDistance = verticalDistance;
this.horizontalDistance = horizontalDistance;
this.attributeProcessor = attributeProcessor;
}
/**
* 获取根对象
*
* @return 根对象
*/
@Override
public Object getRoot() {
return root;
}
/**
* 获取与根对象的垂直距离
*
* @return 与根对象的垂直距离
*/
@Override
public int getVerticalDistance() {
return verticalDistance;
}
/**
* 获取与根对象的水平距离
*
* @return 获取与根对象的水平距离
*/
@Override
public int getHorizontalDistance() {
return horizontalDistance;
}
/**
* 按广度优先扫描{@link #source}上的元注解
*/
@Override
protected Map<Class<? extends Annotation>, SynthesizedAnnotation> loadAnnotations() {
Map<Class<? extends Annotation>, SynthesizedAnnotation> annotationMap = new LinkedHashMap<>();
// 根注解默认水平坐标为0根注解的元注解坐标从1开始
for (int i = 0; i < source.size(); i++) {
final Annotation sourceAnnotation = source.get(i);
Assert.isFalse(AnnotationUtil.isSynthesizedAnnotation(sourceAnnotation), "source [{}] has been synthesized");
annotationMap.put(sourceAnnotation.annotationType(), new MetaAnnotation(sourceAnnotation, sourceAnnotation, 0, i));
Assert.isTrue(
annotationScanner.support(sourceAnnotation.annotationType()),
"annotation scanner [{}] cannot support scan [{}]",
annotationScanner, sourceAnnotation.annotationType()
);
annotationScanner.scan(
(index, annotation) -> {
SynthesizedAnnotation oldAnnotation = annotationMap.get(annotation.annotationType());
SynthesizedAnnotation newAnnotation = new MetaAnnotation(sourceAnnotation, annotation, index + 1, annotationMap.size());
if (ObjectUtil.isNull(oldAnnotation)) {
annotationMap.put(annotation.annotationType(), newAnnotation);
} else {
annotationMap.put(annotation.annotationType(), annotationSelector.choose(oldAnnotation, newAnnotation));
}
},
sourceAnnotation.annotationType(), null
);
}
return annotationMap;
}
/**
* 获取合成注解属性处理器
*
* @return 合成注解属性处理器
*/
@Override
public SynthesizedAnnotationAttributeProcessor getAnnotationAttributeProcessor() {
return this.attributeProcessor;
}
/**
* 根据指定的属性名与属性类型获取对应的属性值,若存在{@link Alias}则获取{@link Alias#value()}指定的别名属性的值
* <p>当不同层级的注解之间存在同名同类型属性时,将优先获取更接近根注解的属性
*
* @param attributeName 属性名
* @param attributeType 属性类型
* @return 属性
*/
@Override
public Object getAttributeValue(String attributeName, Class<?> attributeType) {
return attributeProcessor.getAttributeValue(attributeName, attributeType, synthesizedAnnotationMap.values());
}
/**
* 获取合成注解中包含的指定注解
*
* @param annotationType 注解类型
* @param <T> 注解类型
* @return 注解对象
*/
@Override
public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
return Opt.ofNullable(annotationType)
.map(synthesizedAnnotationMap::get)
.map(SynthesizedAnnotation::getAnnotation)
.map(annotationType::cast)
.orElse(null);
}
/**
* 当前合成注解中是否存在指定元注解
*
* @param annotationType 注解类型
* @return 是否
*/
@Override
public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
return synthesizedAnnotationMap.containsKey(annotationType);
}
/**
* 获取合成注解中包含的全部注解
*
* @return 注解对象
*/
@Override
public Annotation[] getAnnotations() {
return synthesizedAnnotationMap.values().stream()
.map(SynthesizedAnnotation::getAnnotation)
.toArray(Annotation[]::new);
}
/**
* 若合成注解在存在指定元注解,则使用动态代理生成一个对应的注解实例
*
* @param annotationType 注解类型
* @return 合成注解对象
* @see SynthesizedAnnotationProxy#create(Class, AnnotationAttributeValueProvider, SynthesizedAnnotation)
*/
@Override
public <T extends Annotation> T synthesize(Class<T> annotationType, SynthesizedAnnotation annotation) {
return SynthesizedAnnotationProxy.create(annotationType, this, annotation);
}
/**
* 注解包装类,表示{@link #source}以及{@link #source}所属层级结构中的全部关联注解对象
*
* @author huangchengxing
*/
public static class MetaAnnotation extends GenericSynthesizedAnnotation<Annotation, Annotation> {
/**
* 创建一个合成注解
*
* @param root 根对象
* @param annotation 被合成的注解对象
* @param verticalDistance 距离根对象的水平距离
* @param horizontalDistance 距离根对象的垂直距离
*/
protected MetaAnnotation(Annotation root, Annotation annotation, int verticalDistance, int horizontalDistance) {
super(root, annotation, verticalDistance, horizontalDistance);
}
}
}

View File

@@ -0,0 +1,197 @@
package cn.hutool.core.annotation;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjectUtil;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* {@link SynthesizedAnnotation}的基本实现
*
* @param <R> 根对象类型
* @param <T> 注解类型
* @author huangchengxing
*/
public class GenericSynthesizedAnnotation<R, T extends Annotation> implements SynthesizedAnnotation {
private final R root;
private final T annotation;
private final Map<String, AnnotationAttribute> attributeMethodCaches;
private final int verticalDistance;
private final int horizontalDistance;
/**
* 创建一个合成注解
*
* @param root 根对象
* @param annotation 被合成的注解对象
* @param verticalDistance 距离根对象的水平距离
* @param horizontalDistance 距离根对象的垂直距离
*/
protected GenericSynthesizedAnnotation(
R root, T annotation, int verticalDistance, int horizontalDistance) {
this.root = root;
this.annotation = annotation;
this.verticalDistance = verticalDistance;
this.horizontalDistance = horizontalDistance;
this.attributeMethodCaches = new HashMap<>();
this.attributeMethodCaches.putAll(loadAttributeMethods());
}
/**
* 加载注解属性
*
* @return 注解属性
*/
protected Map<String, AnnotationAttribute> loadAttributeMethods() {
return Stream.of(ClassUtil.getDeclaredMethods(annotation.annotationType()))
.filter(AnnotationUtil::isAttributeMethod)
.collect(Collectors.toMap(Method::getName, method -> new CacheableAnnotationAttribute(annotation, method)));
}
/**
* 元注解是否存在该属性
*
* @param attributeName 属性名
* @return 是否存在该属性
*/
public boolean hasAttribute(String attributeName) {
return attributeMethodCaches.containsKey(attributeName);
}
/**
* 元注解是否存在该属性,且该属性的值类型是指定类型或其子类
*
* @param attributeName 属性名
* @param returnType 返回值类型
* @return 是否存在该属性
*/
@Override
public boolean hasAttribute(String attributeName, Class<?> returnType) {
return Opt.ofNullable(attributeMethodCaches.get(attributeName))
.filter(method -> ClassUtil.isAssignable(returnType, method.getAttributeType()))
.isPresent();
}
/**
* 获取该注解的全部属性
*
* @return 注解属性
*/
@Override
public Map<String, AnnotationAttribute> getAttributes() {
return this.attributeMethodCaches;
}
/**
* 设置属性值
*
* @param attributeName 属性名称
* @param attribute 注解属性
*/
@Override
public void setAttribute(String attributeName, AnnotationAttribute attribute) {
attributeMethodCaches.put(attributeName, attribute);
}
/**
* 替换属性值
*
* @param attributeName 属性名
* @param operator 替换操作
*/
@Override
public void replaceAttribute(String attributeName, UnaryOperator<AnnotationAttribute> operator) {
AnnotationAttribute old = attributeMethodCaches.get(attributeName);
if (ObjectUtil.isNotNull(old)) {
attributeMethodCaches.put(attributeName, operator.apply(old));
}
}
/**
* 获取属性值
*
* @param attributeName 属性名
* @return 属性值
*/
@Override
public Object getAttributeValue(String attributeName) {
return Opt.ofNullable(attributeMethodCaches.get(attributeName))
.map(AnnotationAttribute::getValue)
.get();
}
/**
* 获取该合成注解对应的根节点
*
* @return 合成注解对应的根节点
*/
@Override
public R getRoot() {
return root;
}
/**
* 获取被合成的注解对象
*
* @return 注解对象
*/
@Override
public T getAnnotation() {
return annotation;
}
/**
* 获取该合成注解与根对象的垂直距离。
* 默认情况下,该距离即为当前注解与根对象之间相隔的层级数。
*
* @return 合成注解与根对象的垂直距离
*/
@Override
public int getVerticalDistance() {
return verticalDistance;
}
/**
* 获取该合成注解与根对象的水平距离。
* 默认情况下,该距离即为当前注解与根对象之间相隔的已经被扫描到的注解数。
*
* @return 合成注解与根对象的水平距离
*/
@Override
public int getHorizontalDistance() {
return horizontalDistance;
}
/**
* 获取被合成的注解类型
*
* @return 被合成的注解类型
*/
@Override
public Class<? extends Annotation> annotationType() {
return annotation.annotationType();
}
/**
* 获取注解属性值
*
* @param attributeName 属性名称
* @param attributeType 属性类型
* @return 注解属性值
*/
@Override
public Object getAttributeValue(String attributeName, Class<?> attributeType) {
return Opt.ofNullable(attributeMethodCaches.get(attributeName))
.filter(method -> ClassUtil.isAssignable(attributeType, method.getAttributeType()))
.map(AnnotationAttribute::getValue)
.get();
}
}

View File

@@ -0,0 +1,155 @@
package cn.hutool.core.annotation;
import java.util.Comparator;
/**
* <p>描述以一个参照物为对象,存在于该参照物的层级结构中的对象。
*
* <p>该对象可通过{@link #getVerticalDistance()}与{@link #getHorizontalDistance()}
* 描述其在以参照物为基点的坐标坐标系中的位置。<br>
* 在需要对该接口的实现类进行按优先级排序时,距离{@link #getRoot()}对象越近,则该实现类的优先级越高。
* 默认提供了{@link #DEFAULT_HIERARCHICAL_COMPARATOR}用于实现该比较规则。<br>
* 一般情况下,{@link #getRoot()}返回值相同的对象之间的比较才有意义。
*
* <p>此外,还提供了{@link Selector}接口用于根据一定的规则从两个{@link Hierarchical}实现类中选择并返回一个最合适的对象,
* 默认提供了四个实现类:
* <ul>
* <li>{@link Selector#NEAREST_AND_OLDEST_PRIORITY}: 返回距离根对象更近的对象,当距离一样时优先返回旧对象;</li>
* <li>{@link Selector#NEAREST_AND_NEWEST_PRIORITY}: 返回距离根对象更近的对象,当距离一样时优先返回新对象;</li>
* <li>{@link Selector#FARTHEST_AND_OLDEST_PRIORITY}: 返回距离根对象更远的对象,当距离一样时优先返回旧对象;</li>
* <li>{@link Selector#FARTHEST_AND_NEWEST_PRIORITY}: 返回距离根对象更远的对象,当距离一样时优先返回新对象;</li>
* </ul>
*
* @author huangchengxing
*/
public interface Hierarchical extends Comparable<Hierarchical> {
// ====================== compare ======================
/**
* 默认{@link #getHorizontalDistance()}与{@link #getVerticalDistance()}排序的比较器
*/
Comparator<Hierarchical> DEFAULT_HIERARCHICAL_COMPARATOR = Comparator
.comparing(Hierarchical::getVerticalDistance)
.thenComparing(Hierarchical::getHorizontalDistance);
/**
* 按{@link #getVerticalDistance()}和{@link #getHorizontalDistance()}排序
*
* @param o {@link SynthesizedAnnotation}对象
* @return 比较值
*/
@Override
default int compareTo(Hierarchical o) {
return DEFAULT_HIERARCHICAL_COMPARATOR.compare(this, o);
}
// ====================== hierarchical ======================
/**
* 参照物,即坐标为{@code (0, 0)}的对象。
* 当对象本身即为参照物时,该方法应当返回其本身
*
* @return 参照物
*/
Object getRoot();
/**
* 获取该对象与参照物的垂直距离。
* 默认情况下,该距离即为当前对象与参照物之间相隔的层级数。
*
* @return 合成注解与根对象的垂直距离
*/
int getVerticalDistance();
/**
* 获取该对象与参照物的水平距离。
* 默认情况下,该距离即为当前对象在与参照物{@link #getVerticalDistance()}相同的情况下条,
* 该对象被扫描到的顺序。
*
* @return 合成注解与根对象的水平距离
*/
int getHorizontalDistance();
// ====================== selector ======================
/**
* {@link Hierarchical}选择器,用于根据一定的规则从两个{@link Hierarchical}实现类中选择并返回一个最合适的对象
*/
@FunctionalInterface
interface Selector {
/**
* 返回距离根对象更近的对象,当距离一样时优先返回旧对象
*/
Selector NEAREST_AND_OLDEST_PRIORITY = new NearestAndOldestPrioritySelector();
/**
* 返回距离根对象更近的对象,当距离一样时优先返回新对象
*/
Selector NEAREST_AND_NEWEST_PRIORITY = new NearestAndNewestPrioritySelector();
/**
* 返回距离根对象更远的对象,当距离一样时优先返回旧对象
*/
Selector FARTHEST_AND_OLDEST_PRIORITY = new FarthestAndOldestPrioritySelector();
/**
* 返回距离根对象更远的对象,当距离一样时优先返回新对象
*/
Selector FARTHEST_AND_NEWEST_PRIORITY = new FarthestAndNewestPrioritySelector();
/**
* 比较两个被合成的对象,选择其中的一个并返回
*
* @param <T> 复合注解类型
* @param prev 上一对象,该参数不允许为空
* @param next 下一对象,该参数不允许为空
* @return 对象
*/
<T extends Hierarchical> T choose(T prev, T next);
/**
* 返回距离根对象更近的注解,当距离一样时优先返回旧注解
*/
class NearestAndOldestPrioritySelector implements Selector {
@Override
public <T extends Hierarchical> T choose(T oldAnnotation, T newAnnotation) {
return newAnnotation.getVerticalDistance() < oldAnnotation.getVerticalDistance() ? newAnnotation : oldAnnotation;
}
}
/**
* 返回距离根对象更近的注解,当距离一样时优先返回新注解
*/
class NearestAndNewestPrioritySelector implements Selector {
@Override
public <T extends Hierarchical> T choose(T oldAnnotation, T newAnnotation) {
return newAnnotation.getVerticalDistance() <= oldAnnotation.getVerticalDistance() ? newAnnotation : oldAnnotation;
}
}
/**
* 返回距离根对象更远的注解,当距离一样时优先返回旧注解
*/
class FarthestAndOldestPrioritySelector implements Selector {
@Override
public <T extends Hierarchical> T choose(T oldAnnotation, T newAnnotation) {
return newAnnotation.getVerticalDistance() > oldAnnotation.getVerticalDistance() ? newAnnotation : oldAnnotation;
}
}
/**
* 返回距离根对象更远的注解,当距离一样时优先返回新注解
*/
class FarthestAndNewestPrioritySelector implements Selector {
@Override
public <T extends Hierarchical> T choose(T oldAnnotation, T newAnnotation) {
return newAnnotation.getVerticalDistance() >= oldAnnotation.getVerticalDistance() ? newAnnotation : oldAnnotation;
}
}
}
}

View File

@@ -0,0 +1,49 @@
package cn.hutool.core.annotation;
import java.lang.annotation.*;
/**
* <p>用于在同一注解中,或具有一定关联的不同注解的属性中,表明这些属性之间具有特定的关联关系。
* 在通过{@link SynthesizedAggregateAnnotation}获取合成注解后,合成注解获取属性值时会根据该注解进行调整。<br>
*
* <p>该注解存在三个字注解:{@link MirrorFor}、{@link ForceAliasFor}或{@link AliasFor}
* 使用三个子注解等同于{@link Link}。但是需要注意的是,
* 当注解中的属性同时存在多个{@link Link}或基于{@link Link}的子注解时,
* 仅有声明在被注解的属性最上方的注解会生效,其余注解都将被忽略。
*
* <b>注意:该注解的优先级低于{@link Alias}</b>
*
* @author huangchengxing
* @see SynthesizedAggregateAnnotation
* @see RelationType
* @see AliasFor
* @see MirrorFor
* @see ForceAliasFor
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
public @interface Link {
/**
* 产生关联的注解类型,当不指定时,默认指注释的属性所在的类
*
* @return 关联的注解类型
*/
Class<? extends Annotation> annotation() default Annotation.class;
/**
* {@link #annotation()}指定注解中关联的属性
*
* @return 属性名
*/
String attribute() default "";
/**
* {@link #attribute()}指定属性与当前注解的属性建的关联关系类型
*
* @return 关系类型
*/
RelationType type() default RelationType.MIRROR_FOR;
}

View File

@@ -0,0 +1,42 @@
package cn.hutool.core.annotation;
import java.lang.annotation.*;
/**
* <p>{@link Link}的子注解。表示注解的属性与指定的属性互为镜像,通过一个属性将能够获得对方的值。<br>
* 它们遵循下述规则:
* <ul>
* <li>互为镜像的两个属性,必须同时通过指定模式为{@code MIRROR_FOR}的{@link Link}注解指定对方;</li>
* <li>互为镜像的两个属性,类型必须一致;</li>
* <li>互为镜像的两个属性在获取值,且两者的值皆不同时,必须且仅允许有一个非默认值,该值被优先返回;</li>
* <li>互为镜像的两个属性,在值都为默认值或都不为默认值时,两者的值必须相等;</li>
* </ul>
* <b>注意,该注解与{@link Link}、{@link ForceAliasFor}或{@link AliasFor}一起使用时,将只有被声明在最上面的注解会生效</b>
*
* @author huangchengxing
* @see Link
* @see RelationType#MIRROR_FOR
*/
@Link(type = RelationType.MIRROR_FOR)
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
public @interface MirrorFor {
/**
* 产生关联的注解类型,当不指定时,默认指注释的属性所在的类
*
* @return 关联的注解类型
*/
@Link(annotation = Link.class, attribute = "annotation", type = RelationType.FORCE_ALIAS_FOR)
Class<? extends Annotation> annotation() default Annotation.class;
/**
* {@link #annotation()}指定注解中关联的属性
*
* @return 属性名
*/
@Link(annotation = Link.class, attribute = "attribute", type = RelationType.FORCE_ALIAS_FOR)
String attribute() default "";
}

View File

@@ -0,0 +1,132 @@
package cn.hutool.core.annotation;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ObjectUtil;
/**
* <p>用于处理注解对象中带有{@link Link}注解,且{@link Link#type()}为{@link RelationType#MIRROR_FOR}的属性。<br>
* 当该处理器执行完毕后,原始合成注解中被{@link Link}注解的属性与{@link Link}注解指向的目标注解的属性,
* 都将会被被包装并替换为{@link MirroredAnnotationAttribute}。
*
* @author huangchengxing
* @see RelationType#MIRROR_FOR
* @see MirroredAnnotationAttribute
*/
public class MirrorLinkAnnotationPostProcessor extends AbstractLinkAnnotationPostProcessor {
private static final RelationType[] PROCESSED_RELATION_TYPES = new RelationType[]{ RelationType.MIRROR_FOR };
@Override
public int order() {
return Integer.MIN_VALUE + 1;
}
/**
* 该处理器只处理{@link Link#type()}类型为{@link RelationType#MIRROR_FOR}的注解属性
*
* @return 仅有{@link RelationType#MIRROR_FOR}数组
*/
@Override
protected RelationType[] processTypes() {
return PROCESSED_RELATION_TYPES;
}
/**
* 将存在镜像关系的合成注解属性分别包装为{@link MirroredAnnotationAttribute}对象,
* 并使用包装后{@link MirroredAnnotationAttribute}替换在它们对应合成注解实例中的{@link AnnotationAttribute}
*
* @param synthesizer 注解合成器
* @param annotation {@code originalAttribute}上的{@link Link}注解对象
* @param originalAnnotation 当前正在处理的{@link SynthesizedAnnotation}对象
* @param originalAttribute {@code originalAnnotation}上的待处理的属性
* @param linkedAnnotation {@link Link}指向的关联注解对象
* @param linkedAttribute {@link Link}指向的{@code originalAnnotation}中的关联属性,该参数可能为空
*/
@Override
protected void processLinkedAttribute(
AnnotationSynthesizer synthesizer, Link annotation,
SynthesizedAnnotation originalAnnotation, AnnotationAttribute originalAttribute,
SynthesizedAnnotation linkedAnnotation, AnnotationAttribute linkedAttribute) {
// 镜像属性必然成对出现,因此此处必定存在三种情况:
// 1.两属性都不为镜像属性,此时继续进行后续处理;
// 2.两属性都为镜像属性,并且指向对方,此时无需后续处理;
// 3.两属性仅有任意一属性为镜像属性,此时镜像属性必然未指向当前原始属性,此时应该抛出异常;
if (originalAttribute instanceof MirroredAnnotationAttribute
|| linkedAttribute instanceof MirroredAnnotationAttribute) {
checkMirrored(originalAttribute, linkedAttribute);
return;
}
// 校验镜像关系
checkMirrorRelation(annotation, originalAttribute, linkedAttribute);
// 包装这一对镜像属性,并替换原注解中的对应属性
final AnnotationAttribute mirroredOriginalAttribute = new MirroredAnnotationAttribute(originalAttribute, linkedAttribute);
originalAnnotation.setAttribute(originalAttribute.getAttributeName(), mirroredOriginalAttribute);
final AnnotationAttribute mirroredTargetAttribute = new MirroredAnnotationAttribute(linkedAttribute, originalAttribute);
linkedAnnotation.setAttribute(annotation.attribute(), mirroredTargetAttribute);
}
/**
* 检查映射关系是否正确
*/
private void checkMirrored(AnnotationAttribute original, AnnotationAttribute mirror) {
final boolean originalAttributeMirrored = original instanceof MirroredAnnotationAttribute;
final boolean mirrorAttributeMirrored = mirror instanceof MirroredAnnotationAttribute;
// 校验通过
final boolean passed = originalAttributeMirrored && mirrorAttributeMirrored
&& ObjectUtil.equals(((MirroredAnnotationAttribute)original).getLinked(), ((MirroredAnnotationAttribute)mirror).getOriginal());
if (passed) {
return;
}
// 校验失败,拼装异常信息用于抛出异常
String errorMsg;
// 原始字段已经跟其他字段形成镜像
if (originalAttributeMirrored && !mirrorAttributeMirrored) {
errorMsg = CharSequenceUtil.format(
"attribute [{}] cannot mirror for [{}], because it's already mirrored for [{}]",
original.getAttribute(), mirror.getAttribute(), ((MirroredAnnotationAttribute)original).getLinked()
);
}
// 镜像字段已经跟其他字段形成镜像
else if (!originalAttributeMirrored && mirrorAttributeMirrored) {
errorMsg = CharSequenceUtil.format(
"attribute [{}] cannot mirror for [{}], because it's already mirrored for [{}]",
mirror.getAttribute(), original.getAttribute(), ((MirroredAnnotationAttribute)mirror).getLinked()
);
}
// 两者都形成了镜像,但是都未指向对方,理论上不会存在该情况
else {
errorMsg = CharSequenceUtil.format(
"attribute [{}] cannot mirror for [{}], because [{}] already mirrored for [{}] and [{}] already mirrored for [{}]",
mirror.getAttribute(), original.getAttribute(),
mirror.getAttribute(), ((MirroredAnnotationAttribute)mirror).getLinked(),
original.getAttribute(), ((MirroredAnnotationAttribute)original).getLinked()
);
}
throw new IllegalArgumentException(errorMsg);
}
/**
* 基本校验
*/
private void checkMirrorRelation(Link annotation, AnnotationAttribute original, AnnotationAttribute mirror) {
// 镜像属性必须存在
checkLinkedAttributeNotNull(original, mirror, annotation);
// 镜像属性返回值必须一致
checkAttributeType(original, mirror);
// 镜像属性上必须存在对应的注解
final Link mirrorAttributeAnnotation = getLinkAnnotation(mirror, RelationType.MIRROR_FOR);
Assert.isTrue(
ObjectUtil.isNotNull(mirrorAttributeAnnotation) && RelationType.MIRROR_FOR.equals(mirrorAttributeAnnotation.type()),
"mirror attribute [{}] of original attribute [{}] must marked by @Link, and also @LinkType.type() must is [{}]",
mirror.getAttribute(), original.getAttribute(), RelationType.MIRROR_FOR
);
checkLinkedSelf(original, mirror);
}
}

View File

@@ -0,0 +1,48 @@
package cn.hutool.core.annotation;
import cn.hutool.core.lang.Assert;
/**
* 表示存在对应镜像属性的注解属性,当获取值时将根据{@link RelationType#MIRROR_FOR}的规则进行处理
*
* @author huangchengxing
* @see MirrorLinkAnnotationPostProcessor
* @see RelationType#MIRROR_FOR
*/
public class MirroredAnnotationAttribute extends AbstractWrappedAnnotationAttribute {
public MirroredAnnotationAttribute(AnnotationAttribute origin, AnnotationAttribute linked) {
super(origin, linked);
}
@Override
public Object getValue() {
final boolean originIsDefault = original.isValueEquivalentToDefaultValue();
final boolean targetIsDefault = linked.isValueEquivalentToDefaultValue();
final Object originValue = original.getValue();
final Object targetValue = linked.getValue();
// 都为默认值,或都为非默认值时,两方法的返回值必须相等
if (originIsDefault == targetIsDefault) {
Assert.equals(
originValue, targetValue,
"the values of attributes [{}] and [{}] that mirror each other are different: [{}] <==> [{}]",
original.getAttribute(), linked.getAttribute(), originValue, targetValue
);
return originValue;
}
// 两者有一者不为默认值时,优先返回非默认值
return originIsDefault ? targetValue : originValue;
}
/**
* 当{@link #original}与{@link #linked}都为默认值时返回{@code true}
*
* @return 是否
*/
@Override
public boolean isValueEquivalentToDefaultValue() {
return original.isValueEquivalentToDefaultValue() && linked.isValueEquivalentToDefaultValue();
}
}

View File

@@ -0,0 +1,50 @@
package cn.hutool.core.annotation;
/**
* <p>注解属性的关系类型 <br>
* 若将被{@link Link}注解的属性称为“原始属性”,而在{@link Link}注解中指向的注解属性称为“关联属性”,
* 则该枚举用于描述“原始属性”与“关联属性”在{@link SynthesizedAggregateAnnotation}处理过程中的作用关系。<br>
* 根据在{@link Link#type()}中指定的关系类型的不同,通过{@link SynthesizedAggregateAnnotation}合成的注解的属性值也将有所变化。
*
* <p>当一个注解中的所有属性同时具备多种关系时,将依次按下述顺序处理:
* <ol>
* <li>属性上的{@link Alias}注解;</li>
* <li>属性上的{@link Link}注解,且{@link Link#type()}为{@link #MIRROR_FOR}</li>
* <li>属性上的{@link Link}注解,且{@link Link#type()}为{@link #FORCE_ALIAS_FOR}</li>
* <li>属性上的{@link Link}注解,且{@link Link#type()}为{@link #ALIAS_FOR}</li>
* </ol>
*
* @author huangchengxing
* @see SynthesizedAggregateAnnotation
* @see Link
*/
public enum RelationType {
/**
* <p>表示注解的属性与指定的属性互为镜像,通过一个属性将能够获得对方的值。<br>
* 它们遵循下述规则:
* <ul>
* <li>互为镜像的两个属性,必须同时通过指定模式为{@code MIRROR_FOR}的{@link Link}注解指定对方;</li>
* <li>互为镜像的两个属性,类型必须一致;</li>
* <li>互为镜像的两个属性在获取值,且两者的值皆不同时,必须且仅允许有一个非默认值,该值被优先返回;</li>
* <li>互为镜像的两个属性,在值都为默认值或都不为默认值时,两者的值必须相等;</li>
* </ul>
*/
MIRROR_FOR,
/**
* <p>表示“原始属性”将作为“关联属性”的别名。
* <ul>
* <li>当“原始属性”为默认值时,获取“关联属性”将返回“关联属性”本身的值;</li>
* <li>当“原始属性”不为默认值时,获取“关联属性”将返回“原始属性”的值;</li>
* </ul>
*/
ALIAS_FOR,
/**
* <p>表示“原始属性”将强制作为“关联属性”的别名。效果等同于在“原始属性”上添加{@link Alias}注解,
* 任何情况下,获取“关联属性”的值都将直接返回“原始属性”的值
*/
FORCE_ALIAS_FOR;
}

View File

@@ -0,0 +1,102 @@
package cn.hutool.core.annotation;
import java.lang.annotation.Annotation;
/**
* <p>表示基于特定规则聚合,将一组注解聚合而来的注解对象,
* 该注解对象允许根据一定规则“合成”一些跟原始注解属性不一样合成注解。
*
* <p>合成注解一般被用于处理类层级结果中具有直接或间接关联的注解对象,
* 当实例被创建时,会获取到这些注解对象,并使用{@link SynthesizedAnnotationSelector}对类型相同的注解进行过滤,
* 并最终得到类型不重复的有效注解对象。这些有效注解将被包装为{@link SynthesizedAnnotation}
* 然后最终用于“合成”一个{@link SynthesizedAggregateAnnotation}。<br>
* {@link SynthesizedAnnotationSelector}是合成注解生命周期中的第一个钩子,
* 自定义选择器以拦截原始注解被扫描的过程。
*
* <p>当合成注解完成对待合成注解的扫描,并完成了必要属性的加载后,
* 将会按顺序依次调用{@link SynthesizedAnnotationPostProcessor}
* 注解后置处理器允许用于对完成注解的待合成注解进行二次调整,
* 该钩子一般用于根据{@link Link}注解对属性进行调整。<br>
* {@link SynthesizedAnnotationPostProcessor}是合成注解生命周期中的第二个钩子,
* 自定义后置处理器以拦截原始在转为待合成注解后的初始化过程。
*
* <p>合成注解允许通过{@link #synthesize(Class)}合成一个指定的注解对象,
* 该方法返回的注解对象可能是原始的注解对象,也有可能通过动态代理的方式生成,
* 该对象实例的属性不一定来自对象本身,而是来自于经过{@link SynthesizedAnnotationAttributeProcessor}
* 处理后的、用于合成当前实例的全部关联注解的相关属性。<br>
* {@link SynthesizedAnnotationAttributeProcessor}是合成注解生命周期中的第三个钩子,
* 自定义属性处理器以拦截合成注解的取值过程。
*
* @author huangchengxing
* @see AnnotationSynthesizer
* @see SynthesizedAnnotation
* @see SynthesizedAnnotationSelector
* @see SynthesizedAnnotationAttributeProcessor
* @see SynthesizedAnnotationPostProcessor
* @see GenericSynthesizedAggregateAnnotation
*/
public interface SynthesizedAggregateAnnotation extends AggregateAnnotation, Hierarchical, AnnotationSynthesizer, AnnotationAttributeValueProvider {
// ================== hierarchical ==================
/**
* 距离{@link #getRoot()}返回值的垂直距离,
* 默认聚合注解即为根对象因此返回0
*
* @return 距离{@link #getRoot()}返回值的水平距离,
*/
@Override
default int getVerticalDistance() {
return 0;
}
/**
* 距离{@link #getRoot()}返回值的水平距离,
* 默认聚合注解即为根对象因此返回0
*
* @return 距离{@link #getRoot()}返回值的水平距离,
*/
@Override
default int getHorizontalDistance() {
return 0;
}
// ================== synthesize ==================
/**
* 获取在聚合中存在的指定注解对象
*
* @param annotationType 注解类型
* @param <T> 注解类型
* @return 注解对象
*/
<T extends Annotation> T getAnnotation(Class<T> annotationType);
/**
* 获取合成注解属性处理器
*
* @return 合成注解属性处理器
*/
SynthesizedAnnotationAttributeProcessor getAnnotationAttributeProcessor();
/**
* 获取当前的注解类型
*
* @return 注解类型
*/
@Override
default Class<? extends Annotation> annotationType() {
return this.getClass();
}
/**
* 从聚合中获取指定类型的属性值
*
* @param attributeName 属性名称
* @param attributeType 属性类型
* @return 属性值
*/
@Override
Object getAttributeValue(String attributeName, Class<?> attributeType);
}

View File

@@ -1,30 +1,21 @@
package cn.hutool.core.annotation;
import cn.hutool.core.collection.CollUtil;
import java.lang.annotation.Annotation;
import java.util.Map;
import java.util.function.UnaryOperator;
/**
* 用于在{@link SyntheticAnnotation}中表示一个处于合成状态的注解对象
* <p>用于在{@link SynthesizedAggregateAnnotation}中表示一个处于合成状态的注解对象。<br>
* 当对多个合成注解排序时,默认使用{@link #DEFAULT_HIERARCHICAL_COMPARATOR}进行排序,
* 从保证合成注解按{@link #getVerticalDistance()}与{@link #getHorizontalDistance()}的返回值保持有序,
* 从而使得距离根元素更接近的注解对象在被处理是具有更高的优先级。
*
* @author huangchengxing
* @see SyntheticAnnotation
* @see SynthesizedAggregateAnnotation
*/
public interface SynthesizedAnnotation extends Annotation {
/**
* 获取该合成注解对应的根节点
*
* @return 合成注解对应的根节点
*/
Object getRoot();
/**
* 该合成注解是为根对象
*
* @return 根对象
*/
default boolean isRoot() {
return getRoot() == this;
}
public interface SynthesizedAnnotation extends Annotation, Hierarchical, AnnotationAttributeValueProvider {
/**
* 获取被合成的注解对象
@@ -39,6 +30,7 @@ public interface SynthesizedAnnotation extends Annotation {
*
* @return 合成注解与根对象的垂直距离
*/
@Override
int getVerticalDistance();
/**
@@ -47,6 +39,7 @@ public interface SynthesizedAnnotation extends Annotation {
*
* @return 合成注解与根对象的水平距离
*/
@Override
int getHorizontalDistance();
/**
@@ -58,12 +51,46 @@ public interface SynthesizedAnnotation extends Annotation {
*/
boolean hasAttribute(String attributeName, Class<?> returnType);
/**
* 获取该注解的全部属性
*
* @return 注解属性
*/
Map<String, AnnotationAttribute> getAttributes();
/**
* 设置该注解的全部属性
*
* @param attributes 注解属性
*/
default void setAttributes(Map<String, AnnotationAttribute> attributes) {
if (CollUtil.isNotEmpty(attributes)) {
attributes.forEach(this::setAttribute);
}
}
/**
* 设置属性值
*
* @param attributeName 属性名称
* @param attribute 注解属性
*/
void setAttribute(String attributeName, AnnotationAttribute attribute);
/**
* 替换属性值
*
* @param attributeName 属性名
* @param operator 替换操作
*/
void replaceAttribute(String attributeName, UnaryOperator<AnnotationAttribute> operator);
/**
* 获取属性值
*
* @param attributeName 属性名
* @return 属性值
*/
Object getAttribute(String attributeName);
Object getAttributeValue(String attributeName);
}

View File

@@ -3,7 +3,7 @@ package cn.hutool.core.annotation;
import java.util.Collection;
/**
* 合成注解属性选择器。用于在{@link SyntheticAnnotation}中从指定类型的合成注解里获取到对应的属性值
* 合成注解属性选择器。用于在{@link SynthesizedAggregateAnnotation}中从指定类型的合成注解里获取到对应的属性值
*
* @author huangchengxing
*/

View File

@@ -0,0 +1,71 @@
package cn.hutool.core.annotation;
import cn.hutool.core.comparator.CompareUtil;
import java.util.Comparator;
/**
* <p>被合成注解后置处理器,用于在{@link SynthesizedAggregateAnnotation}加载完所有待合成注解后,
* 再对加载好的{@link SynthesizedAnnotation}进行后置处理。<br>
* 当多个{@link SynthesizedAnnotationPostProcessor}需要一起执行时,将按照{@link #order()}的返回值进行排序,
* 该值更小的处理器将被优先执行。
*
* <p>该接口存在多个实现类,调用者应当保证在任何时候,对一批后置处理器的调用顺序都符合:
* <ul>
* <li>{@link AliasAnnotationPostProcessor}</li>
* <li>{@link MirrorLinkAnnotationPostProcessor}</li>
* <li>{@link AliasLinkAnnotationPostProcessor}</li>
* <li>其他后置处理器;</li>
* </ul>
*
* @author huangchengxing
* @see AliasAnnotationPostProcessor
* @see MirrorLinkAnnotationPostProcessor
* @see AliasLinkAnnotationPostProcessor
*/
public interface SynthesizedAnnotationPostProcessor extends Comparable<SynthesizedAnnotationPostProcessor> {
/**
* 属性上带有{@link Alias}的注解对象的后置处理器
*/
AliasAnnotationPostProcessor ALIAS_ANNOTATION_POST_PROCESSOR = new AliasAnnotationPostProcessor();
/**
* 属性上带有{@link Link},且与其他注解的属性存在镜像关系的注解对象的后置处理器
*/
MirrorLinkAnnotationPostProcessor MIRROR_LINK_ANNOTATION_POST_PROCESSOR = new MirrorLinkAnnotationPostProcessor();
/**
* 属性上带有{@link Link},且与其他注解的属性存在别名关系的注解对象的后置处理器
*/
AliasLinkAnnotationPostProcessor ALIAS_LINK_ANNOTATION_POST_PROCESSOR = new AliasLinkAnnotationPostProcessor();
/**
* 在一组后置处理器中被调用的顺序,越小越靠前
*
* @return 排序值
*/
default int order() {
return Integer.MAX_VALUE;
}
/**
* 比较两个后置处理器的{@link #order()}返回值
*
* @param o 比较对象
* @return 大小
*/
@Override
default int compareTo(SynthesizedAnnotationPostProcessor o) {
return CompareUtil.compare(this, o, Comparator.comparing(SynthesizedAnnotationPostProcessor::order));
}
/**
* 给定指定被合成注解与其所属的合成注解聚合器实例,经过处理后返回最终
*
* @param synthesizedAnnotation 合成的注解
* @param synthesizer 注解合成器
*/
void process(SynthesizedAnnotation synthesizedAnnotation, AnnotationSynthesizer synthesizer);
}

View File

@@ -0,0 +1,160 @@
package cn.hutool.core.annotation;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 合成注解代理类,用于为{@link SynthesizedAnnotation}生成对应的合成注解代理对象
*
* @author huangchengxing
* @see SynthesizedAnnotation
* @see AnnotationAttributeValueProvider
*/
public class SynthesizedAnnotationProxy implements InvocationHandler {
private final AnnotationAttributeValueProvider annotationAttributeValueProvider;
private final SynthesizedAnnotation annotation;
private final Map<String, BiFunction<Method, Object[], Object>> methods;
/**
* 创建一个代理注解,生成的代理对象将是{@link SyntheticProxyAnnotation}与指定的注解类的子类。
*
* @param <T> 注解类型
* @param annotationType 注解类型
* @param annotationAttributeValueProvider 注解属性值获取器
* @param annotation 合成注解
* @return 代理注解
*/
@SuppressWarnings("unchecked")
public static <T extends Annotation> T create(
Class<T> annotationType,
AnnotationAttributeValueProvider annotationAttributeValueProvider,
SynthesizedAnnotation annotation) {
if (ObjectUtil.isNull(annotation)) {
return null;
}
final SynthesizedAnnotationProxy proxyHandler = new SynthesizedAnnotationProxy(annotationAttributeValueProvider, annotation);
if (ObjectUtil.isNull(annotation)) {
return null;
}
return (T) Proxy.newProxyInstance(
annotationType.getClassLoader(),
new Class[]{annotationType, SyntheticProxyAnnotation.class},
proxyHandler
);
}
/**
* 创建一个代理注解,生成的代理对象将是{@link SyntheticProxyAnnotation}与指定的注解类的子类。
*
* @param <T> 注解类型
* @param annotationType 注解类型
* @param annotation 合成注解
* @return 代理注解
*/
public static <T extends Annotation> T create(
Class<T> annotationType, SynthesizedAnnotation annotation) {
return create(annotationType, annotation, annotation);
}
/**
* 该类是否为通过{@code SynthesizedAnnotationProxy}生成的代理类
*
* @param annotationType 注解类型
* @return 是否
*/
public static boolean isProxyAnnotation(Class<?> annotationType) {
return ClassUtil.isAssignable(SyntheticProxyAnnotation.class, annotationType);
}
SynthesizedAnnotationProxy(AnnotationAttributeValueProvider annotationAttributeValueProvider, SynthesizedAnnotation annotation) {
Assert.notNull(annotationAttributeValueProvider, "annotationAttributeValueProvider must not null");
Assert.notNull(annotation, "annotation must not null");
this.annotationAttributeValueProvider = annotationAttributeValueProvider;
this.annotation = annotation;
this.methods = new HashMap<>(9);
loadMethods();
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return Opt.ofNullable(methods.get(method.getName()))
.map(m -> m.apply(method, args))
.orElseGet(() -> ReflectUtil.invoke(this, method, args));
}
// ========================= 代理方法 =========================
void loadMethods() {
methods.put("toString", (method, args) -> proxyToString());
methods.put("hashCode", (method, args) -> proxyHashCode());
methods.put("getSynthesizedAnnotation", (method, args) -> proxyGetSynthesizedAnnotation());
methods.put("getRoot", (method, args) -> annotation.getRoot());
methods.put("getVerticalDistance", (method, args) -> annotation.getVerticalDistance());
methods.put("getHorizontalDistance", (method, args) -> annotation.getHorizontalDistance());
methods.put("hasAttribute", (method, args) -> annotation.hasAttribute((String) args[0], (Class<?>) args[1]));
methods.put("getAttributes", (method, args) -> annotation.getAttributes());
methods.put("setAttribute", (method, args) -> {
throw new UnsupportedOperationException("proxied annotation can not reset attributes");
});
methods.put("getAttributeValue", (method, args) -> annotation.getAttributeValue((String) args[0]));
methods.put("annotationType", (method, args) -> annotation.annotationType());
for (final Method declaredMethod : ClassUtil.getDeclaredMethods(annotation.getAnnotation().annotationType())) {
methods.put(declaredMethod.getName(), (method, args) -> proxyAttributeValue(method));
}
}
private String proxyToString() {
final String attributes = Stream.of(ClassUtil.getDeclaredMethods(annotation.getAnnotation().annotationType()))
.filter(AnnotationUtil::isAttributeMethod)
.map(method -> CharSequenceUtil.format(
"{}={}", method.getName(), proxyAttributeValue(method))
)
.collect(Collectors.joining(", "));
return CharSequenceUtil.format("@{}({})", annotation.annotationType().getName(), attributes);
}
private int proxyHashCode() {
return Objects.hash(annotationAttributeValueProvider, annotation);
}
private Object proxyGetSynthesizedAnnotation() {
return annotation;
}
private Object proxyAttributeValue(Method attributeMethod) {
return annotationAttributeValueProvider.getAttributeValue(attributeMethod.getName(), attributeMethod.getReturnType());
}
/**
* 通过代理类生成的合成注解
*
* @author huangchengxing
*/
interface SyntheticProxyAnnotation extends SynthesizedAnnotation {
/**
* 获取该代理注解对应的已合成注解
*
* @return 理注解对应的已合成注解
*/
SynthesizedAnnotation getSynthesizedAnnotation();
}
}

View File

@@ -2,7 +2,7 @@ package cn.hutool.core.annotation;
/**
* 注解选择器,指定两个注解,选择其中一个返回。<br>
* 该接口用于在{@link SyntheticAnnotation}中用于从一批相同的注解对象中筛选最终用于合成注解对象。
* 该接口用于在{@link SynthesizedAggregateAnnotation}中用于从一批相同的注解对象中筛选最终用于合成注解对象。
*
* @author huangchengxing
*/
@@ -45,7 +45,7 @@ public interface SynthesizedAnnotationSelector {
class NearestAndOldestPrioritySelector implements SynthesizedAnnotationSelector {
@Override
public <T extends SynthesizedAnnotation> T choose(T oldAnnotation, T newAnnotation) {
return newAnnotation.getVerticalDistance() < oldAnnotation.getVerticalDistance() ? newAnnotation : oldAnnotation;
return Hierarchical.Selector.NEAREST_AND_OLDEST_PRIORITY.choose(oldAnnotation, newAnnotation);
}
}
@@ -55,7 +55,7 @@ public interface SynthesizedAnnotationSelector {
class NearestAndNewestPrioritySelector implements SynthesizedAnnotationSelector {
@Override
public <T extends SynthesizedAnnotation> T choose(T oldAnnotation, T newAnnotation) {
return newAnnotation.getVerticalDistance() <= oldAnnotation.getVerticalDistance() ? newAnnotation : oldAnnotation;
return Hierarchical.Selector.NEAREST_AND_NEWEST_PRIORITY.choose(oldAnnotation, newAnnotation);
}
}
@@ -65,7 +65,7 @@ public interface SynthesizedAnnotationSelector {
class FarthestAndOldestPrioritySelector implements SynthesizedAnnotationSelector {
@Override
public <T extends SynthesizedAnnotation> T choose(T oldAnnotation, T newAnnotation) {
return newAnnotation.getVerticalDistance() > oldAnnotation.getVerticalDistance() ? newAnnotation : oldAnnotation;
return Hierarchical.Selector.FARTHEST_AND_OLDEST_PRIORITY.choose(oldAnnotation, newAnnotation);
}
}
@@ -75,7 +75,7 @@ public interface SynthesizedAnnotationSelector {
class FarthestAndNewestPrioritySelector implements SynthesizedAnnotationSelector {
@Override
public <T extends SynthesizedAnnotation> T choose(T oldAnnotation, T newAnnotation) {
return newAnnotation.getVerticalDistance() >= oldAnnotation.getVerticalDistance() ? newAnnotation : oldAnnotation;
return Hierarchical.Selector.FARTHEST_AND_NEWEST_PRIORITY.choose(oldAnnotation, newAnnotation);
}
}

View File

@@ -1,119 +0,0 @@
package cn.hutool.core.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
/**
* 表示基于特定规则聚合的一组注解对象
*
* <p>合成注解一般被用于处理类层级结果中具有直接或间接关联的注解对象,
* 当实例被创建时,会获取到这些注解对象,并使用{@link SynthesizedAnnotationSelector}对类型相同的注解进行过滤,
* 并最终得到类型不重复的有效注解对象。这些有效注解将被包装为{@link SynthesizedAnnotation}
* 然后最终用于“合成”一个{@link SynthesizedAnnotation}。
*
* <p>合成注解可以作为一个特殊的{@link Annotation}或者{@link AnnotatedElement}
* 当调用{@link Annotation}的方法时,应当返回当前实例本身的有效信息,
* 而当调用{@link AnnotatedElement}的方法时,应当返回用于合成该对象的相关注解的信息。
*
* <p>合成注解允许通过{@link #syntheticAnnotation(Class)}合成一个指定的注解对象,
* 该方法返回的注解对象可能是原始的注解对象,也有可能通过动态代理的方式生成,
* 该对象实例的属性不一定来自对象本身,而是来自于经过{@link SynthesizedAnnotationAttributeProcessor}
* 处理后的、用于合成当前实例的全部关联注解的相关属性。
*
* @author huangchengxing
* @see SynthesizedAnnotation
* @see SynthesizedAnnotationSelector
* @see SynthesizedAnnotationAttributeProcessor
* @see SyntheticMetaAnnotation
*/
public interface SyntheticAnnotation extends Annotation, AnnotatedElement {
/**
* 获取合成注解选择器
*
* @return 合成注解选择器
*/
SynthesizedAnnotationSelector getAnnotationSelector();
/**
* 获取合成注解属性处理器
*
* @return 合成注解属性处理器
*/
SynthesizedAnnotationAttributeProcessor getAttributeProcessor();
/**
* 获取已合成的注解
*
* @param annotationType 注解类型
* @return 已合成的注解
*/
SynthesizedAnnotation getSynthesizedAnnotation(Class<?> annotationType);
/**
* 获取当前的注解类型
*
* @return 注解类型
*/
@Override
default Class<? extends Annotation> annotationType() {
return this.getClass();
}
/**
* 获取指定注解对象
*
* @param annotationType 注解类型
* @param <T> 注解类型
* @return 注解对象
*/
@Override
<T extends Annotation> T getAnnotation(Class<T> annotationType);
/**
* 是否存在指定注解
*
* @param annotationType 注解类型
* @return 是否
*/
@Override
boolean isAnnotationPresent(Class<? extends Annotation> annotationType);
/**
* 获取全部注解
*
* @return 注解对象
*/
@Override
Annotation[] getAnnotations();
/**
* 获取合成注解
*
* @param annotationType 注解类型
* @param <T> 注解类型
* @return 类型
*/
<T extends Annotation> T syntheticAnnotation(Class<T> annotationType);
/**
* 获取属性值
*
* @param attributeName 属性名称
* @param attributeType 属性类型
* @return 属性值
*/
Object getAttribute(String attributeName, Class<?> attributeType);
/**
* 基于指定根注解,构建包括其元注解在内的合成注解
*
* @param rootAnnotation 根注解
* @param <T> 注解类型
* @return 合成注解
*/
static <T extends Annotation> SyntheticAnnotation of(T rootAnnotation) {
return new SyntheticMetaAnnotation(rootAnnotation);
}
}

View File

@@ -1,141 +0,0 @@
package cn.hutool.core.annotation;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 合成注解代理类
*
* @author huangchengxing
*/
class SyntheticAnnotationProxy implements InvocationHandler {
private final SyntheticAnnotation syntheticAnnotation;
private final SynthesizedAnnotation annotation;
private final Map<String, BiFunction<Method, Object[], Object>> methods;
SyntheticAnnotationProxy(SyntheticAnnotation syntheticAnnotation, SynthesizedAnnotation annotation) {
this.syntheticAnnotation = syntheticAnnotation;
this.annotation = annotation;
this.methods = new HashMap<>(9);
loadMethods();
}
/**
* 创建一个代理注解,生成的代理对象将是{@link SyntheticProxyAnnotation}与指定的注解类的子类。
* <ul>
* <li>当作为{@code annotationType}所指定的类型使用时,其属性将通过合成它的{@link SyntheticAnnotation}获取;</li>
* <li>当作为{@link SyntheticProxyAnnotation}或{@link SynthesizedAnnotation}使用时,将可以获得原始注解实例的相关信息;</li>
* </ul>
*
* @param annotationType 注解类型
* @param syntheticAnnotation 合成注解
* @return 代理注解
*/
@SuppressWarnings("unchecked")
static <T extends Annotation> T create(
Class<T> annotationType, SyntheticAnnotation syntheticAnnotation) {
final SynthesizedAnnotation annotation = syntheticAnnotation.getSynthesizedAnnotation(annotationType);
final SyntheticAnnotationProxy proxyHandler = new SyntheticAnnotationProxy(syntheticAnnotation, annotation);
if (ObjectUtil.isNull(annotation)) {
return null;
}
return (T) Proxy.newProxyInstance(
annotationType.getClassLoader(),
new Class[]{annotationType, SyntheticProxyAnnotation.class},
proxyHandler
);
}
static boolean isProxyAnnotation(Class<?> targetClass) {
return ClassUtil.isAssignable(SyntheticProxyAnnotation.class, targetClass);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return Opt.ofNullable(methods.get(method.getName()))
.map(m -> m.apply(method, args))
.orElseGet(() -> ReflectUtil.invoke(this, method, args));
}
// ========================= 代理方法 =========================
void loadMethods() {
methods.put("toString", (method, args) -> proxyToString());
methods.put("hashCode", (method, args) -> proxyHashCode());
methods.put("getSyntheticAnnotation", (method, args) -> proxyGetSyntheticAnnotation());
methods.put("getSynthesizedAnnotation", (method, args) -> proxyGetSynthesizedAnnotation());
methods.put("getRoot", (method, args) -> annotation.getRoot());
methods.put("isRoot", (method, args) -> annotation.isRoot());
methods.put("getVerticalDistance", (method, args) -> annotation.getVerticalDistance());
methods.put("getHorizontalDistance", (method, args) -> annotation.getHorizontalDistance());
methods.put("hasAttribute", (method, args) -> annotation.hasAttribute((String)args[0], (Class<?>)args[1]));
methods.put("getAttribute", (method, args) -> annotation.getAttribute((String)args[0]));
methods.put("annotationType", (method, args) -> annotation.annotationType());
for (final Method declaredMethod : annotation.getAnnotation().annotationType().getDeclaredMethods()) {
methods.put(declaredMethod.getName(), (method, args) -> proxyAttributeValue(method));
}
}
private String proxyToString() {
final String attributes = Stream.of(annotation.annotationType().getDeclaredMethods())
.filter(AnnotationUtil::isAttributeMethod)
.map(method -> StrUtil.format("{}={}", method.getName(), syntheticAnnotation.getAttribute(method.getName(), method.getReturnType())))
.collect(Collectors.joining(", "));
return StrUtil.format("@{}({})", annotation.annotationType().getName(), attributes);
}
private int proxyHashCode() {
return Objects.hash(syntheticAnnotation, annotation);
}
private Object proxyGetSyntheticAnnotation() {
return syntheticAnnotation;
}
private Object proxyGetSynthesizedAnnotation() {
return annotation;
}
private Object proxyAttributeValue(Method attributeMethod) {
return syntheticAnnotation.getAttribute(attributeMethod.getName(), attributeMethod.getReturnType());
}
/**
* 通过代理类生成的合成注解
*
* @author huangchengxing
*/
interface SyntheticProxyAnnotation extends SynthesizedAnnotation {
/**
* 获取该注解所属的合成注解
*
* @return 合成注解
*/
SyntheticAnnotation getSyntheticAnnotation();
/**
* 获取该代理注解对应的已合成注解
*
* @return 理注解对应的已合成注解
*/
SynthesizedAnnotation getSynthesizedAnnotation();
}
}

View File

@@ -1,369 +0,0 @@
package cn.hutool.core.annotation;
import cn.hutool.core.annotation.scanner.MetaAnnotationScanner;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* 表示一个根注解与根注解上的多层元注解合成的注解
*
* <p>假设现有注解AA上存在元注解BB上存在元注解C则对A解析得到的合成注解X则CBA都是X的元注解X为根注解。<br>
* 通过{@link #isAnnotationPresent(Class)}可确定指定类型是注解是否是该合成注解的元注解,即是否为当前实例的“父类”。
* 若指定注解是当前实例的元注解,则通过{@link #getAnnotation(Class)}可获得动态代理生成的对应的注解实例。<br>
* 需要注意的是由于认为合并注解X以最初的根注解A作为元注解因此{@link #getAnnotations()}或{@link #getDeclaredAnnotations()}
* 都将只能获得A。
*
* <p>若认为该合成注解X在第0层则根注解A在第1层B在第2层......以此类推,
* 则相同或不同的层级中可能会出现类型相同的注解对象,此时将通过{@link SynthesizedAnnotationSelector}选择出最合适的注解对象,
* 该注解对象将在合成注解中作为唯一有效的元注解用于进行相关操作。<br>
* 默认情况下,将选择{@link SynthesizedAnnotationSelector#NEAREST_AND_OLDEST_PRIORITY}选择器实例,
* 即层级越低的注解离根注解距离近,则该注解优先级越高,即遵循“就近原则”。
*
* <p>合成注解中获取到的注解中可能会具有一些同名且同类型的属性,
* 此时将根据{@link SynthesizedAnnotationAttributeProcessor}决定如何从这些注解的相同属性中获取属性值。<br>
* 默认情况下,将选择{@link CacheableSynthesizedAnnotationAttributeProcessor}用于获取属性,
* 该处理器将选择距离根注解最近的注解中的属性用于获取属性值,{@link #getAnnotation(Class)}获得的代理类实例的属性值遵循该规则。<br>
* 举个例子若CBA同时存在属性y则将X视为CB或者A时获得的y属性的值都与最底层元注解A的值保持一致。
* 若两相同注解处于同一层级,则按照从其上一级“子注解”的{@link AnnotatedElement#getAnnotations()}的调用顺序排序。
*
* <p>别名在合成注解中仍然有效若注解X中任意属性上存在{@link Alias}注解,则{@link Alias#value()}指定的属性值将会覆盖注解属性的本身的值。<br>
* {@link Alias}注解仅能指定注解X中存在的属性作为别名不允许指定元注解或子类注解的属性。
*
* @author huangchengxing
* @see AnnotationUtil
* @see SynthesizedAnnotationSelector
*/
public class SyntheticMetaAnnotation implements SyntheticAnnotation {
/**
* 根注解,即当前查找的注解
*/
private final Annotation source;
/**
* 包含根注解以及其元注解在内的全部注解实例
*/
private final Map<Class<? extends Annotation>, SynthesizedAnnotation> metaAnnotationMap;
/**
* 合成注解选择器
*/
private final SynthesizedAnnotationSelector annotationSelector;
/**
* 合成注解属性处理器
*/
private final SynthesizedAnnotationAttributeProcessor attributeProcessor;
/**
* 基于指定根注解,为其层级结构中的全部注解构造一个合成注解。
* 当层级结构中出现了相同的注解对象时,将优先选择以距离根注解最近,且优先被扫描的注解对象,
* 当获取值时,同样遵循该规则。
*
* @param source 源注解
*/
public SyntheticMetaAnnotation(Annotation source) {
this(
source, SynthesizedAnnotationSelector.NEAREST_AND_OLDEST_PRIORITY,
new CacheableSynthesizedAnnotationAttributeProcessor(
Comparator.comparing(SynthesizedAnnotation::getVerticalDistance)
.thenComparing(SynthesizedAnnotation::getHorizontalDistance)
)
);
}
/**
* 基于指定根注解,为其层级结构中的全部注解构造一个合成注解
*
* @param annotation 当前查找的注解对象
* @param annotationSelector 合成注解选择器
* @param attributeProcessor 注解属性处理器
*/
public SyntheticMetaAnnotation(
Annotation annotation,
SynthesizedAnnotationSelector annotationSelector,
SynthesizedAnnotationAttributeProcessor attributeProcessor) {
Assert.notNull(annotation, "annotation must not null");
Assert.notNull(annotationSelector, "annotationSelector must not null");
Assert.notNull(attributeProcessor, "attributeProcessor must not null");
this.source = annotation;
this.annotationSelector = annotationSelector;
this.attributeProcessor = attributeProcessor;
this.metaAnnotationMap = new LinkedHashMap<>();
loadMetaAnnotations();
}
/**
* 获取根注解
*
* @return 根注解
*/
public Annotation getSource() {
return source;
}
/**
* 获取已解析的元注解信息
*
* @return 已解析的元注解信息
*/
Map<Class<? extends Annotation>, SynthesizedAnnotation> getMetaAnnotationMap() {
return metaAnnotationMap;
}
/**
* 获取合成注解选择器
*
* @return 合成注解选择器
*/
@Override
public SynthesizedAnnotationSelector getAnnotationSelector() {
return this.annotationSelector;
}
/**
* 获取合成注解属性处理器
*
* @return 合成注解属性处理器
*/
@Override
public SynthesizedAnnotationAttributeProcessor getAttributeProcessor() {
return this.attributeProcessor;
}
/**
* 获取已合成的注解
*
* @param annotationType 注解类型
* @return 已合成的注解
*/
@Override
public SynthesizedAnnotation getSynthesizedAnnotation(Class<?> annotationType) {
return metaAnnotationMap.get(annotationType);
}
/**
* 获取根注解类型
*
* @return 注解类型
*/
@Override
public Class<? extends Annotation> annotationType() {
return this.getClass();
}
/**
* 根据指定的属性名与属性类型获取对应的属性值,若存在{@link Alias}则获取{@link Alias#value()}指定的别名属性的值
* <p>当不同层级的注解之间存在同名同类型属性时,将优先获取更接近根注解的属性
*
* @param attributeName 属性名
* @param attributeType 属性类型
* @return 属性
*/
@Override
public Object getAttribute(String attributeName, Class<?> attributeType) {
return attributeProcessor.getAttributeValue(attributeName, attributeType, metaAnnotationMap.values());
}
/**
* 获取被合成的注解
*
* @param annotationType 注解类型
* @param <T> 注解类型
* @return 注解对象
*/
@Override
public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
return Opt.ofNullable(annotationType)
.map(metaAnnotationMap::get)
.map(SynthesizedAnnotation::getAnnotation)
.map(annotationType::cast)
.orElse(null);
}
/**
* 当前合成注解中是否存在指定元注解
*
* @param annotationType 注解类型
* @return 是否
*/
@Override
public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
return metaAnnotationMap.containsKey(annotationType);
}
/**
* 获取全部注解
*
* @return 注解对象
*/
@Override
public Annotation[] getAnnotations() {
return getMetaAnnotationMap().values().toArray(new MetaAnnotation[0]);
}
/**
* 若合成注解在存在指定元注解,则使用动态代理生成一个对应的注解实例
*
* @param annotationType 注解类型
* @return 合成注解对象
* @see SyntheticAnnotationProxy#create(Class, SyntheticAnnotation)
*/
@Override
public <T extends Annotation> T syntheticAnnotation(Class<T> annotationType) {
return SyntheticAnnotationProxy.create(annotationType, this);
}
/**
* 获取根注解直接声明的注解,该方法正常情况下当只返回原注解
*
* @return 直接声明注解
*/
@Override
public Annotation[] getDeclaredAnnotations() {
return new Annotation[]{getSource()};
}
/**
* 广度优先遍历并缓存该根注解上的全部元注解
*/
private void loadMetaAnnotations() {
Assert.isFalse(SyntheticAnnotationProxy.isProxyAnnotation(source.getClass()), "source [{}] has been synthesized");
// 扫描元注解
metaAnnotationMap.put(source.annotationType(), new MetaAnnotation(source, source, 0, 0));
new MetaAnnotationScanner().scan(
(index, annotation) -> {
SynthesizedAnnotation oldAnnotation = metaAnnotationMap.get(annotation.annotationType());
SynthesizedAnnotation newAnnotation = new MetaAnnotation(source, annotation, index, metaAnnotationMap.size());
if (ObjectUtil.isNull(oldAnnotation)) {
metaAnnotationMap.put(annotation.annotationType(), newAnnotation);
} else {
metaAnnotationMap.put(annotation.annotationType(), annotationSelector.choose(oldAnnotation, newAnnotation));
}
},
source.annotationType(), null
);
}
/**
* 元注解包装类
*
* @author huangchengxing
*/
public static class MetaAnnotation implements Annotation, SynthesizedAnnotation {
private final Annotation root;
private final Annotation annotation;
private final Map<String, Method> attributeMethodCaches;
private final int verticalDistance;
private final int horizontalDistance;
public MetaAnnotation(Annotation root, Annotation annotation, int verticalDistance, int horizontalDistance) {
this.root = root;
this.annotation = annotation;
this.verticalDistance = verticalDistance;
this.horizontalDistance = horizontalDistance;
this.attributeMethodCaches = AnnotationUtil.getAttributeMethods(annotation.annotationType());
}
/**
* 获取注解类型
*
* @return 注解类型
*/
@Override
public Class<? extends Annotation> annotationType() {
return annotation.annotationType();
}
/**
* 获取根注解
*
* @return 根注解
*/
@Override
public Annotation getRoot() {
return this.root;
}
/**
* 获取元注解
*
* @return 元注解
*/
@Override
public Annotation getAnnotation() {
return annotation;
}
/**
* 获取该合成注解与根注解之间相隔的层级数
*
* @return 该合成注解与根注解之间相隔的层级数
*/
@Override
public int getVerticalDistance() {
return verticalDistance;
}
/**
* 获取该合成注解与根注解之间相隔的注解树
*
* @return 该合成注解与根注解之间相隔的注解树
*/
@Override
public int getHorizontalDistance() {
return horizontalDistance;
}
/**
* 元注解是否存在该属性
*
* @param attributeName 属性名
* @return 是否存在该属性
*/
public boolean hasAttribute(String attributeName) {
return attributeMethodCaches.containsKey(attributeName);
}
/**
* 元注解是否存在该属性,且该属性的值类型是指定类型或其子类
*
* @param attributeName 属性名
* @param returnType 返回值类型
* @return 是否存在该属性
*/
@Override
public boolean hasAttribute(String attributeName, Class<?> returnType) {
return Opt.ofNullable(attributeMethodCaches.get(attributeName))
.filter(method -> ClassUtil.isAssignable(returnType, method.getReturnType()))
.isPresent();
}
/**
* 获取元注解的属性值
*
* @param attributeName 属性名
* @return 元注解的属性值
*/
@Override
public Object getAttribute(String attributeName) {
return Opt.ofNullable(attributeMethodCaches.get(attributeName))
.map(method -> ReflectUtil.invoke(annotation, method))
.orElse(null);
}
}
}

View File

@@ -0,0 +1,125 @@
package cn.hutool.core.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Collection;
/**
* <p>表示一个被包装过的{@link AnnotationAttribute}
* 该实例中的一些方法可能会被代理到另一个注解属性对象中,
* 从而使得通过原始的注解属性的方法获取到另一注解属性的值。<br>
* 除了{@link #getValue()}以外,其他方法的返回值应当尽可能与{@link #getOriginal()}
* 返回的{@link AnnotationAttribute}对象的方法返回值一致。
*
* <p>当包装类被包装了多层后,则规则生效优先级按包装的先后顺序倒序排序,
* 比如a、b互为镜像此时a、b两属性应当都被{@link MirroredAnnotationAttribute}包装,
* 若再指定c为a的别名字段则c、a、b都要在原基础上再次包装一层{@link AliasedAnnotationAttribute}。<br>
* 此时a、b同时被包装了两层则执行时优先执行{@link AliasedAnnotationAttribute}的逻辑,
* 当该规则不生效时比如c只有默认值此时上一次的{@link MirroredAnnotationAttribute}的逻辑才会生效。
*
* <p>被包装的{@link AnnotationAttribute}实际结构为一颗二叉树,
* 当包装类再次被包装时,实际上等于又添加了一个新的根节点,
* 此时需要同时更新树的全部关联叶子节点。
*
* @author huangchengxing
* @see AnnotationAttribute
* @see ForceAliasedAnnotationAttribute
* @see AliasedAnnotationAttribute
* @see MirroredAnnotationAttribute
*/
public interface WrappedAnnotationAttribute extends AnnotationAttribute {
// =========================== 新增方法 ===========================
/**
* 获取被包装的{@link AnnotationAttribute}对象,该对象也可能是{@link AnnotationAttribute}
*
* @return 被包装的{@link AnnotationAttribute}对象
*/
AnnotationAttribute getOriginal();
/**
* 获取最初的被包装的{@link AnnotationAttribute}
*
* @return 最初的被包装的{@link AnnotationAttribute}
*/
AnnotationAttribute getNonWrappedOriginal();
/**
* 获取包装{@link #getOriginal()}的{@link AnnotationAttribute}对象,该对象也可能是{@link AnnotationAttribute}
*
* @return 包装对象
*/
AnnotationAttribute getLinked();
/**
* 遍历以当前实例为根节点的树结构,获取所有未被包装的属性
*
* @return 叶子节点
*/
Collection<AnnotationAttribute> getAllLinkedNonWrappedAttributes();
// =========================== 代理实现 ===========================
/**
* 获取注解对象
*
* @return 注解对象
*/
@Override
default Annotation getAnnotation() {
return getOriginal().getAnnotation();
}
/**
* 获取注解属性对应的方法
*
* @return 注解属性对应的方法
*/
@Override
default Method getAttribute() {
return getOriginal().getAttribute();
}
/**
* 该注解属性的值是否等于默认值 <br>
* 默认仅当{@link #getOriginal()}与{@link #getLinked()}返回的注解属性
* 都为默认值时,才返回{@code true}
*
* @return 该注解属性的值是否等于默认值
*/
@Override
boolean isValueEquivalentToDefaultValue();
/**
* 获取属性类型
*
* @return 属性类型
*/
@Override
default Class<?> getAttributeType() {
return getOriginal().getAttributeType();
}
/**
* 获取属性上的注解
*
* @param annotationType 注解类型
* @return 注解对象
*/
@Override
default <T extends Annotation> T getAnnotation(Class<T> annotationType) {
return getOriginal().getAnnotation(annotationType);
}
/**
* 当前注解属性是否已经被{@link WrappedAnnotationAttribute}包装
*
* @return boolean
*/
@Override
default boolean isWrapped() {
return true;
}
}

View File

@@ -24,8 +24,7 @@ public abstract class AbstractTypeAnnotationScanner<T extends AbstractTypeAnnota
/**
* 是否允许扫描父类
*/
// FIXME rename includeSuperClass
private boolean includeSupperClass;
private boolean includeSuperClass;
/**
* 是否允许扫描父接口
@@ -60,16 +59,16 @@ public abstract class AbstractTypeAnnotationScanner<T extends AbstractTypeAnnota
/**
* 构造一个类注解扫描器
*
* @param includeSupperClass 是否允许扫描父类
* @param includeSuperClass 是否允许扫描父类
* @param includeInterfaces 是否允许扫描父接口
* @param filter 过滤器
* @param excludeTypes 不包含的类型
*/
@SuppressWarnings("unchecked")
protected AbstractTypeAnnotationScanner(boolean includeSupperClass, boolean includeInterfaces, Predicate<Class<?>> filter, Set<Class<?>> excludeTypes) {
protected AbstractTypeAnnotationScanner(boolean includeSuperClass, boolean includeInterfaces, Predicate<Class<?>> filter, Set<Class<?>> excludeTypes) {
Assert.notNull(filter, "filter must not null");
Assert.notNull(excludeTypes, "excludeTypes must not null");
this.includeSupperClass = includeSupperClass;
this.includeSuperClass = includeSuperClass;
this.includeInterfaces = includeInterfaces;
this.filter = filter;
this.excludeTypes = excludeTypes;
@@ -82,8 +81,8 @@ public abstract class AbstractTypeAnnotationScanner<T extends AbstractTypeAnnota
*
* @return 是否允许扫描父类
*/
public boolean isIncludeSupperClass() {
return includeSupperClass;
public boolean isIncludeSuperClass() {
return includeSuperClass;
}
/**
@@ -137,11 +136,11 @@ public abstract class AbstractTypeAnnotationScanner<T extends AbstractTypeAnnota
/**
* 是否允许扫描父类
*
* @param includeSupperClass 是否
* @param includeSuperClass 是否
* @return 当前实例
*/
protected T setIncludeSupperClass(boolean includeSupperClass) {
this.includeSupperClass = includeSupperClass;
protected T setIncludeSuperClass(boolean includeSuperClass) {
this.includeSuperClass = includeSuperClass;
return typedThis;
}
@@ -222,6 +221,7 @@ public abstract class AbstractTypeAnnotationScanner<T extends AbstractTypeAnnota
*
* @param accessedTypes 访问类型
* @param targetClass 目标类型
* @return 是否不需要处理
*/
protected boolean isNotNeedProcess(Set<Class<?>> accessedTypes, Class<?> targetClass) {
return ObjectUtil.isNull(targetClass)
@@ -246,13 +246,13 @@ public abstract class AbstractTypeAnnotationScanner<T extends AbstractTypeAnnota
}
/**
* 若{@link #includeSupperClass}为{@code true}则将目标类的父类也添加到nextClasses
* 若{@link #includeSuperClass}为{@code true}则将目标类的父类也添加到nextClasses
*
* @param nextClassQueue 下一个类队列
* @param targetClass 目标类型
*/
protected void scanSuperClassIfNecessary(List<Class<?>> nextClassQueue, Class<?> targetClass) {
if (includeSupperClass) {
if (includeSuperClass) {
final Class<?> superClass = targetClass.getSuperclass();
if (!ObjectUtil.equals(superClass, Object.class) && ObjectUtil.isNotNull(superClass)) {
nextClassQueue.add(superClass);

View File

@@ -0,0 +1,33 @@
package cn.hutool.core.annotation.scanner;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.util.Collections;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
/**
* 默认不扫描任何元素的扫描器
*
* @author huangchengxing
*/
public class EmptyAnnotationScanner implements AnnotationScanner {
public static final EmptyAnnotationScanner INSTANCE = new EmptyAnnotationScanner();
@Override
public boolean support(AnnotatedElement annotatedEle) {
return true;
}
@Override
public List<Annotation> getAnnotations(AnnotatedElement annotatedEle) {
return Collections.emptyList();
}
@Override
public void scan(BiConsumer<Integer, Annotation> consumer, AnnotatedElement annotatedEle, Predicate<Annotation> filter) {
// do nothing
}
}

View File

@@ -1,16 +1,13 @@
package cn.hutool.core.annotation.scanner;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjectUtil;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -84,6 +81,7 @@ public class MetaAnnotationScanner implements AnnotationScanner {
@Override
public void scan(BiConsumer<Integer, Annotation> consumer, AnnotatedElement annotatedEle, Predicate<Annotation> filter) {
filter = ObjectUtil.defaultIfNull(filter, t -> true);
Set<Class<? extends Annotation>> accessed = new HashSet<>();
final Deque<List<Class<? extends Annotation>>> deque = CollUtil.newLinkedList(CollUtil.newArrayList((Class<? extends Annotation>)annotatedEle));
int distance = 0;
do {
@@ -96,7 +94,14 @@ public class MetaAnnotationScanner implements AnnotationScanner {
for (final Annotation metaAnnotation : metaAnnotations) {
consumer.accept(distance, metaAnnotation);
}
deque.addLast(CollStreamUtil.toList(metaAnnotations, Annotation::annotationType));
accessed.add(type);
List<Class<? extends Annotation>> next = metaAnnotations.stream()
.map(Annotation::annotationType)
.filter(t -> !accessed.contains(t))
.collect(Collectors.toList());
if (CollUtil.isNotEmpty(next)) {
deque.addLast(next);
}
}
distance++;
} while (includeSupperMetaAnnotation && !deque.isEmpty());

View File

@@ -80,7 +80,7 @@ public class MethodAnnotationScanner extends AbstractTypeAnnotationScanner<Metho
@Override
protected Annotation[] getAnnotationsFromTargetClass(AnnotatedElement source, int index, Class<?> targetClass) {
final Method sourceMethod = (Method) source;
return Stream.of(targetClass.getDeclaredMethods())
return Stream.of(ClassUtil.getDeclaredMethods(targetClass))
.filter(superMethod -> !superMethod.isBridge())
.filter(superMethod -> hasSameSignature(sourceMethod, superMethod))
.map(AnnotatedElement::getAnnotations)
@@ -96,7 +96,7 @@ public class MethodAnnotationScanner extends AbstractTypeAnnotationScanner<Metho
*/
public MethodAnnotationScanner setScanSameSignatureMethod(boolean scanSuperMethodIfOverride) {
setIncludeInterfaces(scanSuperMethodIfOverride);
setIncludeSupperClass(scanSuperMethodIfOverride);
setIncludeSuperClass(scanSuperMethodIfOverride);
return this;
}

View File

@@ -73,12 +73,12 @@ public class TypeAnnotationScanner extends AbstractTypeAnnotationScanner<TypeAnn
/**
* 是否允许扫描父类
*
* @param includeSupperClass 是否允许扫描父类
* @param includeSuperClass 是否允许扫描父类
* @return 当前实例
*/
@Override
public TypeAnnotationScanner setIncludeSupperClass(boolean includeSupperClass) {
return super.setIncludeSupperClass(includeSupperClass);
public TypeAnnotationScanner setIncludeSuperClass(boolean includeSuperClass) {
return super.setIncludeSuperClass(includeSuperClass);
}
/**

View File

@@ -442,7 +442,8 @@ public class ListUtil {
* @param end 结束位置(不包含)
* @return 截取后的数组当开始位置超过最大时返回空的List
*/
public static <T> List<T> sub(List<T> list, int start, int end) {
public static <T> List<T>
sub(List<T> list, int start, int end) {
return sub(list, start, end, 1);
}

View File

@@ -1,5 +1,7 @@
package cn.hutool.core.collection;
import cn.hutool.core.lang.Assert;
import java.util.AbstractList;
import java.util.List;
@@ -21,18 +23,18 @@ public class Partition<T> extends AbstractList<List<T>> {
/**
* 列表分区
*
* @param list 被分区的列表
* @param size 每个分区的长度
* @param list 被分区的列表,非空
* @param size 每个分区的长度,必须&gt;0
*/
public Partition(List<T> list, int size) {
this.list = list;
this.size = Math.min(size, list.size());
this.list = Assert.notNull(list);
this.size = Math.min(list.size(), size);
}
@Override
public List<T> get(int index) {
int start = index * size;
int end = Math.min(start + size, list.size());
final int start = index * size;
final int end = Math.min(start + size, list.size());
return list.subList(start, end);
}
@@ -41,11 +43,9 @@ public class Partition<T> extends AbstractList<List<T>> {
// 此处采用动态计算以应对list变
final int size = this.size;
final int total = list.size();
int length = total / size;
if(total % size > 0){
length += 1;
}
return length;
// 类似于判断余数当总数非整份size时多余的数>=1则相当于被除数多一个size做到+1目的
// 类似于if(total % size > 0){length += 1;}
return (total + size - 1) / size;
}
@Override

View File

@@ -94,6 +94,8 @@ public class TemporalAccessorConverter extends AbstractConverter<TemporalAccesso
protected TemporalAccessor convertInternal(Object value) {
if (value instanceof Long) {
return parseFromLong((Long) value);
} else if (value instanceof Integer) {
return parseFromLong((long) (Integer) value);
} else if (value instanceof TemporalAccessor) {
return parseFromTemporalAccessor((TemporalAccessor) value);
} else if (value instanceof Date) {

View File

@@ -171,6 +171,8 @@ public class LocalDateTimeUtil {
return ((LocalDate) temporalAccessor).atStartOfDay();
} else if(temporalAccessor instanceof Instant){
return LocalDateTime.ofInstant((Instant) temporalAccessor, ZoneId.systemDefault());
} else if(temporalAccessor instanceof ZonedDateTime){
return ((ZonedDateTime)temporalAccessor).toLocalDateTime();
}
return LocalDateTime.of(
@@ -571,4 +573,42 @@ public class LocalDateTimeUtil {
public static int weekOfYear(TemporalAccessor date){
return TemporalAccessorUtil.get(date, WeekFields.ISO.weekOfYear());
}
/**
* 比较两个日期是否为同一天
*
* @param date1 日期1
* @param date2 日期2
* @return 是否为同一天
* @since 5.8.5
*/
public static boolean isSameDay(final LocalDateTime date1, final LocalDateTime date2) {
return date1 != null && date2 != null && isSameDay(date1.toLocalDate(), date2.toLocalDate());
}
/**
* 比较两个日期是否为同一天
*
* @param date1 日期1
* @param date2 日期2
* @return 是否为同一天
* @since 5.8.5
*/
public static boolean isSameDay(final LocalDate date1, final LocalDate date2) {
return date1 != null && date2 != null && date1.isEqual(date2);
}
/**
* 当前日期是否在日期指定范围内<br>
* 起始日期和结束日期可以互换
*
* @param date 被检查的日期
* @param beginDate 起始日期(包含)
* @param endDate 结束日期(包含)
* @return 是否在范围内
* @since 5.8.5
*/
public static boolean isIn(ChronoLocalDateTime<?> date, ChronoLocalDateTime<?> beginDate, ChronoLocalDateTime<?> endDate){
return TemporalAccessorUtil.isIn(date, beginDate, endDate);
}
}

View File

@@ -162,4 +162,22 @@ public class TemporalAccessorUtil extends TemporalUtil{
return result;
}
/**
* 当前日期是否在日期指定范围内<br>
* 起始日期和结束日期可以互换
*
* @param date 被检查的日期
* @param beginDate 起始日期(包含)
* @param endDate 结束日期(包含)
* @return 是否在范围内
* @since 5.8.5
*/
public static boolean isIn(TemporalAccessor date, TemporalAccessor beginDate, TemporalAccessor endDate){
final long thisMills = toEpochMilli(date);
final long beginMills = toEpochMilli(beginDate);
final long endMills = toEpochMilli(endDate);
return thisMills >= Math.min(beginMills, endMills) && thisMills <= Math.max(beginMills, endMills);
}
}

View File

@@ -38,9 +38,8 @@ public class FileTypeUtil {
FILE_TYPE_MAP.put("4749463837", "gif"); // GIF (gif)
FILE_TYPE_MAP.put("4749463839", "gif"); // GIF (gif)
FILE_TYPE_MAP.put("49492a00227105008037", "tif"); // TIFF (tif)
FILE_TYPE_MAP.put("424d228c010000000000", "bmp"); // 16色位图(bmp)
FILE_TYPE_MAP.put("424d8240090000000000", "bmp"); // 24色位图(bmp)
FILE_TYPE_MAP.put("424d8e1b030000000000", "bmp"); // 256色位图(bmp)
// https://github.com/sindresorhus/file-type/blob/main/core.js#L90
FILE_TYPE_MAP.put("424d", "bmp"); // 位图(bmp)
FILE_TYPE_MAP.put("41433130313500000000", "dwg"); // CAD (dwg)
FILE_TYPE_MAP.put("7b5c727466315c616e73", "rtf"); // Rich Text Format (rtf)
FILE_TYPE_MAP.put("38425053000100000000", "psd"); // Photoshop (psd)

View File

@@ -19,6 +19,7 @@ import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiFunction;
/**
* 字典对象扩充了HashMap中的方法
@@ -567,6 +568,11 @@ public class Dict extends LinkedHashMap<String, Object> implements BasicTypeGett
}
// -------------------------------------------------------------------- Get end
@Override
public boolean containsKey(Object key) {
return super.containsKey(customKey((String) key));
}
@Override
public Object get(Object key) {
return super.get(customKey((String) key));
@@ -587,6 +593,48 @@ public class Dict extends LinkedHashMap<String, Object> implements BasicTypeGett
return (Dict) super.clone();
}
@Override
public Object remove(Object key) {
return super.remove(customKey((String) key));
}
@Override
public boolean remove(Object key, Object value) {
return super.remove(customKey((String) key), value);
}
@Override
public boolean replace(String key, Object oldValue, Object newValue) {
return super.replace(customKey(key), oldValue, newValue);
}
@Override
public Object replace(String key, Object value) {
return super.replace(customKey(key), value);
}
//---------------------------------------------------------------------------- Override default methods start
@Override
public Object getOrDefault(Object key, Object defaultValue) {
return super.getOrDefault(customKey((String) key), defaultValue);
}
@Override
public Object computeIfPresent(final String key, final BiFunction<? super String, ? super Object, ?> remappingFunction) {
return super.computeIfPresent(customKey(key), remappingFunction);
}
@Override
public Object compute(final String key, final BiFunction<? super String, ? super Object, ?> remappingFunction) {
return super.compute(customKey(key), remappingFunction);
}
@Override
public Object merge(final String key, final Object value, final BiFunction<? super Object, ? super Object, ?> remappingFunction) {
return super.merge(customKey(key), value, remappingFunction);
}
//---------------------------------------------------------------------------- Override default methods end
/**
* 将Key转为小写
*
@@ -616,4 +664,5 @@ public class Dict extends LinkedHashMap<String, Object> implements BasicTypeGett
Arrays.stream(fields).forEach(f -> set(LambdaUtil.getFieldName(f), f.callWithRuntimeException()));
return this;
}
}

View File

@@ -275,6 +275,18 @@ public interface ForestMap<K, V> extends Map<K, TreeEntry<K, V>> {
.orElse(false);
}
/**
* 获取指定节点的值
*
* @param key 节点的key
* @return 节点值若节点不存在或节点值为null都将返回null
*/
default V getNodeValue(K key) {
return Opt.ofNullable(get(key))
.map(TreeEntry::getValue)
.get();
}
// ===================== 子节点相关方法 =====================
/**

View File

@@ -70,7 +70,7 @@ public abstract class TransMap<K, V> extends MapWrapper<K, V> {
@Override
public boolean replace(K key, V oldValue, V newValue) {
return super.replace(customKey(key), customValue(oldValue), customValue(values()));
return super.replace(customKey(key), customValue(oldValue), customValue(newValue));
}
@Override

View File

@@ -1021,7 +1021,7 @@ public class ArrayUtil extends PrimitiveArrayUtil {
* 获取数组中指定多个下标元素值,组成新数组
*
* @param <T> 数组元素类型
* @param array 数组
* @param array 数组,如果提供为{@code null}则返回{@code null}
* @param indexes 下标列表
* @return 结果
*/
@@ -1029,10 +1029,13 @@ public class ArrayUtil extends PrimitiveArrayUtil {
if (null == array) {
return null;
}
if(null == indexes){
return newArray(array.getClass().getComponentType(), 0);
}
final T[] result = newArray(array.getClass().getComponentType(), indexes.length);
for (int i : indexes) {
result[i] = get(array, i);
for (int i = 0; i < indexes.length; i++) {
result[i] = ArrayUtil.get(array, indexes[i]);
}
return result;
}

View File

@@ -175,15 +175,28 @@ public class PageUtil {
* @return 总页数
*/
public static int totalPage(int totalCount, int pageSize) {
return totalPage((long) totalCount,pageSize);
}
/**
* 根据总数计算总页数
*
* @param totalCount 总数
* @param pageSize 每页数
* @return 总页数
* @since 5.8.5
*/
public static int totalPage(long totalCount, int pageSize) {
if (pageSize == 0) {
return 0;
}
return totalCount % pageSize == 0 ? (totalCount / pageSize) : (totalCount / pageSize + 1);
return Math.toIntExact(totalCount % pageSize == 0 ? (totalCount / pageSize) : (totalCount / pageSize + 1));
}
/**
* 分页彩虹算法<br>
* 来自https://github.com/iceroot/iceroot/blob/master/src/main/java/com/icexxx/util/IceUtil.java<br>
* 来自:<a href="https://github.com/iceroot/iceroot/blob/master/src/main/java/com/icexxx/util/IceUtil.java">
* https://github.com/iceroot/iceroot/blob/master/src/main/java/com/icexxx/util/IceUtil.java</a><br>
* 通过传入的信息,生成一个分页列表显示
*
* @param pageNo 当前页
@@ -230,7 +243,8 @@ public class PageUtil {
/**
* 分页彩虹算法(默认展示10页)<br>
* 来自https://github.com/iceroot/iceroot/blob/master/src/main/java/com/icexxx/util/IceUtil.java
* 来自:<a href="https://github.com/iceroot/iceroot/blob/master/src/main/java/com/icexxx/util/IceUtil.java">
* https://github.com/iceroot/iceroot/blob/master/src/main/java/com/icexxx/util/IceUtil.java</a>
*
* @param currentPage 当前页
* @param pageCount 总页数

View File

@@ -206,18 +206,36 @@ public class ReUtil {
* @since 4.0.13
*/
public static List<String> getAllGroups(Pattern pattern, CharSequence content, boolean withGroup0) {
return getAllGroups(pattern, content, withGroup0, false);
}
/**
* 获得匹配的字符串匹配到的所有分组
*
* @param pattern 编译后的正则模式
* @param content 被匹配的内容
* @param withGroup0 是否包括分组0此分组表示全匹配的信息
* @param findAll 是否查找所有匹配到的内容,{@code false}表示只读取第一个匹配到的内容
* @return 匹配后得到的字符串数组按照分组顺序依次列出未匹配到返回空列表任何一个参数为null返回null
* @since 4.0.13
*/
public static List<String> getAllGroups(Pattern pattern, CharSequence content, boolean withGroup0, boolean findAll) {
if (null == content || null == pattern) {
return null;
}
ArrayList<String> result = new ArrayList<>();
final Matcher matcher = pattern.matcher(content);
if (matcher.find()) {
while (matcher.find()) {
final int startGroup = withGroup0 ? 0 : 1;
final int groupCount = matcher.groupCount();
for (int i = startGroup; i <= groupCount; i++) {
result.add(matcher.group(i));
}
if(false == findAll){
break;
}
}
return result;
}

View File

@@ -0,0 +1,106 @@
package cn.hutool.core.annotation;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ReflectUtil;
import org.junit.Assert;
import org.junit.Test;
import java.lang.annotation.*;
import java.lang.reflect.Method;
public class AbstractWrappedAnnotationAttributeTest {
@Test
public void workTest() {
Annotation annotation = ClassForTest1.class.getAnnotation(AnnotationForTest1.class);
Method valueMethod = ReflectUtil.getMethod(AnnotationForTest1.class, "value1");
CacheableAnnotationAttribute valueAttribute = new CacheableAnnotationAttribute(annotation, valueMethod);
Method nameMethod = ReflectUtil.getMethod(AnnotationForTest1.class, "name1");
CacheableAnnotationAttribute nameAttribute = new CacheableAnnotationAttribute(annotation, nameMethod);
AbstractWrappedAnnotationAttribute nameWrapper = new TestWrappedAnnotationAttribute(nameAttribute, valueAttribute);
Assert.assertEquals(nameWrapper.getAnnotation(), annotation);
// 注解属性
Assert.assertEquals(annotation, nameWrapper.getAnnotation());
Assert.assertEquals(annotation.annotationType(), nameWrapper.getAnnotationType());
Assert.assertEquals(nameAttribute, nameWrapper.getOriginal());
Assert.assertEquals(valueAttribute, nameWrapper.getLinked());
// 方法属性
Assert.assertEquals(nameMethod.getName(), nameWrapper.getAttributeName());
Assert.assertEquals(nameMethod.getReturnType(), nameWrapper.getAttributeType());
Assert.assertTrue(nameWrapper.isWrapped());
Assert.assertEquals("value1", nameWrapper.getValue());
}
@Test
public void multiWrapperTest() {
// 包装第一层: name1 + value1
Annotation annotation1 = ClassForTest1.class.getAnnotation(AnnotationForTest1.class);
Method value1Method = ReflectUtil.getMethod(AnnotationForTest1.class, "value1");
CacheableAnnotationAttribute value1Attribute = new CacheableAnnotationAttribute(annotation1, value1Method);
Method name1Method = ReflectUtil.getMethod(AnnotationForTest1.class, "name1");
CacheableAnnotationAttribute name1Attribute = new CacheableAnnotationAttribute(annotation1, name1Method);
AbstractWrappedAnnotationAttribute wrapper1 = new TestWrappedAnnotationAttribute(name1Attribute, value1Attribute);
Assert.assertEquals(name1Attribute, wrapper1.getNonWrappedOriginal());
Assert.assertEquals(CollUtil.newArrayList(name1Attribute, value1Attribute), wrapper1.getAllLinkedNonWrappedAttributes());
// 包装第二层:( name1 + value1 ) + value2
Annotation annotation2 = ClassForTest1.class.getAnnotation(AnnotationForTest2.class);
Method value2Method = ReflectUtil.getMethod(AnnotationForTest2.class, "value2");
CacheableAnnotationAttribute value2Attribute = new CacheableAnnotationAttribute(annotation2, value2Method);
AbstractWrappedAnnotationAttribute wrapper2 = new TestWrappedAnnotationAttribute(wrapper1, value2Attribute);
Assert.assertEquals(name1Attribute, wrapper2.getNonWrappedOriginal());
Assert.assertEquals(CollUtil.newArrayList(name1Attribute, value1Attribute, value2Attribute), wrapper2.getAllLinkedNonWrappedAttributes());
// 包装第二层value3 + ( ( name1 + value1 ) + value2 )
Annotation annotation3 = ClassForTest1.class.getAnnotation(AnnotationForTest3.class);
Method value3Method = ReflectUtil.getMethod(AnnotationForTest3.class, "value3");
CacheableAnnotationAttribute value3Attribute = new CacheableAnnotationAttribute(annotation3, value3Method);
AbstractWrappedAnnotationAttribute wrapper3 = new TestWrappedAnnotationAttribute(value3Attribute, wrapper2);
Assert.assertEquals(value3Attribute, wrapper3.getNonWrappedOriginal());
Assert.assertEquals(CollUtil.newArrayList(value3Attribute, name1Attribute, value1Attribute, value2Attribute), wrapper3.getAllLinkedNonWrappedAttributes());
}
static class TestWrappedAnnotationAttribute extends AbstractWrappedAnnotationAttribute {
protected TestWrappedAnnotationAttribute(AnnotationAttribute original, AnnotationAttribute linked) {
super(original, linked);
}
@Override
public Object getValue() {
return linked.getValue();
}
@Override
public boolean isValueEquivalentToDefaultValue() {
return getOriginal().isValueEquivalentToDefaultValue() && getLinked().isValueEquivalentToDefaultValue();
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface AnnotationForTest1 {
String value1() default "";
String name1() default "";
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface AnnotationForTest2 {
String value2() default "";
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface AnnotationForTest3 {
String value3() default "";
}
@AnnotationForTest1(name1 = "name1", value1 = "value1")
@AnnotationForTest2(value2 = "value2")
@AnnotationForTest3(value3 = "value3")
static class ClassForTest1 {}
}

View File

@@ -0,0 +1,195 @@
package cn.hutool.core.annotation;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import org.junit.Assert;
import org.junit.Test;
import java.lang.annotation.*;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.function.UnaryOperator;
public class AliasAnnotationPostProcessorTest {
@Test
public void processTest() {
AliasAnnotationPostProcessor processor = new AliasAnnotationPostProcessor();
Map<Class<?>, SynthesizedAnnotation> annotationMap = new HashMap<>();
SynthesizedAggregateAnnotation synthesizedAnnotationAggregator = new TestSynthesizedAggregateAnnotation(annotationMap);
AnnotationForTest annotation = ClassForTest.class.getAnnotation(AnnotationForTest.class);
SynthesizedAnnotation synthesizedAnnotation = new TestSynthesizedAnnotation(synthesizedAnnotationAggregator, annotation);
annotationMap.put(annotation.annotationType(), synthesizedAnnotation);
processor.process(synthesizedAnnotation, synthesizedAnnotationAggregator);
AnnotationAttribute valueAttribute = synthesizedAnnotation.getAttributes().get("value");
Assert.assertEquals(ReflectUtil.getMethod(AnnotationForTest.class, "value"), valueAttribute.getAttribute());
Assert.assertTrue(valueAttribute.isWrapped());
Assert.assertEquals(ForceAliasedAnnotationAttribute.class, valueAttribute.getClass());
AnnotationAttribute nameAttribute = synthesizedAnnotation.getAttributes().get("name");
Assert.assertEquals(ReflectUtil.getMethod(AnnotationForTest.class, "name"), nameAttribute.getAttribute());
Assert.assertFalse(nameAttribute.isWrapped());
Assert.assertEquals(CacheableAnnotationAttribute.class, nameAttribute.getClass());
Assert.assertEquals(nameAttribute, ((WrappedAnnotationAttribute)valueAttribute).getLinked());
}
@AnnotationForTest
static class ClassForTest {}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface AnnotationForTest {
@Alias("name")
String value() default "";
String name() default "";
}
static class TestSynthesizedAggregateAnnotation implements SynthesizedAggregateAnnotation {
private final Map<Class<?>, SynthesizedAnnotation> annotationMap;
public TestSynthesizedAggregateAnnotation(Map<Class<?>, SynthesizedAnnotation> annotationMap) {
this.annotationMap = annotationMap;
}
@Override
public Object getSource() {
return null;
}
@Override
public SynthesizedAnnotationSelector getAnnotationSelector() {
return null;
}
@Override
public SynthesizedAnnotationAttributeProcessor getAnnotationAttributeProcessor() {
return null;
}
@Override
public Collection<SynthesizedAnnotationPostProcessor> getAnnotationPostProcessors() {
return null;
}
@Override
public SynthesizedAnnotation getSynthesizedAnnotation(Class<?> annotationType) {
return annotationMap.get(annotationType);
}
@Override
public Map<Class<? extends Annotation>, SynthesizedAnnotation> getAllSynthesizedAnnotation() {
return null;
}
@Override
public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
return null;
}
@Override
public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
return false;
}
@Override
public Annotation[] getAnnotations() {
return new Annotation[0];
}
@Override
public <T extends Annotation> T synthesize(Class<T> annotationType) {
return null;
}
@Override
public Object getAttributeValue(String attributeName, Class<?> attributeType) {
return null;
}
@Override
public Object getRoot() {
return null;
}
}
static class TestSynthesizedAnnotation implements SynthesizedAnnotation {
private final Annotation annotation;
private final SynthesizedAggregateAnnotation owner;
private final Map<String, AnnotationAttribute> attributeMap;
public TestSynthesizedAnnotation(SynthesizedAggregateAnnotation owner, Annotation annotation) {
this.owner = owner;
this.attributeMap = new HashMap<>();
this.annotation = annotation;
for (Method declaredMethod : annotation.annotationType().getDeclaredMethods()) {
attributeMap.put(declaredMethod.getName(), new CacheableAnnotationAttribute(annotation, declaredMethod));
}
}
@Override
public Object getRoot() {
return null;
}
@Override
public Annotation getAnnotation() {
return annotation;
}
@Override
public int getVerticalDistance() {
return 0;
}
@Override
public int getHorizontalDistance() {
return 0;
}
@Override
public boolean hasAttribute(String attributeName, Class<?> returnType) {
return false;
}
@Override
public Map<String, AnnotationAttribute> getAttributes() {
return attributeMap;
}
@Override
public void setAttribute(String attributeName, AnnotationAttribute attribute) {
attributeMap.put(attributeName, attribute);
}
@Override
public void replaceAttribute(String attributeName, UnaryOperator<AnnotationAttribute> operator) {
AnnotationAttribute annotationAttribute = attributeMap.get(attributeName);
if (ObjectUtil.isNotNull(annotationAttribute)) {
attributeMap.put(attributeName, operator.apply(annotationAttribute));
}
}
@Override
public Object getAttributeValue(String attributeName) {
return null;
}
@Override
public Class<? extends Annotation> annotationType() {
return annotation.annotationType();
}
@Override
public Object getAttributeValue(String attributeName, Class<?> attributeType) {
return null;
}
}
}

View File

@@ -0,0 +1,223 @@
package cn.hutool.core.annotation;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import org.junit.Assert;
import org.junit.Test;
import java.lang.annotation.*;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.function.UnaryOperator;
public class AliasLinkAnnotationPostProcessorTest {
@Test
public void processForceAliasForTest() {
AliasLinkAnnotationPostProcessor processor = new AliasLinkAnnotationPostProcessor();
Map<Class<?>, SynthesizedAnnotation> annotationMap = new HashMap<>();
SynthesizedAggregateAnnotation synthesizedAnnotationAggregator = new TestSynthesizedAggregateAnnotation(annotationMap);
AnnotationForTest annotation = ClassForTest.class.getAnnotation(AnnotationForTest.class);
SynthesizedAnnotation synthesizedAnnotation = new TestSynthesizedAnnotation(synthesizedAnnotationAggregator, annotation);
annotationMap.put(annotation.annotationType(), synthesizedAnnotation);
processor.process(synthesizedAnnotation, synthesizedAnnotationAggregator);
AnnotationAttribute valueAttribute = synthesizedAnnotation.getAttributes().get("value");
Assert.assertEquals(ReflectUtil.getMethod(AnnotationForTest.class, "value"), valueAttribute.getAttribute());
Assert.assertFalse(valueAttribute.isWrapped());
Assert.assertEquals(CacheableAnnotationAttribute.class, valueAttribute.getClass());
AnnotationAttribute nameAttribute = synthesizedAnnotation.getAttributes().get("name");
Assert.assertEquals(ReflectUtil.getMethod(AnnotationForTest.class, "name"), nameAttribute.getAttribute());
Assert.assertTrue(nameAttribute.isWrapped());
Assert.assertEquals(ForceAliasedAnnotationAttribute.class, nameAttribute.getClass());
Assert.assertEquals(valueAttribute, ((WrappedAnnotationAttribute)nameAttribute).getLinked());
}
@Test
public void processAliasForTest() {
AliasLinkAnnotationPostProcessor processor = new AliasLinkAnnotationPostProcessor();
Map<Class<?>, SynthesizedAnnotation> annotationMap = new HashMap<>();
SynthesizedAggregateAnnotation synthesizedAnnotationAggregator = new TestSynthesizedAggregateAnnotation(annotationMap);
AnnotationForTest annotation = ClassForTest.class.getAnnotation(AnnotationForTest.class);
SynthesizedAnnotation synthesizedAnnotation = new TestSynthesizedAnnotation(synthesizedAnnotationAggregator, annotation);
annotationMap.put(annotation.annotationType(), synthesizedAnnotation);
processor.process(synthesizedAnnotation, synthesizedAnnotationAggregator);
AnnotationAttribute valueAttribute = synthesizedAnnotation.getAttributes().get("value2");
Assert.assertEquals(ReflectUtil.getMethod(AnnotationForTest.class, "value2"), valueAttribute.getAttribute());
Assert.assertFalse(valueAttribute.isWrapped());
Assert.assertEquals(CacheableAnnotationAttribute.class, valueAttribute.getClass());
AnnotationAttribute nameAttribute = synthesizedAnnotation.getAttributes().get("name2");
Assert.assertEquals(ReflectUtil.getMethod(AnnotationForTest.class, "name2"), nameAttribute.getAttribute());
Assert.assertTrue(nameAttribute.isWrapped());
Assert.assertEquals(AliasedAnnotationAttribute.class, nameAttribute.getClass());
Assert.assertEquals(valueAttribute, ((WrappedAnnotationAttribute)nameAttribute).getLinked());
}
@AnnotationForTest
static class ClassForTest {}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface AnnotationForTest {
@Link(attribute = "name", type = RelationType.FORCE_ALIAS_FOR)
String value() default "";
String name() default "";
@Link(attribute = "name2", type = RelationType.ALIAS_FOR)
String value2() default "";
String name2() default "";
}
static class TestSynthesizedAggregateAnnotation implements SynthesizedAggregateAnnotation {
private final Map<Class<?>, SynthesizedAnnotation> annotationMap;
public TestSynthesizedAggregateAnnotation(Map<Class<?>, SynthesizedAnnotation> annotationMap) {
this.annotationMap = annotationMap;
}
@Override
public Object getSource() {
return null;
}
@Override
public SynthesizedAnnotationSelector getAnnotationSelector() {
return null;
}
@Override
public SynthesizedAnnotationAttributeProcessor getAnnotationAttributeProcessor() {
return null;
}
@Override
public Collection<SynthesizedAnnotationPostProcessor> getAnnotationPostProcessors() {
return null;
}
@Override
public SynthesizedAnnotation getSynthesizedAnnotation(Class<?> annotationType) {
return annotationMap.get(annotationType);
}
@Override
public Map<Class<? extends Annotation>, SynthesizedAnnotation> getAllSynthesizedAnnotation() {
return null;
}
@Override
public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
return null;
}
@Override
public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
return false;
}
@Override
public Annotation[] getAnnotations() {
return new Annotation[0];
}
@Override
public <T extends Annotation> T synthesize(Class<T> annotationType) {
return null;
}
@Override
public Object getAttributeValue(String attributeName, Class<?> attributeType) {
return null;
}
@Override
public Object getRoot() {
return null;
}
}
static class TestSynthesizedAnnotation implements SynthesizedAnnotation {
private final Annotation annotation;
private final SynthesizedAggregateAnnotation owner;
private final Map<String, AnnotationAttribute> attributeMap;
public TestSynthesizedAnnotation(SynthesizedAggregateAnnotation owner, Annotation annotation) {
this.owner = owner;
this.attributeMap = new HashMap<>();
this.annotation = annotation;
for (Method declaredMethod : annotation.annotationType().getDeclaredMethods()) {
attributeMap.put(declaredMethod.getName(), new CacheableAnnotationAttribute(annotation, declaredMethod));
}
}
@Override
public Object getRoot() {
return null;
}
@Override
public Annotation getAnnotation() {
return annotation;
}
@Override
public int getVerticalDistance() {
return 0;
}
@Override
public int getHorizontalDistance() {
return 0;
}
@Override
public boolean hasAttribute(String attributeName, Class<?> returnType) {
return false;
}
@Override
public Map<String, AnnotationAttribute> getAttributes() {
return attributeMap;
}
@Override
public void setAttribute(String attributeName, AnnotationAttribute attribute) {
attributeMap.put(attributeName, attribute);
}
@Override
public void replaceAttribute(String attributeName, UnaryOperator<AnnotationAttribute> operator) {
AnnotationAttribute annotationAttribute = attributeMap.get(attributeName);
if (ObjectUtil.isNotNull(annotationAttribute)) {
attributeMap.put(attributeName, operator.apply(annotationAttribute));
}
}
@Override
public Object getAttributeValue(String attributeName) {
return null;
}
@Override
public Class<? extends Annotation> annotationType() {
return annotation.annotationType();
}
@Override
public Object getAttributeValue(String attributeName, Class<?> attributeType) {
return null;
}
}
}

View File

@@ -0,0 +1,78 @@
package cn.hutool.core.annotation;
import cn.hutool.core.util.ReflectUtil;
import org.junit.Assert;
import org.junit.Test;
import java.lang.annotation.*;
import java.lang.reflect.Method;
public class AliasedAnnotationAttributeTest {
@Test
public void baseInfoTest() {
// 组合属性
final Annotation annotation = ClassForTest1.class.getAnnotation(AnnotationForTest.class);
final Method valueMethod = ReflectUtil.getMethod(AnnotationForTest.class, "value");
final CacheableAnnotationAttribute valueAttribute = new CacheableAnnotationAttribute(annotation, valueMethod);
final Method nameMethod = ReflectUtil.getMethod(AnnotationForTest.class, "name");
final CacheableAnnotationAttribute nameAttribute = new CacheableAnnotationAttribute(annotation, nameMethod);
final AliasedAnnotationAttribute valueAnnotationAttribute = new AliasedAnnotationAttribute(valueAttribute, nameAttribute);
// 注解属性
Assert.assertEquals(annotation, valueAnnotationAttribute.getAnnotation());
Assert.assertEquals(annotation.annotationType(), valueAnnotationAttribute.getAnnotationType());
// 方法属性
Assert.assertEquals(valueMethod.getAnnotation(Alias.class), valueAnnotationAttribute.getAnnotation(Alias.class));
Assert.assertEquals(valueMethod.getName(), valueAnnotationAttribute.getAttributeName());
Assert.assertEquals(nameMethod.getReturnType(), valueAnnotationAttribute.getAttributeType());
}
@Test
public void workWhenValueDefaultTest() {
// 组合属性
final Annotation annotation = ClassForTest1.class.getAnnotation(AnnotationForTest.class);
final Method valueMethod = ReflectUtil.getMethod(AnnotationForTest.class, "value");
final CacheableAnnotationAttribute valueAttribute = new CacheableAnnotationAttribute(annotation, valueMethod);
final Method nameMethod = ReflectUtil.getMethod(AnnotationForTest.class, "name");
final CacheableAnnotationAttribute nameAttribute = new CacheableAnnotationAttribute(annotation, nameMethod);
final AliasedAnnotationAttribute annotationAttribute = new AliasedAnnotationAttribute(valueAttribute, nameAttribute);
// 值处理
Assert.assertEquals("name", annotationAttribute.getValue());
Assert.assertFalse(annotationAttribute.isValueEquivalentToDefaultValue());
Assert.assertTrue(annotationAttribute.isWrapped());
}
@Test
public void workWhenValueNonDefaultTest() {
// 组合属性
final Annotation annotation = ClassForTest2.class.getAnnotation(AnnotationForTest.class);
final Method valueMethod = ReflectUtil.getMethod(AnnotationForTest.class, "value");
final CacheableAnnotationAttribute valueAttribute = new CacheableAnnotationAttribute(annotation, valueMethod);
final Method nameMethod = ReflectUtil.getMethod(AnnotationForTest.class, "name");
final CacheableAnnotationAttribute nameAttribute = new CacheableAnnotationAttribute(annotation, nameMethod);
final AliasedAnnotationAttribute annotationAttribute = new AliasedAnnotationAttribute(valueAttribute, nameAttribute);
// 值处理
Assert.assertEquals("value", annotationAttribute.getValue());
Assert.assertFalse(annotationAttribute.isValueEquivalentToDefaultValue());
Assert.assertTrue(annotationAttribute.isWrapped());
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface AnnotationForTest {
@Alias("value")
String value() default "";
String name() default "";
}
@AnnotationForTest(name = "name", value = "value")
static class ClassForTest1 {}
@AnnotationForTest(value = "value")
static class ClassForTest2 {}
}

View File

@@ -16,14 +16,14 @@ public class AnnotationUtilTest {
public void getCombinationAnnotationsTest(){
final Annotation[] annotations = AnnotationUtil.getAnnotations(ClassWithAnnotation.class, true);
Assert.assertNotNull(annotations);
Assert.assertEquals(3, annotations.length);
Assert.assertEquals(2, annotations.length);
}
@Test
public void getCombinationAnnotationsWithClassTest(){
final AnnotationForTest[] annotations = AnnotationUtil.getCombinationAnnotations(ClassWithAnnotation.class, AnnotationForTest.class);
Assert.assertNotNull(annotations);
Assert.assertEquals(2, annotations.length);
Assert.assertEquals(1, annotations.length);
Assert.assertEquals("测试", annotations[0].value());
}
@@ -42,6 +42,7 @@ public class AnnotationUtilTest {
// 加别名适配
final AnnotationForTest annotation = AnnotationUtil.getAnnotationAlias(ClassWithAnnotation.class, AnnotationForTest.class);
Assert.assertEquals("测试", annotation.retry());
Assert.assertTrue(AnnotationUtil.isSynthesizedAnnotation(annotation));
}
@AnnotationForTest("测试")

View File

@@ -0,0 +1,61 @@
package cn.hutool.core.annotation;
import cn.hutool.core.util.ReflectUtil;
import org.junit.Assert;
import org.junit.Test;
import java.lang.annotation.*;
import java.lang.reflect.Method;
public class CacheableAnnotationAttributeTest {
@Test
public void baseInfoTest() {
final Annotation annotation = ClassForTest1.class.getAnnotation(AnnotationForTest.class);
final Method attribute = ReflectUtil.getMethod(AnnotationForTest.class, "value");
final CacheableAnnotationAttribute annotationAttribute = new CacheableAnnotationAttribute(annotation, attribute);
// 注解属性
Assert.assertEquals(annotation, annotationAttribute.getAnnotation());
Assert.assertEquals(annotation.annotationType(), annotationAttribute.getAnnotationType());
// 方法属性
Assert.assertEquals(attribute.getName(), annotationAttribute.getAttributeName());
Assert.assertEquals(attribute.getReturnType(), annotationAttribute.getAttributeType());
}
@Test
public void workWhenValueDefaultTest() {
final Annotation annotation = ClassForTest1.class.getAnnotation(AnnotationForTest.class);
final Method attribute = ReflectUtil.getMethod(AnnotationForTest.class, "value");
final CacheableAnnotationAttribute annotationAttribute = new CacheableAnnotationAttribute(annotation, attribute);
// 值处理
Assert.assertEquals("", annotationAttribute.getValue());
Assert.assertTrue(annotationAttribute.isValueEquivalentToDefaultValue());
Assert.assertFalse(annotationAttribute.isWrapped());
}
@Test
public void workWhenValueNonDefaultTest() {
final Annotation annotation = ClassForTest2.class.getAnnotation(AnnotationForTest.class);
final Method attribute = ReflectUtil.getMethod(AnnotationForTest.class, "value");
final CacheableAnnotationAttribute annotationAttribute = new CacheableAnnotationAttribute(annotation, attribute);
// 值处理
Assert.assertEquals("test", annotationAttribute.getValue());
Assert.assertFalse(annotationAttribute.isValueEquivalentToDefaultValue());
Assert.assertFalse(annotationAttribute.isWrapped());
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface AnnotationForTest {
String value() default "";
}
@AnnotationForTest("")
static class ClassForTest1 {}
@AnnotationForTest("test")
static class ClassForTest2 {}
}

View File

@@ -0,0 +1,99 @@
package cn.hutool.core.annotation;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.map.MapBuilder;
import cn.hutool.core.util.ClassUtil;
import org.junit.Assert;
import org.junit.Test;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Map;
import java.util.function.UnaryOperator;
public class CacheableSynthesizedAnnotationAttributeProcessorTest {
@Test
public void getAttributeValueTest() {
CacheableSynthesizedAnnotationAttributeProcessor processor = new CacheableSynthesizedAnnotationAttributeProcessor();
Map<String, Object> values1 = MapBuilder.<String, Object> create().put("name", "name1").put("value", 111).build();
SynthesizedAnnotation annotation1 = new TestSynthesizedAnnotation(1, 0, values1);
Map<String, Object> values2 = MapBuilder.<String, Object> create().put("name", "name2").put("value", "value2").build();
SynthesizedAnnotation annotation2 = new TestSynthesizedAnnotation(0, 0, values2);
Assert.assertEquals("name2", processor.getAttributeValue("name", String.class, Arrays.asList(annotation1, annotation2)));
Assert.assertEquals(Integer.valueOf(111), processor.getAttributeValue("value", Integer.class, Arrays.asList(annotation1, annotation2)));
}
static class TestSynthesizedAnnotation implements SynthesizedAnnotation {
private final int verticalDistance;
private final int horizontalDistance;
private final Map<String, Object> value;
public TestSynthesizedAnnotation(int verticalDistance, int horizontalDistance, Map<String, Object> value) {
this.verticalDistance = verticalDistance;
this.horizontalDistance = horizontalDistance;
this.value = value;
}
@Override
public Object getRoot() {
return null;
}
@Override
public Annotation getAnnotation() {
return null;
}
@Override
public int getVerticalDistance() {
return verticalDistance;
}
@Override
public int getHorizontalDistance() {
return horizontalDistance;
}
@Override
public boolean hasAttribute(String attributeName, Class<?> returnType) {
return Opt.ofNullable(value.get(attributeName))
.map(t -> ClassUtil.isAssignable(returnType, t.getClass()))
.orElse(false);
}
@Override
public Map<String, AnnotationAttribute> getAttributes() {
return null;
}
@Override
public void setAttribute(String attributeName, AnnotationAttribute attribute) {
}
@Override
public void replaceAttribute(String attributeName, UnaryOperator<AnnotationAttribute> operator) {
}
@Override
public Object getAttributeValue(String attributeName) {
return value.get(attributeName);
}
@Override
public Class<? extends Annotation> annotationType() {
return null;
}
@Override
public Object getAttributeValue(String attributeName, Class<?> attributeType) {
return null;
}
}
}

View File

@@ -0,0 +1,76 @@
package cn.hutool.core.annotation;
import cn.hutool.core.util.ReflectUtil;
import org.junit.Assert;
import org.junit.Test;
import java.lang.annotation.*;
import java.lang.reflect.Method;
public class ForceAliasedAnnotationAttributeTest {
@Test
public void baseInfoTest() {
// 组合属性
final Annotation annotation = ClassForTest1.class.getAnnotation(AnnotationForTest.class);
final Method valueMethod = ReflectUtil.getMethod(AnnotationForTest.class, "value");
final CacheableAnnotationAttribute valueAttribute = new CacheableAnnotationAttribute(annotation, valueMethod);
final Method nameMethod = ReflectUtil.getMethod(AnnotationForTest.class, "name");
final CacheableAnnotationAttribute nameAttribute = new CacheableAnnotationAttribute(annotation, nameMethod);
final ForceAliasedAnnotationAttribute valueAnnotationAttribute = new ForceAliasedAnnotationAttribute(valueAttribute, nameAttribute);
// 注解属性
Assert.assertEquals(annotation, valueAnnotationAttribute.getAnnotation());
Assert.assertEquals(annotation.annotationType(), valueAnnotationAttribute.getAnnotationType());
// 方法属性
Assert.assertEquals(valueMethod.getName(), valueAnnotationAttribute.getAttributeName());
Assert.assertEquals(valueMethod.getReturnType(), valueAnnotationAttribute.getAttributeType());
}
@Test
public void workWhenValueDefaultTest() {
// 组合属性
final Annotation annotation = ClassForTest1.class.getAnnotation(AnnotationForTest.class);
final Method valueMethod = ReflectUtil.getMethod(AnnotationForTest.class, "value");
final CacheableAnnotationAttribute valueAttribute = new CacheableAnnotationAttribute(annotation, valueMethod);
final Method nameMethod = ReflectUtil.getMethod(AnnotationForTest.class, "name");
final CacheableAnnotationAttribute nameAttribute = new CacheableAnnotationAttribute(annotation, nameMethod);
final AliasedAnnotationAttribute valueAnnotationAttribute = new AliasedAnnotationAttribute(valueAttribute, nameAttribute);
// 值处理
Assert.assertEquals("name", valueAnnotationAttribute.getValue());
Assert.assertFalse(valueAnnotationAttribute.isValueEquivalentToDefaultValue());
Assert.assertTrue(valueAnnotationAttribute.isWrapped());
}
@Test
public void workWhenValueNonDefaultTest() {
// 组合属性
final Annotation annotation = ClassForTest2.class.getAnnotation(AnnotationForTest.class);
final Method valueMethod = ReflectUtil.getMethod(AnnotationForTest.class, "value");
final CacheableAnnotationAttribute valueAttribute = new CacheableAnnotationAttribute(annotation, valueMethod);
final Method nameMethod = ReflectUtil.getMethod(AnnotationForTest.class, "name");
final CacheableAnnotationAttribute nameAttribute = new CacheableAnnotationAttribute(annotation, nameMethod);
final ForceAliasedAnnotationAttribute valueAnnotationAttribute = new ForceAliasedAnnotationAttribute(valueAttribute, nameAttribute);
// 值处理
Assert.assertEquals("", valueAnnotationAttribute.getValue());
Assert.assertTrue(valueAnnotationAttribute.isValueEquivalentToDefaultValue());
Assert.assertTrue(valueAnnotationAttribute.isWrapped());
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface AnnotationForTest {
String value() default "";
String name() default "";
}
@AnnotationForTest(name = "name", value = "value")
static class ClassForTest1 {}
@AnnotationForTest(value = "value")
static class ClassForTest2 {}
}

View File

@@ -0,0 +1,370 @@
package cn.hutool.core.annotation;
import cn.hutool.core.util.ReflectUtil;
import org.junit.Assert;
import org.junit.Test;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
/**
* 合成注解{@link GenericSynthesizedAggregateAnnotation}的测试用例
*
* @author huangchengxing
*/
public class GenericSynthesizedAggregateAnnotationTest {
@Test
public void baseSynthesisAnnotationWorkTest() {
// AnnotatedClass -> @ChildAnnotation -> @ParentAnnotation -> @GrandParentAnnotation
// -> @GrandParentAnnotation
final GrandParentAnnotation grandParentAnnotation = ChildAnnotation.class.getAnnotation(GrandParentAnnotation.class);
final ParentAnnotation parentAnnotation = ChildAnnotation.class.getAnnotation(ParentAnnotation.class);
final ChildAnnotation childAnnotation = AnnotatedClass.class.getAnnotation(ChildAnnotation.class);
final GenericSynthesizedAggregateAnnotation syntheticMetaAnnotation = new GenericSynthesizedAggregateAnnotation(childAnnotation);
// Annotation & AnnotatedElement
Assert.assertEquals(GenericSynthesizedAggregateAnnotation.class, syntheticMetaAnnotation.annotationType());
Assert.assertTrue(syntheticMetaAnnotation.isAnnotationPresent(GrandParentAnnotation.class));
Assert.assertTrue(syntheticMetaAnnotation.isAnnotationPresent(ParentAnnotation.class));
Assert.assertTrue(syntheticMetaAnnotation.isAnnotationPresent(ChildAnnotation.class));
Assert.assertEquals(grandParentAnnotation, syntheticMetaAnnotation.getAnnotation(GrandParentAnnotation.class));
Assert.assertEquals(parentAnnotation, syntheticMetaAnnotation.getAnnotation(ParentAnnotation.class));
Assert.assertEquals(childAnnotation, syntheticMetaAnnotation.getAnnotation(ChildAnnotation.class));
Assert.assertEquals(
Arrays.asList(childAnnotation, grandParentAnnotation, parentAnnotation),
Arrays.asList(syntheticMetaAnnotation.getAnnotations())
);
// 扩展方法
Assert.assertNotNull(syntheticMetaAnnotation.getSynthesizedAnnotation(GrandParentAnnotation.class));
Assert.assertNotNull(syntheticMetaAnnotation.getSynthesizedAnnotation(ParentAnnotation.class));
Assert.assertNotNull(syntheticMetaAnnotation.getSynthesizedAnnotation(ChildAnnotation.class));
Assert.assertEquals(3, syntheticMetaAnnotation.getAllSynthesizedAnnotation().size());
// 属性
Assert.assertEquals(SynthesizedAnnotationSelector.NEAREST_AND_OLDEST_PRIORITY, syntheticMetaAnnotation.getAnnotationSelector());
Assert.assertEquals(CacheableSynthesizedAnnotationAttributeProcessor.class, syntheticMetaAnnotation.getAnnotationAttributeProcessor().getClass());
Assert.assertEquals(3, syntheticMetaAnnotation.getAnnotationPostProcessors().size());
}
@Test
public void synthesisAnnotationAttributeTest() {
final ChildAnnotation rootAnnotation = AnnotatedClass.class.getAnnotation(ChildAnnotation.class);
GenericSynthesizedAggregateAnnotation syntheticMetaAnnotation = new GenericSynthesizedAggregateAnnotation(rootAnnotation);
Assert.assertEquals(syntheticMetaAnnotation.getSource(), Collections.singletonList(rootAnnotation));
Assert.assertEquals(syntheticMetaAnnotation.annotationType(), GenericSynthesizedAggregateAnnotation.class);
Assert.assertEquals(3, syntheticMetaAnnotation.getAnnotations().length);
Assert.assertEquals("Child!", syntheticMetaAnnotation.getAttributeValue("childValue", String.class));
Assert.assertEquals("Child!", syntheticMetaAnnotation.getAttributeValue("childValueAlias", String.class));
Assert.assertEquals("Child's Parent!", syntheticMetaAnnotation.getAttributeValue("parentValue", String.class));
Assert.assertEquals("Child's GrandParent!", syntheticMetaAnnotation.getAttributeValue("grandParentValue", String.class));
}
@Test
public void syntheticAnnotationTest() {
final ChildAnnotation rootAnnotation = AnnotatedClass.class.getAnnotation(ChildAnnotation.class);
GenericSynthesizedAggregateAnnotation syntheticMetaAnnotation = new GenericSynthesizedAggregateAnnotation(rootAnnotation);
final ChildAnnotation childAnnotation = syntheticMetaAnnotation.synthesize(ChildAnnotation.class);
SynthesizedAnnotation childSyntheticAnnotation = syntheticMetaAnnotation.getSynthesizedAnnotation(ChildAnnotation.class);
Assert.assertNotNull(childSyntheticAnnotation);
Assert.assertTrue(childSyntheticAnnotation.hasAttribute("childValue", String.class));
Assert.assertEquals(AnnotatedClass.class.getAnnotation(ChildAnnotation.class), childSyntheticAnnotation.getRoot());
Assert.assertEquals(AnnotatedClass.class.getAnnotation(ChildAnnotation.class), childSyntheticAnnotation.getAnnotation());
Assert.assertTrue(syntheticMetaAnnotation.isAnnotationPresent(ChildAnnotation.class));
Assert.assertNotNull(childAnnotation);
Assert.assertEquals("Child!", childAnnotation.childValue());
Assert.assertEquals("Child!", childAnnotation.childValueAlias());
Assert.assertEquals(childAnnotation.grandParentType(), Integer.class);
Assert.assertThrows(IllegalArgumentException.class, () -> new GenericSynthesizedAggregateAnnotation(childAnnotation));
final ParentAnnotation parentAnnotation = syntheticMetaAnnotation.synthesize(ParentAnnotation.class);
SynthesizedAnnotation parentSyntheticAnnotation = syntheticMetaAnnotation.getSynthesizedAnnotation(ParentAnnotation.class);
Assert.assertNotNull(parentSyntheticAnnotation);
Assert.assertTrue(parentSyntheticAnnotation.hasAttribute("parentValue", String.class));
Assert.assertEquals(AnnotatedClass.class.getAnnotation(ChildAnnotation.class), parentSyntheticAnnotation.getRoot());
Assert.assertEquals(ChildAnnotation.class.getAnnotation(ParentAnnotation.class), parentSyntheticAnnotation.getAnnotation());
Assert.assertNotNull(parentAnnotation);
Assert.assertEquals("Child's Parent!", parentAnnotation.parentValue());
Assert.assertEquals("java.lang.Void", parentAnnotation.grandParentType());
Assert.assertThrows(IllegalArgumentException.class, () -> new GenericSynthesizedAggregateAnnotation(parentAnnotation));
final GrandParentAnnotation grandParentAnnotation = syntheticMetaAnnotation.synthesize(GrandParentAnnotation.class);
SynthesizedAnnotation grandParentSyntheticAnnotation = syntheticMetaAnnotation.getSynthesizedAnnotation(GrandParentAnnotation.class);
Assert.assertNotNull(grandParentSyntheticAnnotation);
Assert.assertTrue(grandParentSyntheticAnnotation.hasAttribute("grandParentType", Class.class));
Assert.assertEquals(AnnotatedClass.class.getAnnotation(ChildAnnotation.class), grandParentSyntheticAnnotation.getRoot());
Assert.assertEquals(ChildAnnotation.class.getAnnotation(GrandParentAnnotation.class), grandParentSyntheticAnnotation.getAnnotation());
Assert.assertTrue(syntheticMetaAnnotation.isAnnotationPresent(GrandParentAnnotation.class));
Assert.assertNotNull(grandParentAnnotation);
Assert.assertEquals("Child's GrandParent!", grandParentAnnotation.grandParentValue());
Assert.assertEquals(grandParentAnnotation.grandParentType(), Integer.class);
Assert.assertThrows(IllegalArgumentException.class, () -> new GenericSynthesizedAggregateAnnotation(grandParentAnnotation));
}
@Test
public void linkTest() {
final Method method = ReflectUtil.getMethod(AnnotationForLinkTest.class, "value");
final SynthesizedAggregateAnnotation synthesizedAnnotationAggregator = new GenericSynthesizedAggregateAnnotation(method.getAnnotation(AliasFor.class));
final Link link = synthesizedAnnotationAggregator.synthesize(Link.class);
Assert.assertEquals(AnnotationForLinkTest.class, link.annotation());
Assert.assertEquals("name", link.attribute());
}
@Test
public void mirrorAttributeTest() {
AnnotationForMirrorTest annotation = ClassForMirrorTest.class.getAnnotation(AnnotationForMirrorTest.class);
SynthesizedAggregateAnnotation synthetic = new GenericSynthesizedAggregateAnnotation(annotation);
AnnotationForMirrorTest syntheticAnnotation = synthetic.synthesize(AnnotationForMirrorTest.class);
Assert.assertEquals("Foo", syntheticAnnotation.name());
Assert.assertEquals("Foo", syntheticAnnotation.value());
annotation = ClassForMirrorTest2.class.getAnnotation(AnnotationForMirrorTest.class);
synthetic = new GenericSynthesizedAggregateAnnotation(annotation);
syntheticAnnotation = synthetic.synthesize(AnnotationForMirrorTest.class);
Assert.assertEquals("Foo", syntheticAnnotation.name());
Assert.assertEquals("Foo", syntheticAnnotation.value());
annotation = ClassForMirrorTest3.class.getAnnotation(AnnotationForMirrorTest.class);
synthetic = new GenericSynthesizedAggregateAnnotation(annotation);
syntheticAnnotation = synthetic.synthesize(AnnotationForMirrorTest.class);
AnnotationForMirrorTest finalSyntheticAnnotation = syntheticAnnotation;
Assert.assertThrows(IllegalArgumentException.class, finalSyntheticAnnotation::name);
}
@Test
public void aliasForTest() {
AnnotationForAliasForTest annotation = ClassForAliasForTest.class.getAnnotation(AnnotationForAliasForTest.class);
SynthesizedAggregateAnnotation synthetic = new GenericSynthesizedAggregateAnnotation(annotation);
MetaAnnotationForAliasForTest metaAnnotation = synthetic.synthesize(MetaAnnotationForAliasForTest.class);
Assert.assertEquals("Meta", metaAnnotation.name());
AnnotationForAliasForTest childAnnotation = synthetic.synthesize(AnnotationForAliasForTest.class);
Assert.assertEquals("", childAnnotation.value());
annotation = ClassForAliasForTest2.class.getAnnotation(AnnotationForAliasForTest.class);
synthetic = new GenericSynthesizedAggregateAnnotation(annotation);
metaAnnotation = synthetic.synthesize(MetaAnnotationForAliasForTest.class);
Assert.assertEquals("Foo", metaAnnotation.name());
childAnnotation = synthetic.synthesize(AnnotationForAliasForTest.class);
Assert.assertEquals("Foo", childAnnotation.value());
}
@Test
public void forceAliasForTest() {
AnnotationForceForAliasForTest annotation = ClassForForceAliasForTest.class.getAnnotation(AnnotationForceForAliasForTest.class);
SynthesizedAggregateAnnotation synthetic = new GenericSynthesizedAggregateAnnotation(annotation);
MetaAnnotationForForceAliasForTest metaAnnotation = synthetic.synthesize(MetaAnnotationForForceAliasForTest.class);
Assert.assertEquals("", metaAnnotation.name());
AnnotationForceForAliasForTest childAnnotation = synthetic.synthesize(AnnotationForceForAliasForTest.class);
Assert.assertEquals("", childAnnotation.value());
annotation = ClassForForceAliasForTest2.class.getAnnotation(AnnotationForceForAliasForTest.class);
synthetic = new GenericSynthesizedAggregateAnnotation(annotation);
metaAnnotation = synthetic.synthesize(MetaAnnotationForForceAliasForTest.class);
Assert.assertEquals("Foo", metaAnnotation.name());
childAnnotation = synthetic.synthesize(AnnotationForceForAliasForTest.class);
Assert.assertEquals("Foo", childAnnotation.value());
}
@Test
public void aliasForAndMirrorTest() {
AnnotationForMirrorThenAliasForTest annotation = ClassForAliasForAndMirrorTest.class.getAnnotation(AnnotationForMirrorThenAliasForTest.class);
SynthesizedAggregateAnnotation synthetic = new GenericSynthesizedAggregateAnnotation(annotation);
MetaAnnotationForMirrorThenAliasForTest metaAnnotation = synthetic.synthesize(MetaAnnotationForMirrorThenAliasForTest.class);
Assert.assertEquals("test", metaAnnotation.name());
Assert.assertEquals("test", metaAnnotation.value());
AnnotationForMirrorThenAliasForTest childAnnotation = synthetic.synthesize(AnnotationForMirrorThenAliasForTest.class);
Assert.assertEquals("test", childAnnotation.childValue());
}
@Test
public void multiAliasForTest() {
final AnnotationForMultiAliasForTest annotation = ClassForMultiAliasForTest.class.getAnnotation(AnnotationForMultiAliasForTest.class);
final SynthesizedAggregateAnnotation synthetic = new GenericSynthesizedAggregateAnnotation(annotation);
final MetaAnnotationForMultiAliasForTest1 metaAnnotation1 = synthetic.synthesize(MetaAnnotationForMultiAliasForTest1.class);
Assert.assertEquals("test", metaAnnotation1.name());
Assert.assertEquals("test", metaAnnotation1.value1());
final MetaAnnotationForMultiAliasForTest2 metaAnnotation2 = synthetic.synthesize(MetaAnnotationForMultiAliasForTest2.class);
Assert.assertEquals("test", metaAnnotation2.value2());
final AnnotationForMultiAliasForTest childAnnotation = synthetic.synthesize(AnnotationForMultiAliasForTest.class);
Assert.assertEquals("test", childAnnotation.value3());
}
@Test
public void implicitAliasTest() {
final AnnotationForImplicitAliasTest annotation = ClassForImplicitAliasTest.class.getAnnotation(AnnotationForImplicitAliasTest.class);
final SynthesizedAggregateAnnotation synthetic = new GenericSynthesizedAggregateAnnotation(annotation);
final MetaAnnotationForImplicitAliasTest metaAnnotation = synthetic.synthesize(MetaAnnotationForImplicitAliasTest.class);
Assert.assertEquals("Meta", metaAnnotation.name());
Assert.assertEquals("Foo", metaAnnotation.value());
final AnnotationForImplicitAliasTest childAnnotation = synthetic.synthesize(AnnotationForImplicitAliasTest.class);
Assert.assertEquals("Foo", childAnnotation.value());
}
// 注解结构如下:
// AnnotatedClass -> @ChildAnnotation -> @ParentAnnotation -> @GrandParentAnnotation
// -> @GrandParentAnnotation
@ChildAnnotation(childValueAlias = "Child!", grandParentType = Integer.class)
static class AnnotatedClass {}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.ANNOTATION_TYPE })
@interface GrandParentAnnotation {
String grandParentValue() default "";
Class<?> grandParentType() default Void.class;
}
@GrandParentAnnotation(grandParentValue = "Parent's GrandParent!") // 覆盖元注解@GrandParentAnnotation的属性
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
@interface ParentAnnotation {
String parentValue() default "";
String grandParentType() default "java.lang.Void";
}
@GrandParentAnnotation(grandParentValue = "Child's GrandParent!") // 重复的元注解,靠近根注解的优先级高
@ParentAnnotation(parentValue = "Child's Parent!") // 覆盖元注解@ParentAnnotation的属性
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface ChildAnnotation {
String childValueAlias() default "";
@Alias("childValueAlias")
String childValue() default "";
Class<?> grandParentType() default Void.class;
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface AnnotationForMirrorTest {
//@Link(attribute = "name")
@MirrorFor(attribute = "name")
String value() default "";
//@Link(attribute = "value")
@MirrorFor(attribute = "value")
String name() default "";
}
@AnnotationForMirrorTest("Foo")
static class ClassForMirrorTest {}
@AnnotationForMirrorTest(name = "Foo")
static class ClassForMirrorTest2 {}
@AnnotationForMirrorTest(value = "Aoo", name = "Foo")
static class ClassForMirrorTest3 {}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface MetaAnnotationForAliasForTest {
String name() default "";
}
@MetaAnnotationForAliasForTest(name = "Meta")
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface AnnotationForAliasForTest {
@AliasFor(
annotation = MetaAnnotationForAliasForTest.class,
attribute = "name"
)
String value() default "";
}
@AnnotationForAliasForTest
static class ClassForAliasForTest {}
@AnnotationForAliasForTest("Foo")
static class ClassForAliasForTest2 {}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface MetaAnnotationForForceAliasForTest {
String name() default "";
}
@MetaAnnotationForForceAliasForTest(name = "Meta")
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface AnnotationForceForAliasForTest {
//@Link(
// annotation = MetaAnnotationForForceAliasForTest.class,
// attribute = "name",
// type = RelationType.FORCE_ALIAS_FOR
//)
@ForceAliasFor(annotation = MetaAnnotationForForceAliasForTest.class, attribute = "name")
String value() default "";
}
@AnnotationForceForAliasForTest
static class ClassForForceAliasForTest {}
@AnnotationForceForAliasForTest("Foo")
static class ClassForForceAliasForTest2 {}
@interface AnnotationForLinkTest {
@AliasFor(attribute = "name", annotation = AnnotationForLinkTest.class)
String value() default "value";
String name() default "name";
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface MetaAnnotationForMirrorThenAliasForTest {
@MirrorFor(attribute = "value")
String name() default "";
@MirrorFor(attribute = "name")
String value() default "";
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@MetaAnnotationForMirrorThenAliasForTest("Meta")
@interface AnnotationForMirrorThenAliasForTest {
@AliasFor(attribute = "name", annotation = MetaAnnotationForMirrorThenAliasForTest.class)
String childValue() default "value";
}
@AnnotationForMirrorThenAliasForTest(childValue = "test")
static class ClassForAliasForAndMirrorTest{}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface MetaAnnotationForMultiAliasForTest1 {
@MirrorFor(attribute = "value1")
String name() default "";
@MirrorFor(attribute = "name")
String value1() default "";
}
@MetaAnnotationForMultiAliasForTest1
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface MetaAnnotationForMultiAliasForTest2 {
@AliasFor(attribute = "name", annotation = MetaAnnotationForMultiAliasForTest1.class)
String value2() default "";
}
@MetaAnnotationForMultiAliasForTest2
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface AnnotationForMultiAliasForTest {
@AliasFor(attribute = "value2", annotation = MetaAnnotationForMultiAliasForTest2.class)
String value3() default "value";
}
@AnnotationForMultiAliasForTest(value3 = "test")
static class ClassForMultiAliasForTest{}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface MetaAnnotationForImplicitAliasTest {
@MirrorFor(attribute = "value")
String name() default "";
@MirrorFor(attribute = "name")
String value() default "";
}
@MetaAnnotationForImplicitAliasTest("Meta")
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface AnnotationForImplicitAliasTest {
String value() default "";
}
@AnnotationForImplicitAliasTest("Foo")
static class ClassForImplicitAliasTest {}
}

View File

@@ -0,0 +1,198 @@
package cn.hutool.core.annotation;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import org.junit.Assert;
import org.junit.Test;
import java.lang.annotation.*;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.function.UnaryOperator;
public class MirrorLinkAnnotationPostProcessorTest {
@Test
public void processTest() {
MirrorLinkAnnotationPostProcessor processor = new MirrorLinkAnnotationPostProcessor();
Map<Class<?>, SynthesizedAnnotation> annotationMap = new HashMap<>();
SynthesizedAggregateAnnotation synthesizedAnnotationAggregator = new TestSynthesizedAggregateAnnotation(annotationMap);
AnnotationForTest annotation = ClassForTest.class.getAnnotation(AnnotationForTest.class);
SynthesizedAnnotation synthesizedAnnotation = new TestSynthesizedAnnotation(synthesizedAnnotationAggregator, annotation);
annotationMap.put(annotation.annotationType(), synthesizedAnnotation);
processor.process(synthesizedAnnotation, synthesizedAnnotationAggregator);
AnnotationAttribute valueAttribute = synthesizedAnnotation.getAttributes().get("value");
Assert.assertEquals(ReflectUtil.getMethod(AnnotationForTest.class, "value"), valueAttribute.getAttribute());
Assert.assertTrue(valueAttribute.isWrapped());
Assert.assertEquals(MirroredAnnotationAttribute.class, valueAttribute.getClass());
AnnotationAttribute nameAttribute = synthesizedAnnotation.getAttributes().get("name");
Assert.assertEquals(ReflectUtil.getMethod(AnnotationForTest.class, "name"), nameAttribute.getAttribute());
Assert.assertTrue(nameAttribute.isWrapped());
Assert.assertEquals(MirroredAnnotationAttribute.class, nameAttribute.getClass());
Assert.assertEquals(((WrappedAnnotationAttribute)nameAttribute).getLinked(), ((WrappedAnnotationAttribute)valueAttribute).getOriginal());
Assert.assertEquals(((WrappedAnnotationAttribute)nameAttribute).getOriginal(), ((WrappedAnnotationAttribute)valueAttribute).getLinked());
}
@AnnotationForTest
static class ClassForTest {}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface AnnotationForTest {
@Link(attribute = "name", type = RelationType.MIRROR_FOR)
String value() default "";
@Link(attribute = "value", type = RelationType.MIRROR_FOR)
String name() default "";
}
static class TestSynthesizedAggregateAnnotation implements SynthesizedAggregateAnnotation {
private final Map<Class<?>, SynthesizedAnnotation> annotationMap;
public TestSynthesizedAggregateAnnotation(Map<Class<?>, SynthesizedAnnotation> annotationMap) {
this.annotationMap = annotationMap;
}
@Override
public Object getSource() {
return null;
}
@Override
public SynthesizedAnnotationSelector getAnnotationSelector() {
return null;
}
@Override
public SynthesizedAnnotationAttributeProcessor getAnnotationAttributeProcessor() {
return null;
}
@Override
public Collection<SynthesizedAnnotationPostProcessor> getAnnotationPostProcessors() {
return null;
}
@Override
public SynthesizedAnnotation getSynthesizedAnnotation(Class<?> annotationType) {
return annotationMap.get(annotationType);
}
@Override
public Map<Class<? extends Annotation>, SynthesizedAnnotation> getAllSynthesizedAnnotation() {
return null;
}
@Override
public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
return null;
}
@Override
public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
return false;
}
@Override
public Annotation[] getAnnotations() {
return new Annotation[0];
}
@Override
public <T extends Annotation> T synthesize(Class<T> annotationType) {
return null;
}
@Override
public Object getAttributeValue(String attributeName, Class<?> attributeType) {
return null;
}
@Override
public Object getRoot() {
return null;
}
}
static class TestSynthesizedAnnotation implements SynthesizedAnnotation {
private final Annotation annotation;
private final SynthesizedAggregateAnnotation owner;
private final Map<String, AnnotationAttribute> attributeMap;
public TestSynthesizedAnnotation(SynthesizedAggregateAnnotation owner, Annotation annotation) {
this.owner = owner;
this.attributeMap = new HashMap<>();
this.annotation = annotation;
for (Method declaredMethod : annotation.annotationType().getDeclaredMethods()) {
attributeMap.put(declaredMethod.getName(), new CacheableAnnotationAttribute(annotation, declaredMethod));
}
}
@Override
public Object getRoot() {
return null;
}
@Override
public Annotation getAnnotation() {
return annotation;
}
@Override
public int getVerticalDistance() {
return 0;
}
@Override
public int getHorizontalDistance() {
return 0;
}
@Override
public boolean hasAttribute(String attributeName, Class<?> returnType) {
return false;
}
@Override
public Map<String, AnnotationAttribute> getAttributes() {
return attributeMap;
}
@Override
public void setAttribute(String attributeName, AnnotationAttribute attribute) {
attributeMap.put(attributeName, attribute);
}
@Override
public void replaceAttribute(String attributeName, UnaryOperator<AnnotationAttribute> operator) {
AnnotationAttribute annotationAttribute = attributeMap.get(attributeName);
if (ObjectUtil.isNotNull(annotationAttribute)) {
attributeMap.put(attributeName, operator.apply(annotationAttribute));
}
}
@Override
public Object getAttributeValue(String attributeName) {
return null;
}
@Override
public Class<? extends Annotation> annotationType() {
return annotation.annotationType();
}
@Override
public Object getAttributeValue(String attributeName, Class<?> attributeType) {
return null;
}
}
}

View File

@@ -0,0 +1,76 @@
package cn.hutool.core.annotation;
import cn.hutool.core.util.ReflectUtil;
import org.junit.Assert;
import org.junit.Test;
import java.lang.annotation.*;
import java.lang.reflect.Method;
public class MirroredAnnotationAttributeTest {
@Test
public void baseInfoTest() {
// 组合属性
final Annotation annotation = ClassForTest1.class.getAnnotation(AnnotationForTest.class);
final Method valueMethod = ReflectUtil.getMethod(AnnotationForTest.class, "value");
final CacheableAnnotationAttribute valueAttribute = new CacheableAnnotationAttribute(annotation, valueMethod);
final Method nameMethod = ReflectUtil.getMethod(AnnotationForTest.class, "name");
final CacheableAnnotationAttribute nameAttribute = new CacheableAnnotationAttribute(annotation, nameMethod);
final MirroredAnnotationAttribute nameAnnotationAttribute = new MirroredAnnotationAttribute(nameAttribute, valueAttribute);
// 注解属性
Assert.assertEquals(annotation, nameAnnotationAttribute.getAnnotation());
Assert.assertEquals(annotation.annotationType(), nameAnnotationAttribute.getAnnotationType());
// 方法属性
Assert.assertEquals(nameMethod.getName(), nameAnnotationAttribute.getAttributeName());
Assert.assertEquals(nameMethod.getReturnType(), nameAnnotationAttribute.getAttributeType());
}
@Test
public void workWhenValueDefaultTest() {
// 组合属性
final Annotation annotation = ClassForTest2.class.getAnnotation(AnnotationForTest.class);
final Method valueMethod = ReflectUtil.getMethod(AnnotationForTest.class, "value");
final CacheableAnnotationAttribute valueAttribute = new CacheableAnnotationAttribute(annotation, valueMethod);
final Method nameMethod = ReflectUtil.getMethod(AnnotationForTest.class, "name");
final CacheableAnnotationAttribute nameAttribute = new CacheableAnnotationAttribute(annotation, nameMethod);
final MirroredAnnotationAttribute nameAnnotationAttribute = new MirroredAnnotationAttribute(nameAttribute, valueAttribute);
// 值处理
Assert.assertEquals("", nameAnnotationAttribute.getValue());
Assert.assertTrue(nameAnnotationAttribute.isValueEquivalentToDefaultValue());
Assert.assertTrue(nameAnnotationAttribute.isWrapped());
}
@Test
public void workWhenValueNonDefaultTest() {
// 组合属性
final Annotation annotation = ClassForTest1.class.getAnnotation(AnnotationForTest.class);
final Method valueMethod = ReflectUtil.getMethod(AnnotationForTest.class, "value");
final CacheableAnnotationAttribute valueAttribute = new CacheableAnnotationAttribute(annotation, valueMethod);
final Method nameMethod = ReflectUtil.getMethod(AnnotationForTest.class, "name");
final CacheableAnnotationAttribute nameAttribute = new CacheableAnnotationAttribute(annotation, nameMethod);
final MirroredAnnotationAttribute nameAnnotationAttribute = new MirroredAnnotationAttribute(nameAttribute, valueAttribute);
// 值处理
Assert.assertEquals("name", nameAnnotationAttribute.getValue());
Assert.assertFalse(nameAnnotationAttribute.isValueEquivalentToDefaultValue());
Assert.assertTrue(nameAnnotationAttribute.isWrapped());
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface AnnotationForTest {
String value() default "";
String name() default "";
}
@AnnotationForTest(value = "name")
static class ClassForTest1 {}
@AnnotationForTest
static class ClassForTest2 {}
}

View File

@@ -0,0 +1,146 @@
package cn.hutool.core.annotation;
import org.junit.Assert;
import org.junit.Test;
import java.lang.annotation.Annotation;
import java.util.Map;
import java.util.function.UnaryOperator;
public class SynthesizedAnnotationSelectorTest {
@Test
public void chooseTest() {
final SynthesizedAnnotationSelector.NearestAndOldestPrioritySelector selector = (SynthesizedAnnotationSelector.NearestAndOldestPrioritySelector)SynthesizedAnnotationSelector.NEAREST_AND_OLDEST_PRIORITY;
TestSynthesizedAnnotation annotation1 = new TestSynthesizedAnnotation(0, 0);
TestSynthesizedAnnotation annotation2 = new TestSynthesizedAnnotation(0, 0);
Assert.assertEquals(annotation1, selector.choose(annotation1, annotation2));
annotation1 = new TestSynthesizedAnnotation(0, 1);
annotation2 = new TestSynthesizedAnnotation(0, 0);
Assert.assertEquals(annotation1, selector.choose(annotation1, annotation2));
annotation1 = new TestSynthesizedAnnotation(1, 0);
annotation2 = new TestSynthesizedAnnotation(0, 0);
Assert.assertEquals(annotation2, selector.choose(annotation1, annotation2));
}
@Test
public void nearestAndNewestPriorityTest() {
final SynthesizedAnnotationSelector.NearestAndNewestPrioritySelector selector = (SynthesizedAnnotationSelector.NearestAndNewestPrioritySelector)SynthesizedAnnotationSelector.NEAREST_AND_NEWEST_PRIORITY;
TestSynthesizedAnnotation annotation1 = new TestSynthesizedAnnotation(0, 0);
TestSynthesizedAnnotation annotation2 = new TestSynthesizedAnnotation(0, 0);
Assert.assertEquals(annotation2, selector.choose(annotation1, annotation2));
annotation1 = new TestSynthesizedAnnotation(0, 1);
annotation2 = new TestSynthesizedAnnotation(0, 0);
Assert.assertEquals(annotation2, selector.choose(annotation1, annotation2));
annotation1 = new TestSynthesizedAnnotation(0, 0);
annotation2 = new TestSynthesizedAnnotation(1, 0);
Assert.assertEquals(annotation1, selector.choose(annotation1, annotation2));
}
@Test
public void farthestAndOldestPriorityTest() {
final SynthesizedAnnotationSelector.FarthestAndOldestPrioritySelector selector = (SynthesizedAnnotationSelector.FarthestAndOldestPrioritySelector)SynthesizedAnnotationSelector.FARTHEST_AND_OLDEST_PRIORITY;
TestSynthesizedAnnotation annotation1 = new TestSynthesizedAnnotation(0, 0);
TestSynthesizedAnnotation annotation2 = new TestSynthesizedAnnotation(0, 0);
Assert.assertEquals(annotation1, selector.choose(annotation1, annotation2));
annotation1 = new TestSynthesizedAnnotation(0, 1);
annotation2 = new TestSynthesizedAnnotation(0, 0);
Assert.assertEquals(annotation1, selector.choose(annotation1, annotation2));
annotation1 = new TestSynthesizedAnnotation(0, 0);
annotation2 = new TestSynthesizedAnnotation(1, 0);
Assert.assertEquals(annotation2, selector.choose(annotation1, annotation2));
}
@Test
public void farthestAndNewestPriorityTest() {
final SynthesizedAnnotationSelector.FarthestAndNewestPrioritySelector selector = (SynthesizedAnnotationSelector.FarthestAndNewestPrioritySelector)SynthesizedAnnotationSelector.FARTHEST_AND_NEWEST_PRIORITY;
TestSynthesizedAnnotation annotation1 = new TestSynthesizedAnnotation(0, 0);
TestSynthesizedAnnotation annotation2 = new TestSynthesizedAnnotation(0, 0);
Assert.assertEquals(annotation2, selector.choose(annotation1, annotation2));
annotation1 = new TestSynthesizedAnnotation(0, 1);
annotation2 = new TestSynthesizedAnnotation(0, 0);
Assert.assertEquals(annotation2, selector.choose(annotation1, annotation2));
annotation1 = new TestSynthesizedAnnotation(1, 0);
annotation2 = new TestSynthesizedAnnotation(0, 0);
Assert.assertEquals(annotation1, selector.choose(annotation1, annotation2));
}
static class TestSynthesizedAnnotation implements SynthesizedAnnotation {
private final int verticalDistance;
private final int horizontalDistance;
public TestSynthesizedAnnotation(int verticalDistance, int horizontalDistance) {
this.verticalDistance = verticalDistance;
this.horizontalDistance = horizontalDistance;
}
@Override
public Object getRoot() {
return null;
}
@Override
public Annotation getAnnotation() {
return null;
}
@Override
public int getVerticalDistance() {
return this.verticalDistance;
}
@Override
public int getHorizontalDistance() {
return this.horizontalDistance;
}
@Override
public boolean hasAttribute(String attributeName, Class<?> returnType) {
return false;
}
@Override
public Map<String, AnnotationAttribute> getAttributes() {
return null;
}
@Override
public void setAttribute(String attributeName, AnnotationAttribute attribute) {
}
@Override
public void replaceAttribute(String attributeName, UnaryOperator<AnnotationAttribute> operator) {
}
@Override
public Object getAttributeValue(String attributeName) {
return null;
}
@Override
public Class<? extends Annotation> annotationType() {
return null;
}
@Override
public Object getAttributeValue(String attributeName, Class<?> attributeType) {
return null;
}
}
}

View File

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

View File

@@ -36,7 +36,7 @@ public class TypeAnnotationScannerTest {
annotations.forEach(a -> Assert.assertEquals(a.annotationType(), AnnotationForScannerTest.class));
// 不查找父类
scanner = new TypeAnnotationScanner().setIncludeSupperClass(false);
scanner = new TypeAnnotationScanner().setIncludeSuperClass(false);
annotations = scanner.getAnnotations(Example.class);
Assert.assertEquals(1, annotations.size());
annotations.forEach(a -> Assert.assertEquals(a.annotationType(), AnnotationForScannerTest.class));
@@ -88,7 +88,7 @@ public class TypeAnnotationScannerTest {
// 不查找父类
map.clear();
new TypeAnnotationScanner()
.setIncludeSupperClass(false)
.setIncludeSuperClass(false)
.scan(
(index, annotation) -> map.computeIfAbsent(index, i -> new ArrayList<>()).add(annotation),
Example.class, null

View File

@@ -73,12 +73,16 @@ public class ListUtilTest {
lists = ListUtil.splitAvg(Arrays.asList(1, 2, 3, 4), 4);
Assert.assertEquals("[[1], [2], [3], [4]]", lists.toString());
lists = ListUtil.splitAvg(Arrays.asList(1, 2, 3), 5);
Assert.assertEquals("[[1], [2], [3], [], []]", lists.toString());
lists = ListUtil.splitAvg(Arrays.asList(1, 2, 3), 2);
Assert.assertEquals("[[1, 2], [3]]", lists.toString());
}
@Test
public void splitAvgTest2() {
List<List<Object>> lists = ListUtil.splitAvg(Arrays.asList(1, 2, 3), 5);
Assert.assertEquals("[[1], [2], [3], [], []]", lists.toString());
}
@Test(expected = IllegalArgumentException.class)
public void splitAvgNotZero() {
// limit不能小于等于0

View File

@@ -31,4 +31,16 @@ public class TemporalAccessorUtilTest {
LocalDate.of(2021, 6, 26), "#SSS");
Assert.assertEquals("1624636800000", today2);
}
@Test
public void isInTest(){
final String sourceStr = "2022-04-19 00:00:00";
final String startTimeStr = "2022-04-19 00:00:00";
final String endTimeStr = "2022-04-19 23:59:59";
final boolean between = TemporalAccessorUtil.isIn(
LocalDateTimeUtil.parse(sourceStr, DatePattern.NORM_DATETIME_FORMATTER),
LocalDateTimeUtil.parse(startTimeStr, DatePattern.NORM_DATETIME_FORMATTER),
LocalDateTimeUtil.parse(endTimeStr, DatePattern.NORM_DATETIME_FORMATTER));
Assert.assertTrue(between);
}
}

View File

@@ -142,6 +142,14 @@ public class LinkedForestMapTest {
Assert.assertFalse(c.hasChildren());
}
@Test
public void getNodeValueTest() {
final ForestMap<String, String> map = new LinkedForestMap<>(false);
map.putNode("a", "aaa");
Assert.assertEquals("aaa", map.getNodeValue("a"));
Assert.assertNull(map.getNodeValue("b"));
}
@Test
public void putAllNodeTest() {
final ForestMap<String, Map<String, String>> map = new LinkedForestMap<>(false);

View File

@@ -534,4 +534,13 @@ public class ArrayUtilTest {
String[] newArr = ArrayUtil.setOrAppend(arr, 0, "Good");// ClassCastException
Assert.assertArrayEquals(new String[]{"Good"}, newArr);
}
@Test
public void getAnyTest() {
final String[] a = {"a", "b", "c", "d", "e"};
final Object o = ArrayUtil.getAny(a, 3, 4);
final String[] resultO = (String[]) o;
final String[] c = {"d", "e"};
Assert.assertTrue(ArrayUtil.containsAll(c, resultO[0], resultO[1]));
}
}