添加@MirrorFor和@AliasFor注解

This commit is contained in:
huangchengxing 2022-07-13 22:31:48 +08:00
parent e4a7576d7f
commit 491c53e7dd
19 changed files with 505 additions and 91 deletions

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 AnnotationAttributeWrapper}的基本实现
*
* @author huangchengxing
* @see ForceAliasedAnnotationAttribute
* @see AliasedAnnotationAttribute
* @see MirroredAnnotationAttribute
*/
public abstract class AbstractAnnotationAttributeWrapper implements AnnotationAttributeWrapper {
protected final AnnotationAttribute original;
protected final AnnotationAttribute linked;
protected AbstractAnnotationAttributeWrapper(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() ? ((AnnotationAttributeWrapper)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;
}
AnnotationAttributeWrapper wrappedAttribute = (AnnotationAttributeWrapper)curr;
collectLeafAttribute(wrappedAttribute.getOriginal(), leafAttributes);
collectLeafAttribute(wrappedAttribute.getLinked(), leafAttributes);
}
}

View File

@ -26,36 +26,36 @@ public class AliasAttributePostProcessor implements SynthesizedAnnotationPostPro
@Override
public void process(SynthesizedAnnotation annotation, SyntheticAnnotation syntheticAnnotation) {
Map<String, AnnotationAttribute> attributeMap = annotation.getAttributes();
final Map<String, AnnotationAttribute> attributeMap = annotation.getAttributes();
// 记录别名与属性的关系
ForestMap<String, AnnotationAttribute> attributeAliasMappings = new LinkedForestMap<>(false);
final ForestMap<String, AnnotationAttribute> attributeAliasMappings = new LinkedForestMap<>(false);
attributeMap.forEach((attributeName, attribute) -> {
String alias = Opt.ofNullable(attribute.getAnnotation(Alias.class))
final String alias = Opt.ofNullable(attribute.getAnnotation(Alias.class))
.map(Alias::value)
.orElse(null);
if (ObjectUtil.isNull(alias)) {
return;
}
AnnotationAttribute aliasAttribute = attributeMap.get(alias);
final AnnotationAttribute aliasAttribute = attributeMap.get(alias);
Assert.notNull(aliasAttribute, "no method for alias: [{}]", alias);
attributeAliasMappings.putLinkedNodes(alias, aliasAttribute, attributeName, attribute);
});
// 处理别名
attributeMap.forEach((attributeName, attribute) -> {
AnnotationAttribute resolvedAttributeMethod = Opt.ofNullable(attributeName)
final AnnotationAttribute resolvedAttribute = Opt.ofNullable(attributeName)
.map(attributeAliasMappings::getRootNode)
.map(TreeEntry::getValue)
.map(aliasAttribute -> (AnnotationAttribute)new ForceAliasedAnnotationAttribute(attribute, aliasAttribute))
.orElse(attribute);
Assert.isTrue(
ObjectUtil.isNull(resolvedAttributeMethod)
|| ClassUtil.isAssignable(attribute.getAttribute().getReturnType(), resolvedAttributeMethod.getAttribute().getReturnType()),
ObjectUtil.isNull(resolvedAttribute)
|| ClassUtil.isAssignable(attribute.getAttributeType(), resolvedAttribute.getAttributeType()),
"return type of the root alias method [{}] is inconsistent with the original [{}]",
resolvedAttributeMethod.getClass(), attribute.getAttribute().getReturnType()
resolvedAttribute.getClass(), attribute.getAttributeType()
);
attributeMap.put(attributeName, resolvedAttributeMethod);
attributeMap.put(attributeName, resolvedAttribute);
});
annotation.setAttributes(attributeMap);
}

View File

@ -0,0 +1,35 @@
package cn.hutool.core.annotation;
import java.lang.annotation.*;
/**
* <p>表示原始属性将作为关联属性的别名
* <ul>
* <li>原始属性为默认值时获取关联属性将返回关联属性本身的值</li>
* <li>原始属性不为默认值时获取关联属性将返回原始属性的值</li>
* </ul>
* <b>注意该注解与{@link Link}{@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 {
/**
* 产生关联的注解类型当不指定时默认指注释的属性所在的类
*/
@Link(annotation = Link.class, attribute = "annotation", type = RelationType.FORCE_ALIAS_FOR)
Class<? extends Annotation> annotation() default Annotation.class;
/**
* {@link #annotation()}指定注解中关联的属性
*/
@Link(annotation = Link.class, attribute = "attribute", type = RelationType.FORCE_ALIAS_FOR)
String attribute() default "";
}

