• spring core:@AliasFor的派生性


    spring对Annotation的派生性应用可谓炉火纯青,在spring core:@Component的派生性讲过支持层次上派生性,而属性上派生的需求则借助了@AliasFor,它是从spring4.2中开始支持的。

    @AliasFor注解用于声明注解元素的别名,应用于方法上(别忘了注解本质是接口)。Spring框架在内部使用大量的使用这个注解,例如,@Bean,@ComponentScan,@Scope等。

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    @Documented
    public @interface AliasFor {
    /**
    * * 引用的注解别名..
    */
    @AliasFor("attribute")
    String value() default "";
    /**
    * 引用的注解别名.
    * @see #value
    */
    @AliasFor("value")
    String attribute() default "";
    /**层次结构的父注解
    */
    Class<? extends Annotation> annotation() default Annotation.class;
    }
    示例1

    可通过AnnotatedElementUtils#getMergedAnnotationAttributes来读取。下面演示没有派生性的情况:

    @Test
    public void metaTest() throws IOException {
    AnnotationAttributes aa = AnnotatedElementUtils.getMergedAnnotationAttributes(Home.class, AccessRole.class);
    System.out.println(aa);//{value=super-user, module=gui, accessType=super-user}
    }
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface AccessRole {
    @AliasFor("accessType")
    String value() default "visitor";

    @AliasFor("value")
    String accessType() default "visitor";

    String module() default "gui";
    }
    @AccessRole("super-user")
    //@AccessRole(value = "super-user",accessType = "super")//error
    public class Home {
    }
    注:alias references的默认值必须一致,使用时指定值也必须一致,否则会抛出AnnotationConfigurationException

    下面演示有单层次及多层次派生性的情况:

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @AccessRole("admin")
    public @interface AdminAccess {
    @AliasFor(annotation = AccessRole.class, attribute = "module")
    String value() default "service";
    }
    @AdminAccess
    public class Home2 {
    }
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @AdminAccess("supper")
    public @interface SupperAccess {
    String value() default "service3";
    @AliasFor(annotation = AccessRole.class, attribute = "module")
    String module() default "service3";
    }
    @SupperAccess
    public class Home3 {
    }
    @Test
    public void metaTest() throws IOException {
    AnnotationAttributes aa = AnnotatedElementUtils.getMergedAnnotationAttributes(Home2.class, AdminAccess.class);
    System.out.println(aa);//{value=service}
    aa = AnnotatedElementUtils.getMergedAnnotationAttributes(Home2.class, AccessRole.class);
    System.out.println(aa);//{value=admin, accessType=admin, module=service}
    aa = AnnotatedElementUtils.getMergedAnnotationAttributes(Home3.class, AccessRole.class);
    System.out.println(aa);//{value=admin, module=service3, accessType=admin}
    }
    原理

    AnnotatedElementUtils处理也不是特别复杂,很好理解

    public abstract class AnnotatedElementUtils {
    @Nullable
    public static AnnotationAttributes getMergedAnnotationAttributes(
    AnnotatedElement element, Class<? extends Annotation> annotationType) {

    AnnotationAttributes attributes = searchWithGetSemantics(element, annotationType, null,
    new MergedAnnotationAttributesProcessor());
    AnnotationUtils.postProcessAnnotationAttributes(element, attributes, false, false);
    return attributes;
    }
    //第一步:searchWithGetSemantics真正调用的方法,递归获取所有的注解
    @Nullable
    private static <T> T searchWithGetSemantics(AnnotatedElement element,
    Set<Class<? extends Annotation>> annotationTypes, @Nullable String annotationName,
    @Nullable Class<? extends Annotation> containerType, Processor<T> processor,
    Set<AnnotatedElement> visited, int metaDepth) {

    if (visited.add(element)) {
    try {
    // Start searching within locally declared annotations
    List<Annotation> declaredAnnotations = Arrays.asList(AnnotationUtils.getDeclaredAnnotations(element));
    T result = searchWithGetSemanticsInAnnotations(element, declaredAnnotations,
    annotationTypes, annotationName, containerType, processor, visited, metaDepth);
    if (result != null) {
    return result;
    }

    if (element instanceof Class) { // otherwise getAnnotations doesn't return anything new
    Class<?> superclass = ((Class<?>) element).getSuperclass();
    if (superclass != null && superclass != Object.class) {
    List<Annotation> inheritedAnnotations = new LinkedList<>();
    for (Annotation annotation : element.getAnnotations()) {
    if (!declaredAnnotations.contains(annotation)) {
    inheritedAnnotations.add(annotation);
    }
    }
    // Continue searching within inherited annotations
    result = searchWithGetSemanticsInAnnotations(element, inheritedAnnotations,
    annotationTypes, annotationName, containerType, processor, visited, metaDepth);
    if (result != null) {
    return result;
    }
    }
    }
    }
    catch (Throwable ex) {
    AnnotationUtils.handleIntrospectionFailure(element, ex);
    }
    }

    return null;
    }
    }
    public abstract class AnnotationUtils {
    //第二步:获取@AliasFor的标记,处理其值
    static void postProcessAnnotationAttributes(@Nullable Object annotatedElement,
    @Nullable AnnotationAttributes attributes, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {

    if (attributes == null) {
    return;
    }

    Class<? extends Annotation> annotationType = attributes.annotationType();

    // Track which attribute values have already been replaced so that we can short
    // circuit the search algorithms.
    Set<String> valuesAlreadyReplaced = new HashSet<>();

    if (!attributes.validated) {
    // Validate @AliasFor configuration
    Map<String, List<String>> aliasMap = getAttributeAliasMap(annotationType);
    aliasMap.forEach((attributeName, aliasedAttributeNames) -> {
    if (valuesAlreadyReplaced.contains(attributeName)) {
    return;
    }
    Object value = attributes.get(attributeName);
    boolean valuePresent = (value != null && !(value instanceof DefaultValueHolder));
    for (String aliasedAttributeName : aliasedAttributeNames) {
    if (valuesAlreadyReplaced.contains(aliasedAttributeName)) {
    continue;
    }
    Object aliasedValue = attributes.get(aliasedAttributeName);
    boolean aliasPresent = (aliasedValue != null && !(aliasedValue instanceof DefaultValueHolder));
    // Something to validate or replace with an alias?
    if (valuePresent || aliasPresent) {
    if (valuePresent && aliasPresent) {
    // Since annotation attributes can be arrays, we must use ObjectUtils.nullSafeEquals().
    if (!ObjectUtils.nullSafeEquals(value, aliasedValue)) {
    String elementAsString =
    (annotatedElement != null ? annotatedElement.toString() : "unknown element");
    throw new AnnotationConfigurationException(String.format(
    "In AnnotationAttributes for annotation [%s] declared on %s, " +
    "attribute '%s' and its alias '%s' are declared with values of [%s] and [%s], " +
    "but only one is permitted.", attributes.displayName, elementAsString,
    attributeName, aliasedAttributeName, ObjectUtils.nullSafeToString(value),
    ObjectUtils.nullSafeToString(aliasedValue)));
    }
    }
    else if (aliasPresent) {
    // Replace value with aliasedValue
    attributes.put(attributeName,
    adaptValue(annotatedElement, aliasedValue, classValuesAsString, nestedAnnotationsAsMap));
    valuesAlreadyReplaced.add(attributeName);
    }
    else {
    // Replace aliasedValue with value
    attributes.put(aliasedAttributeName,
    adaptValue(annotatedElement, value, classValuesAsString, nestedAnnotationsAsMap));
    valuesAlreadyReplaced.add(aliasedAttributeName);
    }
    }
    }
    });
    attributes.validated = true;
    }

    // Replace any remaining placeholders with actual default values
    for (Map.Entry<String, Object> attributeEntry : attributes.entrySet()) {
    String attributeName = attributeEntry.getKey();
    if (valuesAlreadyReplaced.contains(attributeName)) {
    continue;
    }
    Object value = attributeEntry.getValue();
    if (value instanceof DefaultValueHolder) {
    value = ((DefaultValueHolder) value).defaultValue;
    attributes.put(attributeName,
    adaptValue(annotatedElement, value, classValuesAsString, nestedAnnotationsAsMap));
    }
    }
    }
    }
    待续
    ————————————————
    版权声明:本文为CSDN博主「布道」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/alex_xfboy/article/details/103787806

  • 相关阅读:
    RabbitMQ一:消息队列的认识
    RabbitMQ二:AMQP协议
    SVN中如何去除版本控制器
    Asp.net:MVC认识
    时间连接查询展示
    C#string类型总结
    JavaScript01天学习笔记分享
    UML中的类图及类图之间的关系
    23 种设计模式的分类和功能
    WCF入门
  • 原文地址:https://www.cnblogs.com/softidea/p/12327794.html
Copyright © 2020-2023  润新知