1.添加@Link注解以及对应的关系枚举

2.添加注解属性对象与合成注解后置处理器,用于支持基于@Alias与@Link的枚举字段映射功能
This commit is contained in:
huangchengxing 2022-07-13 20:38:18 +08:00
parent 72b9741928
commit e4a7576d7f
19 changed files with 987 additions and 34 deletions

View File

@ -0,0 +1,63 @@
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;
/**
* 用于处理合成注解属性中{@link Alias}的映射关系
* 该处理器会令{@link Alias#value()}指向的属性值强制覆盖当前属性
*
* @author huangchengxing
* @see ForceAliasedAnnotationAttribute
*/
public class AliasAttributePostProcessor implements SynthesizedAnnotationPostProcessor {
@Override
public int order() {
return Integer.MIN_VALUE;
}
@Override
public void process(SynthesizedAnnotation annotation, SyntheticAnnotation syntheticAnnotation) {
Map<String, AnnotationAttribute> attributeMap = annotation.getAttributes();
// 记录别名与属性的关系
ForestMap<String, AnnotationAttribute> attributeAliasMappings = new LinkedForestMap<>(false);
attributeMap.forEach((attributeName, attribute) -> {
String alias = Opt.ofNullable(attribute.getAnnotation(Alias.class))
.map(Alias::value)
.orElse(null);
if (ObjectUtil.isNull(alias)) {
return;
}
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)
.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()),
"return type of the root alias method [{}] is inconsistent with the original [{}]",
resolvedAttributeMethod.getClass(), attribute.getAttribute().getReturnType()
);
attributeMap.put(attributeName, resolvedAttributeMethod);
});
annotation.setAttributes(attributeMap);
}
}

View File

@ -0,0 +1,54 @@
package cn.hutool.core.annotation;
import cn.hutool.core.util.ObjectUtil;
import java.util.HashMap;
import java.util.Map;
/**
* 处理注解中带有{@link Link}注解{@link Link#type()}{@link RelationType#FORCE_ALIAS_FOR}
* {@link RelationType#ALIAS_FOR}的属性
*
* @author huangchengxing
* @see ForceAliasedAnnotationAttribute
* @see AliasedAnnotationAttribute
*/
public class AliasForLinkAttributePostProcessor implements SynthesizedAnnotationPostProcessor {
@Override
public int order() {
return Integer.MIN_VALUE + 2;
}
@Override
public void process(SynthesizedAnnotation annotation, SyntheticAnnotation syntheticAnnotation) {
Map<String, AnnotationAttribute> attributeMap = new HashMap<>(annotation.getAttributes());
attributeMap.forEach((originalAttributeName, originalAttribute) -> {
// 获取注解
final Link link = SyntheticAnnotationUtil.getLink(
originalAttribute, RelationType.ALIAS_FOR, RelationType.FORCE_ALIAS_FOR
);
if (ObjectUtil.isNull(link)) {
return;
}
// 获取注解属性
SynthesizedAnnotation aliasAnnotation = SyntheticAnnotationUtil.getLinkedAnnotation(link, syntheticAnnotation, annotation.annotationType());
if (ObjectUtil.isNull(aliasAnnotation)) {
return;
}
final AnnotationAttribute aliasAttribute = aliasAnnotation.getAttributes().get(link.attribute());
SyntheticAnnotationUtil.checkLinkedAttributeNotNull(originalAttribute, aliasAttribute, link);
SyntheticAnnotationUtil.checkAttributeType(originalAttribute, aliasAttribute);
// aliasFor
if (RelationType.ALIAS_FOR.equals(link.type())) {
aliasAnnotation.setAttributes(aliasAttribute.getAttributeName(), new AliasedAnnotationAttribute(aliasAttribute, originalAttribute));
return;
}
// forceAliasFor
aliasAnnotation.setAttributes(aliasAttribute.getAttributeName(), new ForceAliasedAnnotationAttribute(aliasAttribute, originalAttribute));
});
}
}