View File

@ -1,9 +1,11 @@
package cn.hutool.core.annotation;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.util.ObjectUtil;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BinaryOperator;
/**
* 处理注解中带有{@link Link}注解{@link Link#type()}{@link RelationType#FORCE_ALIAS_FOR}
@ -22,7 +24,7 @@ public class AliasForLinkAttributePostProcessor implements SynthesizedAnnotation
@Override
public void process(SynthesizedAnnotation annotation, SyntheticAnnotation syntheticAnnotation) {
Map<String, AnnotationAttribute> attributeMap = new HashMap<>(annotation.getAttributes());
final Map<String, AnnotationAttribute> attributeMap = new HashMap<>(annotation.getAttributes());
attributeMap.forEach((originalAttributeName, originalAttribute) -> {
// 获取注解
final Link link = SyntheticAnnotationUtil.getLink(
@ -33,7 +35,7 @@ public class AliasForLinkAttributePostProcessor implements SynthesizedAnnotation
}
// 获取注解属性
SynthesizedAnnotation aliasAnnotation = SyntheticAnnotationUtil.getLinkedAnnotation(link, syntheticAnnotation, annotation.annotationType());
final SynthesizedAnnotation aliasAnnotation = SyntheticAnnotationUtil.getLinkedAnnotation(link, syntheticAnnotation, annotation.annotationType());
if (ObjectUtil.isNull(aliasAnnotation)) {
return;
}
@ -43,12 +45,40 @@ public class AliasForLinkAttributePostProcessor implements SynthesizedAnnotation
// aliasFor
if (RelationType.ALIAS_FOR.equals(link.type())) {
aliasAnnotation.setAttributes(aliasAttribute.getAttributeName(), new AliasedAnnotationAttribute(aliasAttribute, originalAttribute));
wrappingLinkedAttribute(syntheticAnnotation, originalAttribute, aliasAttribute, AliasedAnnotationAttribute::new);
return;
}
// forceAliasFor
aliasAnnotation.setAttributes(aliasAttribute.getAttributeName(), new ForceAliasedAnnotationAttribute(aliasAttribute, originalAttribute));
wrappingLinkedAttribute(syntheticAnnotation, originalAttribute, aliasAttribute, ForceAliasedAnnotationAttribute::new);
});
}
/**
* 对指定注解属性进行包装若该属性已被包装过则递归以其为根节点的树结构对树上全部的叶子节点进行包装
*/
private void wrappingLinkedAttribute(
SyntheticAnnotation syntheticAnnotation, AnnotationAttribute originalAttribute, AnnotationAttribute aliasAttribute, BinaryOperator<AnnotationAttribute> wrapping) {
// 不是包装属性
if (!aliasAttribute.isWrapped()) {
processAttribute(syntheticAnnotation, originalAttribute, aliasAttribute, wrapping);
return;
}
// 是包装属性
final AbstractAnnotationAttributeWrapper wrapper = (AbstractAnnotationAttributeWrapper)aliasAttribute;
wrapper.getAllLinkedNonWrappedAttributes().forEach(
t -> processAttribute(syntheticAnnotation, originalAttribute, t, wrapping)
);
}
/**
* 获取指定注解属性然后将其再进行一层包装
*/
private void processAttribute(
SyntheticAnnotation syntheticAnnotation, AnnotationAttribute originalAttribute,
AnnotationAttribute target, BinaryOperator<AnnotationAttribute> wrapping) {
Opt.ofNullable(target.getAnnotationType())
.map(syntheticAnnotation::getSynthesizedAnnotation)
.ifPresent(t -> t.replaceAttribute(target.getAttributeName(), old -> wrapping.apply(old, originalAttribute)));
}
}

View File