View File

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

View File

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

View File

@ -0,0 +1,57 @@
package cn.hutool.core.annotation;
import cn.hutool.core.lang.Assert;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
/**
* <p>表示一个被包装过的{@link AnnotationAttribute}
* 该实例中的一些方法可能会被代理到其他的注解属性对象中
* 从而使得通过原始的注解属性的方法获取到另一注解属性的值
*
* @author huangchengxing
* @see AnnotationAttribute
* @see ForceAliasedAnnotationAttribute
* @see AliasedAnnotationAttribute
* @see MirroredAnnotationAttribute
*/
public abstract class AnnotationAttributeWrapper implements AnnotationAttribute {
protected final AnnotationAttribute origin;
protected AnnotationAttributeWrapper(AnnotationAttribute origin) {
Assert.notNull(origin, "target must not null");
this.origin = origin;
}
@Override
public Annotation getAnnotation() {
return origin.getAnnotation();
}
@Override
public Method getAttribute() {
return origin.getAttribute();
}
@Override
public boolean isValueEquivalentToDefaultValue() {
return origin.isValueEquivalentToDefaultValue();
}
@Override
public Class<?> getAttributeType() {
return origin.getAttributeType();
}
@Override
public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
return origin.getAnnotation(annotationType);
}
@Override
public boolean isWrapped() {
return true;
}
}

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

@ -39,7 +39,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

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

View File

@ -0,0 +1,34 @@
package cn.hutool.core.annotation;
import java.lang.annotation.*;
/**
* <p>用于在同一注解中或具有一定关联的不同注解的属性中表明这些属性之间具有特定的关联关系
* 在通过{@link SyntheticAnnotation}获取合成注解后合成注解获取属性值时会根据该注解进行调整<br />
* <b>注意该注解的优先级低于{@link Alias}
*
* @author huangchengxing
* @see SyntheticAnnotation
* @see RelationType
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
public @interface Link {
/**
* 产生关联的注解类型当不指定时默认指注释的属性所在的类
*/
Class<? extends Annotation> annotation() default Annotation.class;
/**
* {@link #annotation()}指定注解中关联的属性
*/
String attribute() default "";
/**
* {@link #attribute()}指定属性与当前注解的属性建的关联关系类型
*/
RelationType type() default RelationType.MIRROR_FOR;
}

View File

@ -0,0 +1,70 @@
package cn.hutool.core.annotation;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import java.util.HashMap;
import java.util.Map;
/**
* 处理注解中带有{@link Link}注解{@link Link#type()}{@link RelationType#MIRROR_FOR}的属性
*
* @author huangchengxing
* @see MirroredAnnotationAttribute
*/
public class MirrorLinkAttributePostProcessor implements SynthesizedAnnotationPostProcessor {
@Override
public int order() {
return Integer.MIN_VALUE + 1;
}
@Override
public void process(SynthesizedAnnotation annotation, SyntheticAnnotation syntheticAnnotation) {
Map<String, AnnotationAttribute> attributeMap = new HashMap<>(annotation.getAttributes());
attributeMap.forEach((originalAttributeName, originalAttribute) -> {
// 跳过已经解析的镜像属性
if (originalAttribute instanceof MirroredAnnotationAttribute) {
return;
}
// 获取注解
final Link link = SyntheticAnnotationUtil.getLink(originalAttribute, RelationType.MIRROR_FOR);
if (ObjectUtil.isNull(link)) {
return;
}
// 获取指定镜像属性所在的注解
final SynthesizedAnnotation mirrorAnnotation = SyntheticAnnotationUtil.getLinkedAnnotation(link, syntheticAnnotation, annotation.annotationType());
if (ObjectUtil.isNull(mirrorAnnotation)) {
return;
}
// 获取镜像属性并进行校验
final AnnotationAttribute mirrorAttribute = mirrorAnnotation.getAttributes().get(link.attribute());
checkMirrorRelation(link, originalAttribute, mirrorAttribute);
// 包装这一对镜像属性并替换原注解中的对应属性
final AnnotationAttribute mirroredOriginalAttribute = new MirroredAnnotationAttribute(originalAttribute, mirrorAttribute);
syntheticAnnotation.getSynthesizedAnnotation(originalAttribute.getAttribute().getDeclaringClass())
.setAttributes(originalAttributeName, mirroredOriginalAttribute);
final AnnotationAttribute mirroredTargetAttribute = new MirroredAnnotationAttribute(mirrorAttribute, originalAttribute);
mirrorAnnotation.setAttributes(link.attribute(), mirroredTargetAttribute);
});
}
private void checkMirrorRelation(Link annotation, AnnotationAttribute original, AnnotationAttribute mirror) {
// 镜像属性必须存在
SyntheticAnnotationUtil.checkLinkedAttributeNotNull(original, mirror, annotation);
// 镜像属性返回值必须一致
SyntheticAnnotationUtil.checkAttributeType(original, mirror);
// 镜像属性上必须存在对应的注解
final Link mirrorAttributeAnnotation = mirror.getAnnotation(Link.class);
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
);
}
}

View File

@ -0,0 +1,41 @@
package cn.hutool.core.annotation;
import cn.hutool.core.lang.Assert;
/**
* 表示存在对应镜像属性的注解属性当获取值时将根据{@link RelationType#MIRROR_FOR}的规则进行处理
*
* @author huangchengxing
* @see MirrorLinkAttributePostProcessor
* @see RelationType#MIRROR_FOR
*/
public class MirroredAnnotationAttribute extends AnnotationAttributeWrapper implements AnnotationAttribute {
private final AnnotationAttribute mirrorAttribute;
public MirroredAnnotationAttribute(AnnotationAttribute origin, AnnotationAttribute mirrorAttribute) {
super(origin);
this.mirrorAttribute = mirrorAttribute;
}
@Override
public Object getValue() {
boolean originIsDefault = origin.isValueEquivalentToDefaultValue();
boolean targetIsDefault = mirrorAttribute.isValueEquivalentToDefaultValue();
Object originValue = origin.getValue();
Object targetValue = mirrorAttribute.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
);
return originValue;
}
// 两者有一者不为默认值时优先返回非默认值
return originIsDefault ? targetValue : originValue;
}
}

View File

@ -0,0 +1,50 @@
package cn.hutool.core.annotation;
/**
* <p>注解属性的关系类型 <br>
* 若将被{@link Link}注解的属性称为原始属性而在{@link Link}注解中指向的注解属性称为关联属性
* 则该枚举用于描述原始属性关联属性{@link SyntheticAnnotation}处理过程中的作用关系<br>
* 根据在{@link Link#type()}中指定的关系类型的不同通过{@link SyntheticAnnotation}合成的注解的属性值也将有所变化
*
* <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 SyntheticAnnotation
* @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

@ -1,6 +1,9 @@
package cn.hutool.core.annotation;
import cn.hutool.core.collection.CollUtil;
import java.lang.annotation.Annotation;
import java.util.Map;
/**
* 用于在{@link SyntheticAnnotation}中表示一个处于合成状态的注解对象
@ -10,6 +13,13 @@ import java.lang.annotation.Annotation;
*/
public interface SynthesizedAnnotation extends Annotation {
/**
* 获取所属的合成注解
*
* @return 合成注解
*/
SyntheticAnnotation getOwner();
/**
* 获取该合成注解对应的根节点
*
@ -58,12 +68,38 @@ 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::setAttributes);
}
}
/**
* 设置属性值
*
* @param attributeName 属性名称
* @param attribute 注解属性
*/
void setAttributes(String attributeName, AnnotationAttribute attribute);
/**
* 获取属性值
*
* @param attributeName 属性名
* @return 属性值
*/
Object getAttribute(String attributeName);
Object getAttributeValue(String attributeName);
}

View File