@ -1,29 +1,37 @@
package cn.hutool.core.annotation;
import cn.hutool.core.lang.Assert;
/**
* <p>表示一个具有别名的属性
* 当别名属性值为默认值时优先返回原属性的值当别名属性不为默认值时优先返回别名属性的值
*
* @author huangchengxing
* @see AliasForLinkAttributePostProcessor
* @see RelationType#ALIAS_BY
* @see RelationType#FORCE_ALIAS_FOR
* @see RelationType#ALIAS_FOR
*/
public class AliasedAnnotationAttribute extends AnnotationAttributeWrapper implements AnnotationAttribute {
public class AliasedAnnotationAttribute extends AbstractAnnotationAttributeWrapper {
private final AnnotationAttribute aliasAttribute;
protected AliasedAnnotationAttribute(AnnotationAttribute origin, AnnotationAttribute aliasAttribute) {
super(origin);
Assert.notNull(aliasAttribute, "aliasAttribute must not null");
this.aliasAttribute = aliasAttribute;
protected AliasedAnnotationAttribute(AnnotationAttribute origin, AnnotationAttribute linked) {
super(origin, linked);
}
/**
* {@link #linked}为默认值则返回{@link #original}的值否则返回{@link #linked}的值
*
* @return 属性值
*/
@Override
public Object getValue() {
return aliasAttribute.isValueEquivalentToDefaultValue() ? super.getValue() : aliasAttribute.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

@ -16,8 +16,9 @@ import java.lang.reflect.Method;
*
* @author huangchengxing
* @see SynthesizedAnnotationPostProcessor
* @see CacheableAnnotationAttribute
* @see AnnotationAttributeWrapper
* @see CacheableAnnotationAttribute
* @see AbstractAnnotationAttributeWrapper
* @see ForceAliasedAnnotationAttribute
* @see AliasedAnnotationAttribute
* @see MirroredAnnotationAttribute
@ -38,6 +39,15 @@ public interface AnnotationAttribute {
*/
Method getAttribute();
/**
* 获取声明属性的注解类
*
* @return 声明注解的注解类
*/
default Class<?> getAnnotationType() {
return getAttribute().getDeclaringClass();
}
/**
* 获取属性名称
*
@ -90,7 +100,7 @@ public interface AnnotationAttribute {
* @return boolean
*/
default boolean isWrapped() {
return this instanceof AnnotationAttributeWrapper;
return false;
}
}

View File

@ -1,14 +1,25 @@
package cn.hutool.core.annotation;
import cn.hutool.core.lang.Assert;
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>当包装类被包装了多层后则规则生效优先级按包装的先后顺序倒序排序
* 比如ab互为镜像此时ab两属性应当都被{@link MirroredAnnotationAttribute}包装
* 若再指定c为a的别名字段则cab都要在原基础上再次包装一层{@link AliasedAnnotationAttribute}<br>
* 此时ab同时被包装了两层则执行时优先执行{@link AliasedAnnotationAttribute}的逻辑
* 当该规则不生效时比如c只有默认值此时上一次的{@link MirroredAnnotationAttribute}的逻辑才会生效
*
* <p>被包装的{@link AnnotationAttribute}实际结构为一颗二叉树
* 当包装类再次被包装时实际上等于又添加了一个新的根节点
* 此时需要同时更新树的全部关联叶子节点
*
* @author huangchengxing
* @see AnnotationAttribute
@ -16,42 +27,101 @@ import java.lang.reflect.Method;
* @see AliasedAnnotationAttribute
* @see MirroredAnnotationAttribute
*/
public abstract class AnnotationAttributeWrapper implements AnnotationAttribute {
public interface AnnotationAttributeWrapper extends AnnotationAttribute {
protected final AnnotationAttribute origin;
// =========================== 新增方法 ===========================
protected AnnotationAttributeWrapper(AnnotationAttribute origin) {
Assert.notNull(origin, "target must not null");
this.origin = origin;
/**
* 获取被包装的{@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
public Annotation getAnnotation() {
return origin.getAnnotation();
default Method getAttribute() {
return getOriginal().getAttribute();
}
/**
* 该注解属性的值是否等于默认值 <br>
* 默认仅当{@link #getOriginal()}{@link #getLinked()}返回的注解属性
* 都为默认值时才返回{@code true}
*
* @return 该注解属性的值是否等于默认值
*/
@Override
public Method getAttribute() {
return origin.getAttribute();
default boolean isValueEquivalentToDefaultValue() {
return getOriginal().isValueEquivalentToDefaultValue() && getLinked().isValueEquivalentToDefaultValue();
}
/**
* 获取属性类型
*
* @return 属性类型
*/
@Override
public boolean isValueEquivalentToDefaultValue() {
return origin.isValueEquivalentToDefaultValue();
default Class<?> getAttributeType() {
return getOriginal().getAttributeType();
}
/**
* 获取属性上的注解
*
* @param annotationType 注解类型
* @return 注解对象
*/
@Override
public Class<?> getAttributeType() {
return origin.getAttributeType();
default <T extends Annotation> T getAnnotation(Class<T> annotationType) {
return getOriginal().getAnnotation(annotationType);
}
/**
* 当前注解属性是否已经被{@link AnnotationAttributeWrapper}包装
*
* @return boolean
*/
@Override
public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
return origin.getAnnotation(annotationType);
}
@Override
public boolean isWrapped() {
default boolean isWrapped() {
return true;
}
}

View File

@ -359,7 +359,8 @@ public class AnnotationUtil {
* @see SyntheticAnnotation
*/
public static <T extends Annotation> T getSynthesisAnnotation(Annotation annotation, Class<T> annotationType) {
return SyntheticAnnotation.of(annotation).getAnnotation(annotationType);
// TODO 缓存合成注解信息避免重复解析
return SyntheticAnnotation.of(annotation).syntheticAnnotation(annotationType);
}
/**
@ -385,6 +386,30 @@ public class AnnotationUtil {
.collect(Collectors.toList());
}
/**
* 获取元素上距离指定元素最接近的合成注解
* <ul>
* <li>若元素是类则递归解析全部父类和全部父接口上的注解;</li>
* <li>若元素是方法属性或注解则只解析其直接声明的注解;</li>
* </ul>
*
* @param annotatedEle {@link AnnotatedElement}可以是ClassMethodFieldConstructorReflectPermission
* @param annotationType 注解类
* @param <T> 注解类型
* @return 合成注解
* @see SyntheticAnnotation
*/
public static <T extends Annotation> T getSyntheticAnnotation(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)
.findFirst()
.orElse(null);
}
/**
* 扫描注解类以及注解类的{@link Class}层级结构中的注解将返回除了{@link #META_ANNOTATIONS}中指定的JDK默认注解外
* 按元注解对象与{@code annotationType}的距离和{@link Class#getAnnotations()}顺序排序的注解对象集合

View File

@ -8,7 +8,7 @@ import java.util.Collection;
import java.util.Comparator;
/**
* 带缓存功能的{@link SynthesizedAnnotationAttributeProcessor}实现
* <p>带缓存功能的{@link SynthesizedAnnotationAttributeProcessor}实现
* 构建时需要传入比较器获取属性值时将根据比较器对合成注解进行排序
* 然后选择具有所需属性的排序最靠前的注解用于获取属性值
*

View File

@ -1,40 +1,49 @@
package cn.hutool.core.annotation;
import cn.hutool.core.lang.Assert;
/**
* 表示一个被指定了强制别名的注解属性
* 当调用{@link #getValue()}总是返回{@link #aliasAttribute}的值
* 当调用{@link #getValue()}总是返回{@link #linked}的值
*
* @author huangchengxing
* @see AliasAttributePostProcessor
* @see AliasForLinkAttributePostProcessor
* @see RelationType#FORCE_ALIAS_BY
* @see RelationType#ALIAS_FOR
* @see RelationType#FORCE_ALIAS_FOR
*/
public class ForceAliasedAnnotationAttribute extends AnnotationAttributeWrapper implements AnnotationAttribute {
public class ForceAliasedAnnotationAttribute extends AbstractAnnotationAttributeWrapper {
private final AnnotationAttribute aliasAttribute;
protected ForceAliasedAnnotationAttribute(AnnotationAttribute origin, AnnotationAttribute aliasAttribute) {
super(origin);
Assert.notNull(aliasAttribute, "aliasAttribute must not null");
this.aliasAttribute = aliasAttribute;
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 aliasAttribute.getValue();
return linked.getValue();
}
/**
* 总是返回{@link #linked}{@link AnnotationAttribute#isValueEquivalentToDefaultValue()}的返回值
*
* @return {@link #linked}{@link AnnotationAttribute#isValueEquivalentToDefaultValue()}的返回值
*/
@Override
public boolean isValueEquivalentToDefaultValue() {
return aliasAttribute.isValueEquivalentToDefaultValue();
return linked.isValueEquivalentToDefaultValue();
}
/**
* 总是返回{@link #linked}{@link AnnotationAttribute#getAttributeType()}的返回值
*
* @return {@link #linked}{@link AnnotationAttribute#getAttributeType()}的返回值
*/
@Override
public Class<?> getAttributeType() {
return aliasAttribute.getAttributeType();
return linked.getAttributeType();
}
}

View File

@ -5,7 +5,7 @@ import java.lang.annotation.*;
/**
* <p>用于在同一注解中或具有一定关联的不同注解的属性中表明这些属性之间具有特定的关联关系
* 在通过{@link SyntheticAnnotation}获取合成注解后合成注解获取属性值时会根据该注解进行调整<br />
* <b>注意该注解的优先级低于{@link Alias}
* <b>注意该注解的优先级低于{@link Alias}且与{@link MirrorFor}{@link AliasFor}一起使用时将只有被声明在最上面的注解会生效</b>
*
* @author huangchengxing
* @see SyntheticAnnotation

View File

@ -0,0 +1,38 @@
package cn.hutool.core.annotation;
import java.lang.annotation.*;
/**
* <p>表示注解的属性与指定的属性互为镜像通过一个属性将能够获得对方的值<br>
* 它们遵循下述规则
* <ul>
* <li>互为镜像的两个属性必须同时通过指定模式为{@code MIRROR_FOR}{@link Link}注解指定对方</li>
* <li>互为镜像的两个属性类型必须一致</li>
* <li>互为镜像的两个属性在获取值且两者的值皆不同时必须且仅允许有一个非默认值该值被优先返回</li>
* <li>互为镜像的两个属性在值都为默认值或都不为默认值时两者的值必须相等</li>
* </ul>
* <b>注意该注解与{@link Link}{@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 {
/**
* 产生关联的注解类型当不指定时默认指注释的属性所在的类
*/
@Link(annotation = Link.class, attribute = "annotation", type = RelationType.FORCE_ALIAS_FOR)
Class<? extends Annotation> annotation() default Annotation.class;
/**
* {@link #annotation()}指定注解中关联的属性
*/
@Link(annotation = Link.class, attribute = "attribute", type = RelationType.FORCE_ALIAS_FOR)
String attribute() default "";
}

View File

@ -46,10 +46,10 @@ public class MirrorLinkAttributePostProcessor implements SynthesizedAnnotationPo
// 包装这一对镜像属性并替换原注解中的对应属性
final AnnotationAttribute mirroredOriginalAttribute = new MirroredAnnotationAttribute(originalAttribute, mirrorAttribute);
syntheticAnnotation.getSynthesizedAnnotation(originalAttribute.getAttribute().getDeclaringClass())
.setAttributes(originalAttributeName, mirroredOriginalAttribute);
syntheticAnnotation.getSynthesizedAnnotation(originalAttribute.getAnnotationType())
.setAttribute(originalAttributeName, mirroredOriginalAttribute);
final AnnotationAttribute mirroredTargetAttribute = new MirroredAnnotationAttribute(mirrorAttribute, originalAttribute);
mirrorAnnotation.setAttributes(link.attribute(), mirroredTargetAttribute);
mirrorAnnotation.setAttribute(link.attribute(), mirroredTargetAttribute);
});
}
@ -59,7 +59,7 @@ public class MirrorLinkAttributePostProcessor implements SynthesizedAnnotationPo
// 镜像属性返回值必须一致
SyntheticAnnotationUtil.checkAttributeType(original, mirror);
// 镜像属性上必须存在对应的注解
final Link mirrorAttributeAnnotation = mirror.getAnnotation(Link.class);
final Link mirrorAttributeAnnotation = SyntheticAnnotationUtil.getLink(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 [{}]",

View File

@ -9,28 +9,25 @@ import cn.hutool.core.lang.Assert;
* @see MirrorLinkAttributePostProcessor
* @see RelationType#MIRROR_FOR
*/
public class MirroredAnnotationAttribute extends AnnotationAttributeWrapper implements AnnotationAttribute {
public class MirroredAnnotationAttribute extends AbstractAnnotationAttributeWrapper {
private final AnnotationAttribute mirrorAttribute;
public MirroredAnnotationAttribute(AnnotationAttribute origin, AnnotationAttribute mirrorAttribute) {
super(origin);
this.mirrorAttribute = mirrorAttribute;
public MirroredAnnotationAttribute(AnnotationAttribute origin, AnnotationAttribute linked) {
super(origin, linked);
}
@Override
public Object getValue() {
boolean originIsDefault = origin.isValueEquivalentToDefaultValue();
boolean targetIsDefault = mirrorAttribute.isValueEquivalentToDefaultValue();
Object originValue = origin.getValue();
Object targetValue = mirrorAttribute.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: [{}] <==> [{}]",
origin.getAttribute(), mirrorAttribute.getAttribute(), originValue, targetValue
original.getAttribute(), linked.getAttribute(), originValue, targetValue
);
return originValue;
}
@ -38,4 +35,14 @@ public class MirroredAnnotationAttribute extends AnnotationAttributeWrapper impl
// 两者有一者不为默认值时优先返回非默认值
return originIsDefault ? targetValue : originValue;
}
/**
* {@link #original}{@link #linked}都为默认值时返回{@code true}
*
* @return 是否
*/
@Override
public boolean isValueEquivalentToDefaultValue() {
return original.isValueEquivalentToDefaultValue() && linked.isValueEquivalentToDefaultValue();
}
}

View File

@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil;
import java.lang.annotation.Annotation;
import java.util.Map;
import java.util.function.UnaryOperator;
/**
* 用于在{@link SyntheticAnnotation}中表示一个处于合成状态的注解对象
@ -82,7 +83,7 @@ public interface SynthesizedAnnotation extends Annotation {
*/
default void setAttributes(Map<String, AnnotationAttribute> attributes) {
if (CollUtil.isNotEmpty(attributes)) {
attributes.forEach(this::setAttributes);
attributes.forEach(this::setAttribute);
}
}
@ -92,7 +93,15 @@ public interface SynthesizedAnnotation extends Annotation {
* @param attributeName 属性名称
* @param attribute 注解属性
*/
void setAttributes(String attributeName, AnnotationAttribute attribute);
void setAttribute(String attributeName, AnnotationAttribute attribute);
/**
* 替换属性值
*
* @param attributeName 属性名
* @param operator 替换操作
*/
void replaceAttribute(String attributeName, UnaryOperator<AnnotationAttribute> operator);
/**
* 获取属性值

View File

@ -88,7 +88,7 @@ class SyntheticAnnotationProxy implements InvocationHandler {
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("setAttributes", (method, args) -> {
methods.put("setAttribute", (method, args) -> {
throw new UnsupportedOperationException("proxied annotation can not reset attributes");
});
methods.put("getAttributeValue", (method, args) -> annotation.getAttributeValue((String)args[0]));

View File

@ -23,7 +23,7 @@ class SyntheticAnnotationUtil {
*/
static Link getLink(AnnotationAttribute attribute, RelationType... relationTypes) {
return Opt.ofNullable(attribute)
.map(t -> t.getAnnotation(Link.class))
.map(t -> AnnotationUtil.getSyntheticAnnotation(attribute.getAttribute(), Link.class))
.filter(a -> ArrayUtil.contains(relationTypes, a.type()))
.get();
}

View File

@ -10,6 +10,7 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.*;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -117,6 +118,7 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation {
this.metaAnnotationMap = new LinkedHashMap<>();
// 初始化元注解信息并进行后置处理
// TODO 缓存元注解信息避免重复解析
loadMetaAnnotations();
annotationPostProcessors.forEach(processor ->
metaAnnotationMap.values().forEach(synthesized -> processor.process(synthesized, this))
@ -411,10 +413,24 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation {
* @param attribute 注解属性
*/
@Override
public void setAttributes(String attributeName, AnnotationAttribute attribute) {
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));
}
}
/**
* 获取属性值
*

View File

@ -1,5 +1,6 @@
package cn.hutool.core.annotation;
import cn.hutool.core.util.ReflectUtil;
import org.junit.Assert;
import org.junit.Test;
@ -7,6 +8,7 @@ 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;
/**
* 合成注解{@link SyntheticMetaAnnotation}的测试用例
@ -53,6 +55,15 @@ public class SyntheticMetaAnnotationTest {
Assert.assertThrows(IllegalArgumentException.class, () -> new SyntheticMetaAnnotation(grandParentAnnotation));
}
@Test
public void linkTest() {
Method method = ReflectUtil.getMethod(AnnotationForLinkTest.class, "value");
SyntheticAnnotation syntheticAnnotation = new SyntheticMetaAnnotation(method.getAnnotation(AliasFor.class));
Link link = syntheticAnnotation.syntheticAnnotation(Link.class);
Assert.assertEquals(AnnotationForLinkTest.class, link.annotation());
Assert.assertEquals("name", link.attribute());
}
@Test
public void mirrorAttributeTest() {
AnnotationForMirrorTest annotation = ClassForMirrorTest.class.getAnnotation(AnnotationForMirrorTest.class);
@ -108,6 +119,31 @@ public class SyntheticMetaAnnotationTest {
Assert.assertEquals("Foo", childAnnotation.value());
}
@Test
public void aliasForAndMirrorTest() {
AnnotationForMirrorThenAliasForTest annotation = ClassForAliasForAndMirrorTest.class.getAnnotation(AnnotationForMirrorThenAliasForTest.class);
SyntheticAnnotation synthetic = new SyntheticMetaAnnotation(annotation);
MetaAnnotationForMirrorThenAliasForTest metaAnnotation = synthetic.syntheticAnnotation(MetaAnnotationForMirrorThenAliasForTest.class);
Assert.assertEquals("test", metaAnnotation.name());
Assert.assertEquals("test", metaAnnotation.value());
AnnotationForMirrorThenAliasForTest childAnnotation = synthetic.syntheticAnnotation(AnnotationForMirrorThenAliasForTest.class);
Assert.assertEquals("test", childAnnotation.childValue());
}
@Test
public void multiAliasForTest() {
AnnotationForMultiAliasForTest annotation = ClassForMultiAliasForTest.class.getAnnotation(AnnotationForMultiAliasForTest.class);
SyntheticAnnotation synthetic = new SyntheticMetaAnnotation(annotation);
MetaAnnotationForMultiAliasForTest1 metaAnnotation1 = synthetic.syntheticAnnotation(MetaAnnotationForMultiAliasForTest1.class);
Assert.assertEquals("test", metaAnnotation1.name());
Assert.assertEquals("test", metaAnnotation1.value1());
MetaAnnotationForMultiAliasForTest2 metaAnnotation2 = synthetic.syntheticAnnotation(MetaAnnotationForMultiAliasForTest2.class);
Assert.assertEquals("test", metaAnnotation2.value2());
AnnotationForMultiAliasForTest childAnnotation = synthetic.syntheticAnnotation(AnnotationForMultiAliasForTest.class);
Assert.assertEquals("test", childAnnotation.value3());
}
// 注解结构如下
// AnnotatedClass -> @ChildAnnotation -> @ParentAnnotation -> @GrandParentAnnotation
// -> @GrandParentAnnotation
@ -143,9 +179,11 @@ public class SyntheticMetaAnnotationTest {
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface AnnotationForMirrorTest {
@Link(attribute = "name")
//@Link(attribute = "name")
@MirrorFor(attribute = "name")
String value() default "";
@Link(attribute = "value")
//@Link(attribute = "value")
@MirrorFor(attribute = "value")
String name() default "";
}
@AnnotationForMirrorTest("Foo")
@ -164,10 +202,9 @@ public class SyntheticMetaAnnotationTest {
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface AnnotationForAliasForTest {
@Link(
@AliasFor(
annotation = MetaAnnotationForAliasForTest.class,
attribute = "name",
type = RelationType.ALIAS_FOR
attribute = "name"
)
String value() default "";
}
@ -197,4 +234,53 @@ public class SyntheticMetaAnnotationTest {
@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{}
}