@ -0,0 +1,45 @@
package cn.hutool.core.annotation;
import cn.hutool.core.comparator.CompareUtil;
import java.util.Comparator;
/**
* <p>被合成注解后置处理器用于在{@link SyntheticAnnotation}加载完所有待合成注解后
* 再对加载好的{@link SynthesizedAnnotation}进行后置处理<br>
* 当多个{@link SynthesizedAnnotationPostProcessor}需要一起执行时将按照{@link #order()}的返回值进行排序
* 该值更小的处理器将被优先执行
*
* @author huangchengxing
*/
public interface SynthesizedAnnotationPostProcessor extends Comparable<SynthesizedAnnotationPostProcessor> {
/**
* 在一组后置处理器中被调用的顺序越小越靠前
*
* @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 annotation 被合成的注解
* @param syntheticAnnotation 注解所属的合成注解
*/
void process(SynthesizedAnnotation annotation, SyntheticAnnotation syntheticAnnotation);
}

View File

@ -2,6 +2,7 @@ package cn.hutool.core.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.util.Collection;
/**
* 表示基于特定规则聚合的一组注解对象
@ -9,21 +10,33 @@ import java.lang.reflect.AnnotatedElement;
* <p>合成注解一般被用于处理类层级结果中具有直接或间接关联的注解对象
* 当实例被创建时会获取到这些注解对象并使用{@link SynthesizedAnnotationSelector}对类型相同的注解进行过滤
* 并最终得到类型不重复的有效注解对象这些有效注解将被包装为{@link SynthesizedAnnotation}
* 然后最终用于合成一个{@link SynthesizedAnnotation}
* 然后最终用于合成一个{@link SyntheticAnnotation}<br>
* {@link SynthesizedAnnotationSelector}是合成注解生命周期中的第一个钩子
* 自定义选择器以拦截原始注解被扫描的过程
*
* <p>当合成注解完成对待合成注解的扫描并完成了必要属性的加载后
* 将会按顺序依次调用{@link SynthesizedAnnotationPostProcessor}
* 注解后置处理器允许用于对完成注解的待合成注解进行二次调整
* 该钩子一般用于根据{@link Link}注解对属性进行调整<br>
* {@link SynthesizedAnnotationPostProcessor}是合成注解生命周期中的第二个钩子
* 自定义后置处理器以拦截原始在转为待合成注解后的初始化过程
*
* <p>合成注解允许通过{@link #syntheticAnnotation(Class)}合成一个指定的注解对象
* 该方法返回的注解对象可能是原始的注解对象也有可能通过动态代理的方式生成
* 该对象实例的属性不一定来自对象本身而是来自于经过{@link SynthesizedAnnotationAttributeProcessor}
* 处理后的用于合成当前实例的全部关联注解的相关属性<br>
* {@link SynthesizedAnnotationAttributeProcessor}是合成注解生命周期中的第三个钩子
* 自定义属性处理器以拦截合成注解的取值过程
*
* <p>合成注解可以作为一个特殊的{@link Annotation}或者{@link AnnotatedElement}
* 当调用{@link Annotation}的方法时应当返回当前实例本身的有效信息
* 而当调用{@link AnnotatedElement}的方法时应当返回用于合成该对象的相关注解的信息
*
* <p>合成注解允许通过{@link #syntheticAnnotation(Class)}合成一个指定的注解对象
* 该方法返回的注解对象可能是原始的注解对象也有可能通过动态代理的方式生成
* 该对象实例的属性不一定来自对象本身而是来自于经过{@link SynthesizedAnnotationAttributeProcessor}
* 处理后的用于合成当前实例的全部关联注解的相关属性
*
* @author huangchengxing
* @see SynthesizedAnnotation
* @see SynthesizedAnnotationSelector
* @see SynthesizedAnnotationAttributeProcessor
* @see SynthesizedAnnotationPostProcessor
* @see SyntheticMetaAnnotation
*/
public interface SyntheticAnnotation extends Annotation, AnnotatedElement {
@ -42,6 +55,13 @@ public interface SyntheticAnnotation extends Annotation, AnnotatedElement {
*/
SynthesizedAnnotationAttributeProcessor getAttributeProcessor();
/**
* 获取合成注解属性后置处理器
*
* @return 合成注解属性后置处理器
*/
Collection<SynthesizedAnnotationPostProcessor> getSynthesizedAnnotationAttributePostProcessors();
/**
* 获取已合成的注解
*

View File

@ -1,5 +1,6 @@
package cn.hutool.core.annotation;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjectUtil;
@ -24,12 +25,11 @@ import java.util.stream.Stream;
*/
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;
SyntheticAnnotationProxy(SynthesizedAnnotation annotation) {
Assert.notNull(annotation, "annotation must not null");
this.annotation = annotation;
this.methods = new HashMap<>(9);
loadMethods();
@ -50,7 +50,10 @@ class SyntheticAnnotationProxy implements InvocationHandler {
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;
}
final SyntheticAnnotationProxy proxyHandler = new SyntheticAnnotationProxy(annotation);
if (ObjectUtil.isNull(annotation)) {
return null;
}
@ -84,7 +87,12 @@ class SyntheticAnnotationProxy implements InvocationHandler {
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("getAttributes", (method, args) -> annotation.getAttributes());
methods.put("setAttributes", (method, args) -> {
throw new UnsupportedOperationException("proxied annotation can not reset attributes");
});
methods.put("getAttributeValue", (method, args) -> annotation.getAttributeValue((String)args[0]));
methods.put("getOwner", (method, args) -> annotation.getOwner());
methods.put("annotationType", (method, args) -> annotation.annotationType());
for (final Method declaredMethod : annotation.getAnnotation().annotationType().getDeclaredMethods()) {
methods.put(declaredMethod.getName(), (method, args) -> proxyAttributeValue(method));
@ -94,17 +102,17 @@ class SyntheticAnnotationProxy implements InvocationHandler {
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())))
.map(method -> StrUtil.format("{}={}", method.getName(), annotation.getOwner().getAttribute(method.getName(), method.getReturnType())))
.collect(Collectors.joining(", "));
return StrUtil.format("@{}({})", annotation.annotationType().getName(), attributes);
}
private int proxyHashCode() {
return Objects.hash(syntheticAnnotation, annotation);
return Objects.hash(annotation.getOwner(), annotation);
}
private Object proxyGetSyntheticAnnotation() {
return syntheticAnnotation;
return annotation.getOwner();
}
private Object proxyGetSynthesizedAnnotation() {
@ -112,7 +120,7 @@ class SyntheticAnnotationProxy implements InvocationHandler {
}
private Object proxyAttributeValue(Method attributeMethod) {
return syntheticAnnotation.getAttribute(attributeMethod.getName(), attributeMethod.getReturnType());
return annotation.getOwner().getAttribute(attributeMethod.getName(), attributeMethod.getReturnType());
}
/**

View File

@ -0,0 +1,71 @@
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;
/**
* {@link SyntheticAnnotation}相关工具用于内部使用
*
* @author huangchengxing
*/
class SyntheticAnnotationUtil {
/**
* 从注解属性上获取指定类型的{@link Link}注解
*
* @param attribute 注解属性
* @param relationTypes 类型
* @return 注解
*/
static Link getLink(AnnotationAttribute attribute, RelationType... relationTypes) {
return Opt.ofNullable(attribute)
.map(t -> t.getAnnotation(Link.class))
.filter(a -> ArrayUtil.contains(relationTypes, a.type()))
.get();
}
/**
* 从合成注解中获取{@link Link#type()}指定的注解对象
*
* @param annotation {@link Link}注解
* @param syntheticAnnotation 合成注解
*/
static SynthesizedAnnotation getLinkedAnnotation(
Link annotation, SyntheticAnnotation syntheticAnnotation, Class<? extends Annotation> defaultType) {
final Class<? extends Annotation> targetAnnotationType = ObjectUtil.equals(annotation.annotation(), Annotation.class) ?
defaultType : annotation.annotation();
return syntheticAnnotation.getSynthesizedAnnotation(targetAnnotationType);
}
/**
* 校验两个注解属性的返回值类型是否一致
*
* @param original 原属性
* @param alias 别名属性
*/
static 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 linkedAttribute {@link Link}指向的注解属性
* @param annotation {@link Link}注解
*/
static void checkLinkedAttributeNotNull(AnnotationAttribute original, AnnotationAttribute linkedAttribute, Link annotation) {
Assert.notNull(linkedAttribute, "cannot find linked attribute [{}] of original [{}] in [{}]",
original.getAttribute(), annotation.attribute(), annotation.annotation()
);
}
}

View File

@ -5,14 +5,13 @@ 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;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 表示一个根注解与根注解上的多层元注解合成的注解
@ -65,6 +64,11 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation {
*/
private final SynthesizedAnnotationAttributeProcessor attributeProcessor;
/**
* 合成注解属性处理器
*/
private final List<SynthesizedAnnotationPostProcessor> postProcessors;
/**
* 基于指定根注解为其层级结构中的全部注解构造一个合成注解
* 当层级结构中出现了相同的注解对象时将优先选择以距离根注解最近且优先被扫描的注解对象,
@ -78,6 +82,11 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation {
new CacheableSynthesizedAnnotationAttributeProcessor(
Comparator.comparing(SynthesizedAnnotation::getVerticalDistance)
.thenComparing(SynthesizedAnnotation::getHorizontalDistance)
),
Arrays.asList(
new AliasAttributePostProcessor(),
new MirrorLinkAttributePostProcessor(),
new AliasForLinkAttributePostProcessor()
)
);
}
@ -92,16 +101,26 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation {
public SyntheticMetaAnnotation(
Annotation annotation,
SynthesizedAnnotationSelector annotationSelector,
SynthesizedAnnotationAttributeProcessor attributeProcessor) {
SynthesizedAnnotationAttributeProcessor attributeProcessor,
Collection<? extends SynthesizedAnnotationPostProcessor> annotationPostProcessors) {
Assert.notNull(annotation, "annotation must not null");
Assert.notNull(annotationSelector, "annotationSelector must not null");
Assert.notNull(attributeProcessor, "attributeProcessor must not null");
Assert.notNull(annotationPostProcessors, "attributePostProcessors must not null");
// 初始化属性
this.source = annotation;
this.annotationSelector = annotationSelector;
this.attributeProcessor = attributeProcessor;
this.postProcessors = new ArrayList<>(annotationPostProcessors);
this.postProcessors.sort(Comparator.comparing(SynthesizedAnnotationPostProcessor::order));
this.metaAnnotationMap = new LinkedHashMap<>();
// 初始化元注解信息并进行后置处理
loadMetaAnnotations();
annotationPostProcessors.forEach(processor ->
metaAnnotationMap.values().forEach(synthesized -> processor.process(synthesized, this))
);
}
/**
@ -142,6 +161,16 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation {
return this.attributeProcessor;
}
/**
* 获取合成注解属性后置处理器
*
* @return 合成注解属性后置处理器
*/
@Override
public Collection<SynthesizedAnnotationPostProcessor> getSynthesizedAnnotationAttributePostProcessors() {
return this.postProcessors;
}
/**
* 获取已合成的注解
*
@ -241,11 +270,11 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation {
private void loadMetaAnnotations() {
Assert.isFalse(SyntheticAnnotationProxy.isProxyAnnotation(source.getClass()), "source [{}] has been synthesized");
// 扫描元注解
metaAnnotationMap.put(source.annotationType(), new MetaAnnotation(source, source, 0, 0));
metaAnnotationMap.put(source.annotationType(), new MetaAnnotation(this, source, source, 0, 0));
new MetaAnnotationScanner().scan(
(index, annotation) -> {
SynthesizedAnnotation oldAnnotation = metaAnnotationMap.get(annotation.annotationType());
SynthesizedAnnotation newAnnotation = new MetaAnnotation(source, annotation, index, metaAnnotationMap.size());
SynthesizedAnnotation newAnnotation = new MetaAnnotation(this, source, annotation, index, metaAnnotationMap.size());
if (ObjectUtil.isNull(oldAnnotation)) {
metaAnnotationMap.put(annotation.annotationType(), newAnnotation);
} else {
@ -263,18 +292,32 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation {
*/
public static class MetaAnnotation implements Annotation, SynthesizedAnnotation {
private final SyntheticAnnotation owner;
private final Annotation root;
private final Annotation annotation;
private final Map<String, Method> attributeMethodCaches;
private final Map<String, AnnotationAttribute> attributeMethodCaches;
private final int verticalDistance;
private final int horizontalDistance;
public MetaAnnotation(Annotation root, Annotation annotation, int verticalDistance, int horizontalDistance) {
public MetaAnnotation(SyntheticAnnotation owner, Annotation root, Annotation annotation, int verticalDistance, int horizontalDistance) {
this.owner = owner;
this.root = root;
this.annotation = annotation;
this.verticalDistance = verticalDistance;
this.horizontalDistance = horizontalDistance;
this.attributeMethodCaches = AnnotationUtil.getAttributeMethods(annotation.annotationType());
this.attributeMethodCaches = Stream.of(annotation.annotationType().getDeclaredMethods())
.filter(AnnotationUtil::isAttributeMethod)
.collect(Collectors.toMap(Method::getName, method -> new CacheableAnnotationAttribute(annotation, method)));
}
/**
* 获取所属的合成注解
*
* @return 合成注解
*/
@Override
public SyntheticAnnotation getOwner() {
return owner;
}
/**
@ -347,21 +390,42 @@ public class SyntheticMetaAnnotation implements SyntheticAnnotation {
@Override
public boolean hasAttribute(String attributeName, Class<?> returnType) {
return Opt.ofNullable(attributeMethodCaches.get(attributeName))
.filter(method -> ClassUtil.isAssignable(returnType, method.getReturnType()))
.filter(method -> ClassUtil.isAssignable(returnType, method.getAttributeType()))
.isPresent();
}
/**
* 获取元注解的属性值
* 获取该注解的全部属性
*
* @param attributeName 属性名
* @return 元注解的属性值
* @return 注解属性
*/
@Override
public Object getAttribute(String attributeName) {
public Map<String, AnnotationAttribute> getAttributes() {
return this.attributeMethodCaches;
}
/**
* 设置属性值
*
* @param attributeName 属性名称
* @param attribute 注解属性
*/
@Override
public void setAttributes(String attributeName, AnnotationAttribute attribute) {
attributeMethodCaches.put(attributeName, attribute);
}
/**
* 获取属性值
*
* @param attributeName 属性名
* @return 属性值
*/
@Override
public Object getAttributeValue(String attributeName) {
return Opt.ofNullable(attributeMethodCaches.get(attributeName))
.map(method -> ReflectUtil.invoke(annotation, method))
.orElse(null);
.map(AnnotationAttribute::getValue)
.get();
}
}

View File

@ -53,6 +53,61 @@ public class SyntheticMetaAnnotationTest {
Assert.assertThrows(IllegalArgumentException.class, () -> new SyntheticMetaAnnotation(grandParentAnnotation));
}
@Test
public void mirrorAttributeTest() {
AnnotationForMirrorTest annotation = ClassForMirrorTest.class.getAnnotation(AnnotationForMirrorTest.class);
SyntheticAnnotation synthetic = new SyntheticMetaAnnotation(annotation);
AnnotationForMirrorTest syntheticAnnotation = synthetic.syntheticAnnotation(AnnotationForMirrorTest.class);
Assert.assertEquals("Foo", syntheticAnnotation.name());
Assert.assertEquals("Foo", syntheticAnnotation.value());
annotation = ClassForMirrorTest2.class.getAnnotation(AnnotationForMirrorTest.class);
synthetic = new SyntheticMetaAnnotation(annotation);
syntheticAnnotation = synthetic.syntheticAnnotation(AnnotationForMirrorTest.class);
Assert.assertEquals("Foo", syntheticAnnotation.name());
Assert.assertEquals("Foo", syntheticAnnotation.value());
annotation = ClassForMirrorTest3.class.getAnnotation(AnnotationForMirrorTest.class);
synthetic = new SyntheticMetaAnnotation(annotation);
syntheticAnnotation = synthetic.syntheticAnnotation(AnnotationForMirrorTest.class);
AnnotationForMirrorTest finalSyntheticAnnotation = syntheticAnnotation;
Assert.assertThrows(IllegalArgumentException.class, () -> finalSyntheticAnnotation.name());
}
@Test
public void aliasForTest() {
AnnotationForAliasForTest annotation = ClassForAliasForTest.class.getAnnotation(AnnotationForAliasForTest.class);
SyntheticAnnotation synthetic = new SyntheticMetaAnnotation(annotation);
MetaAnnotationForAliasForTest metaAnnotation = synthetic.syntheticAnnotation(MetaAnnotationForAliasForTest.class);
Assert.assertEquals("Meta", metaAnnotation.name());
AnnotationForAliasForTest childAnnotation = synthetic.syntheticAnnotation(AnnotationForAliasForTest.class);
Assert.assertEquals("", childAnnotation.value());
annotation = ClassForAliasForTest2.class.getAnnotation(AnnotationForAliasForTest.class);
synthetic = new SyntheticMetaAnnotation(annotation);
metaAnnotation = synthetic.syntheticAnnotation(MetaAnnotationForAliasForTest.class);
Assert.assertEquals("Foo", metaAnnotation.name());
childAnnotation = synthetic.syntheticAnnotation(AnnotationForAliasForTest.class);
Assert.assertEquals("Foo", childAnnotation.value());
}
@Test
public void forceAliasForTest() {
AnnotationForceForAliasForTest annotation = ClassForForceAliasForTest.class.getAnnotation(AnnotationForceForAliasForTest.class);
SyntheticAnnotation synthetic = new SyntheticMetaAnnotation(annotation);
MetaAnnotationForForceAliasForTest metaAnnotation = synthetic.syntheticAnnotation(MetaAnnotationForForceAliasForTest.class);
Assert.assertEquals("", metaAnnotation.name());
AnnotationForceForAliasForTest childAnnotation = synthetic.syntheticAnnotation(AnnotationForceForAliasForTest.class);
Assert.assertEquals("", childAnnotation.value());
annotation = ClassForForceAliasForTest2.class.getAnnotation(AnnotationForceForAliasForTest.class);
synthetic = new SyntheticMetaAnnotation(annotation);
metaAnnotation = synthetic.syntheticAnnotation(MetaAnnotationForForceAliasForTest.class);
Assert.assertEquals("Foo", metaAnnotation.name());
childAnnotation = synthetic.syntheticAnnotation(AnnotationForceForAliasForTest.class);
Assert.assertEquals("Foo", childAnnotation.value());
}
// 注解结构如下
// AnnotatedClass -> @ChildAnnotation -> @ParentAnnotation -> @GrandParentAnnotation
// -> @GrandParentAnnotation
@ -85,4 +140,61 @@ public class SyntheticMetaAnnotationTest {
Class<?> grandParentType() default Void.class;
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@interface AnnotationForMirrorTest {
@Link(attribute = "name")
String value() default "";
@Link(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 {
@Link(
annotation = MetaAnnotationForAliasForTest.class,
attribute = "name",
type = RelationType.ALIAS_FOR
)
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
)
String value() default "";
}
@AnnotationForceForAliasForTest
static class ClassForForceAliasForTest {}
@AnnotationForceForAliasForTest("Foo")
static class ClassForForceAliasForTest2 {}
}