AliasFor的实现原理

AliasFor的实现原理,第1张

AliasFor的实现原理 什么是AliasFor

  AliasFor是Spring 4.2引入的注解,它提供了给注解中的属性定义别名的功能。所谓注解中的属性,指的是注解中定义的方法,因为它无参并且有返回值。

  Java标准定义了几个术语来表示注解A和被标注的元素E之间的关系:

direct present:注解A是运行时可见的,并且A直接标注在E上,比如E是一个类,A标注在这个类上indirect present:满足direct present语义,但是限定注解A必须是repeatable的present:满足direct present语义,或注解A是inheritable的且E是一个类,A标注在E的基类上associated:满足present或indirect present的语义

  随着AliasFor注解的引入,Spring在Java标准之上延伸出了一个新的概念——meta present。介绍meta present之前,简单说一下什么是meta annotation。顾名思义,元注解(meta annotation)就是注解的注解,它作用在别的注解之上,通过它可以合成新的注解,比如Component就是一个元注解,通过它衍生出了Controller。

meta present:如果注解A作为元注解作用在注解B上,并且注解B和元素E之间满足present语义,就说注解A meta present于元素E;注解A meta present于注解B是指注解A和注解B之间满足direct present或meta present语义。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AliasFor {
    
    @AliasFor("attribute")
    String value() default "";

    
    @AliasFor("value")
    String attribute() default "";

    
    Class annotation() default Annotation.class;
}

  基于AliasFor的使用方式,分为显式别名(explicit aliases)和隐式别名(implicit aliases)两种情况:

    显式别名也有两种情况:
      在同一个注解中的不同属性上使用时(对应AliasFor#annotation()使用默认值),表示它们是可互换的(interchangeable),比如AliasFor注解本身的attribute和value属性显式设置了AliasFor#annotation()属性(相当于指定元注解),那么当前注解中的属性就作为元注解中属性的别名,比如Controller#value()是Component#value()的别名
    隐式别名:如果显式设置了AliasFor#annotation()属性,并且当前注解中有多个属性指向元注解中的某个属性(可以是直接指向或者通过传递指向),那么这几个属性互为隐式别名
@Component
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyComponent {
  @AliasFor(annotation = Component.class)
  String value() default "";
  
  @AliasFor(attribute = "value", annotation = Component.class)
  String beanName() default "";
}
为什么要引入AliasFor

  Java语言中,注解是不能被继承的。这意味着,在组合注解时,我们无法覆盖元注解中的属性,灵活性很差。

@Configuration
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@importResource("classpath:test.properties")
public @interface importResourceConfiguration {
}

引入AliasFor以后,让原本不存在层次结构的注解体系也拥有了层次的概念。换个角度思考一下,AliasFor的别名机制其实是一种属性重写,因为作为别名的属性是可以无缝替换源属性的,和继承中重写的概念很像不是吗?

@Configuration
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@importResource
public @interface importResourceConfiguration {
  
  @AliasFor(attribute = "value", annotation = importResource.class)
  String[] value() default {};
}

AliasFor还可以用来简化编码,当我们让注解的value属性和长的、更具描述性的属性互为别名以后,就能够使用注解的简写形式了。AliasFor注解本身就是这么做的,不指定AliasFor#annotation()的情况下@AliasFor("someAttribute")和@AliasFor(attribute = "someAttribute")是等价的。

AliasFor的语义是如何实现的

  天下没有免费的午餐,只定义AliasFor注解并不会自动获得其语义,接下来让我们一起探究一下Spring 5.1.8中是如何实现AliasFor语义的。AliasFor注解的JavaDoc上有提到,其语义生效的前提是相关注解必须通过AnnotationUtils中的工具方法来加载,转到AnnotationUtils中定义的第一个方法:

    
		@Nullable
    @SuppressWarnings("unchecked")
    public static  A getAnnotation(Annotation annotation, Class annotationType) {
        // annotation就是要寻找的注解
        if (annotationType.isInstance(annotation)) {
            // 对annotation进行加强,使之可以处理标注了AliasFor的情况
            return synthesizeAnnotation((A) annotation);
        }
        // annotation不是要寻找的注解
        Class annotatedElement = annotation.annotationType();
        try {
            // 查找它的直接元注解
            A metaAnnotation = annotatedElement.getAnnotation(annotationType);
            // 对注解进行增强
            return metaAnnotation != null ? synthesizeAnnotation(metaAnnotation, annotatedElement) : null;
        } catch (Throwable e) {
            handleIntrospectionFailure(annotatedElement, e);
            return null;
        }
    }

很明显,AliasFor的黑魔法隐藏在synthesizeAnnotation(...)方法中。

    
    @SuppressWarnings("unchecked")
    static  A synthesizeAnnotation(A annotation, @Nullable Object annotatedElement) {
        // 如果已经被代理过了
        // 或者是java标准注解
        if (annotation instanceof SynthesizedAnnotation ||
                hasPlainJavaAnnotationsOnly(annotatedElement)) {
            return annotation;
        }
        // 获取表示这个注解的接口
        Class annotationType = annotation.annotationType();
        // 不能合成直接返回原注解
        if (!isSynthesizable(annotationType)) {
            return annotation;
        }
        // 创建动态代理来保证AliasFor语义
        DefaultAnnotationAttributeExtractor extractor = new DefaultAnnotationAttributeExtractor(annotation, annotatedElement);
        InvocationHandler handler = new SynthesizedAnnotationInvocationHandler(extractor);
        Class[] interfaces = new Class[]{annotationType, SynthesizedAnnotation.class};
        return (A) Proxy.newProxyInstance(annotation.getClass().getClassLoader(), interfaces, handler);
    }

可以看到,Spring是使用JDK动态代理来实现AliasFor的语义的。SynthesizedAnnotation是一个标记接口,它的作用是保证作为实参的annotation不会被多次代理,相同的设计技巧在org.springframework.core.SeriablizableTypeWrapper中也有体现。

    
    @SuppressWarnings("unchecked")
    private static boolean isSynthesizable(Class annotationType) {
        // 基本注解不能合成
        if (hasPlainJavaAnnotationsOnly(annotationType)) {
            return false;
        }
        // 读缓存
        Boolean synthesizable = synthesizableCache.get(annotationType);
        if (synthesizable != null) {
            return synthesizable;
        }
        synthesizable = Boolean.FALSE;
        // 遍历注解中定义的方法
        for (Method attribute : getAttributeMethods(annotationType)) {
            // 有使用AliasFor注解定义别名
            if (!getAttributeAliasNames(attribute).isEmpty()) {
                synthesizable = Boolean.TRUE;
                break;
            }
            // 没有直接使用AliasFor注解
            // 此时要看看是否有间接使用,比如注解的某个属性返回某个注解,这个返回的注解使用了AliasFor注解
            Class returnType = attribute.getReturnType();
            if (Annotation[].class.isAssignableFrom(returnType)) {
                Class nestedAnnotationType = (Class) returnType.getComponentType();
                if (isSynthesizable(nestedAnnotationType)) {
                    synthesizable = Boolean.TRUE;
                    break;
                }
            } else if (Annotation.class.isAssignableFrom(returnType)) {
                Class nestedAnnotationType = (Class) returnType;
                if (isSynthesizable(nestedAnnotationType)) {
                    synthesizable = Boolean.TRUE;
                    break;
                }
            }
        }

        synthesizableCache.put(annotationType, synthesizable);
        return synthesizable;
    }

我们知道,Java中所有的注解都间接继承自java.lang.annotation.Annotation,因此注解中除去自定义的属性外,还有继承自Annotation接口的方法,getAttributeMethods(...)方法的作用就是排除继承自Annotation接口的方法。现在我们可以很容易地发现isSynthesizable(...)方法的核心是getAttributeAliasNames(...)。

  static List getAttributeAliasNames(Method attribute) {
    AliasDescriptor descriptor = AliasDescriptor.from(attribute);
    return descriptor != null ?
      descriptor.getAttributeAliasNames() : Collections.emptyList();
  }

很不幸,getAttributeAliasNames(...)又代理给了AliasDescriptor。AliasDescriptor是一个静态内部类,它封装了注解属性上AliasFor的相关信息,继续往下追踪。

  
  @Nullable
  public static AliasDescriptor from(Method attribute) {
    // 读缓存
    AliasDescriptor descriptor = aliasDescriptorCache.get(attribute);
    if (descriptor != null) {
      return descriptor;
    }
    // 查看一下是否有AliasFor注解
    // 没有的话就没必要创建了
    AliasFor aliasFor = attribute.getAnnotation(AliasFor.class);
    if (aliasFor == null) {
      return null;
    }
    // 有的话进行创建并校验
    descriptor = new AliasDescriptor(attribute, aliasFor);
    descriptor.validate(); // 校验一下
    aliasDescriptorCache.put(attribute, descriptor);
    return descriptor;
  }

工厂方法AliasDescriptor#from(...)首先检测注解属性上是否标注有AliasFor注解,没有的话自然没有继续分析的必要了。如果注解属性标注有AliasFor,就会创建一个AliasDescriptor并进行校验。

  
	@SuppressWarnings("unchecked")
  private AliasDescriptor(Method sourceAttribute, AliasFor aliasFor) {
    // sourceAttribute是注解中的属性方法,因此declaringClass就是注解的类型
    Class declaringClass = sourceAttribute.getDeclaringClass();
    // 很容易通过sourceAttribute获取以下几个信息
    this.sourceAttribute = sourceAttribute;
    this.sourceAttributeName = sourceAttribute.getName();
    this.sourceAnnotationType = (Class) declaringClass;
    // 如果没有指定AliasFor的annotation()属性
    // 那么默认是同一注解内的相互指向,否则使用指定的
    this.aliasedAnnotationType = (Annotation.class == aliasFor.annotation()) ?
      this.sourceAnnotationType :
    aliasFor.annotation();
    // 获取别名
    this.aliasedAttributeName = getAliasedAttributeName(aliasFor, sourceAttribute);
    // 不能自己指向自己
    // 在attributeName相同的情况下,{@link AliasFor#annotation()}必须指向某个元注解
    if (this.aliasedAnnotationType == this.sourceAnnotationType &&
        this.aliasedAttributeName.equals(this.sourceAttributeName)) {
      String msg = String.format("@AliasFor declaration on attribute '%s' in annotation [%s] points to " +
                                 "itself. Specify 'annotation' to point to a same-named attribute on a meta-annotation.",
                                 sourceAttribute.getName(), declaringClass.getName());
      throw new AnnotationConfigurationException(msg);
    }
    try {
      // 获取别名属性方法,已经知道了名字和类型,可以通过反射直接获取
      // 根据java标准,注解内的方法是没有参数的
      this.aliasedAttribute = this.aliasedAnnotationType.getDeclaredMethod(this.aliasedAttributeName);
    } catch (NoSuchMethodException ex) {
      String msg = String.format(
        "Attribute '%s' in annotation [%s] is declared as an @AliasFor nonexistent attribute '%s' in annotation [%s].",
        this.sourceAttributeName, this.sourceAnnotationType.getName(), this.aliasedAttributeName,
        this.aliasedAnnotationType.getName());
      throw new AnnotationConfigurationException(msg, ex);
    }
    // source和alias类型相同的话表示是同一注解内部的两个属性互相指向
    // 否则的话说明指定了元注解
    this.isAliasPair = this.sourceAnnotationType == this.aliasedAnnotationType;
  }


  
  private void validate() {
    // 如果不是注解内部的互相指向,说明是指向了其它的注解属性
    // 这种情况下要求其它注解要meta-present在当前注解上
    if (!this.isAliasPair && isAnnotationmetaPresent(this.sourceAnnotationType, this.aliasedAnnotationType)) {
      String msg = String.format("@AliasFor declaration on attribute '%s' in annotation [%s] declares " +
                                 "an alias for attribute '%s' in meta-annotation [%s] which is not meta-present.",
                                 this.sourceAttributeName, this.sourceAnnotationType.getName(), this.aliasedAttributeName,
                                 this.aliasedAnnotationType.getName());
      throw new AnnotationConfigurationException(msg);
    }
    // 如果是注解内部的互相指向,那么相互指向的两个属性都需要标注AliasFor
    // 比如 AliasFor注解的attribute()和value()
    if (this.isAliasPair) {
      AliasFor mirrorAliasFor = this.aliasedAttribute.getAnnotation(AliasFor.class);
      if (mirrorAliasFor == null) {
        String msg = String.format("Attribute '%s' in annotation [%s] must be declared as an @AliasFor [%s].",
                                   this.aliasedAttributeName, this.sourceAnnotationType.getName(), this.sourceAttributeName);
        throw new AnnotationConfigurationException(msg);
      }

      // 互相指向时,属性名称要能对应上(A指向B,B也要指向A)
      String mirrorAliasedAttributeName = getAliasedAttributeName(mirrorAliasFor, this.aliasedAttribute);
      if (!this.sourceAttributeName.equals(mirrorAliasedAttributeName)) {
        String msg = String.format("Attribute '%s' in annotation [%s] must be declared as an @AliasFor [%s], not [%s].",
                                   this.aliasedAttributeName, this.sourceAnnotationType.getName(), this.sourceAttributeName,
                                   mirrorAliasedAttributeName);
        throw new AnnotationConfigurationException(msg);
      }
    }
    // 返回类型必须相同
    //  1. 完全相同
    //  2. 如果aliasedReturnType是数组的话,returnType必须和数组元素类型相同
    // 关于第2点,这是因为注解在书写时,如果是数组类型,并且只有一个元素的话,是可以省略
    // 花括号的
    Class returnType = this.sourceAttribute.getReturnType();
    Class aliasedReturnType = this.aliasedAttribute.getReturnType();
    if (returnType != aliasedReturnType &&
        (!aliasedReturnType.isArray() ||
         returnType != aliasedReturnType.getComponentType())) {
      String msg = String.format("Misconfigured aliases: attribute '%s' in annotation [%s] " +
                                 "and attribute '%s' in annotation [%s] must declare the same return type.",
                                 this.sourceAttributeName, this.sourceAnnotationType.getName(), this.aliasedAttributeName,
                                 this.aliasedAnnotationType.getName());
      throw new AnnotationConfigurationException(msg);
    }

    // 如果是相互指向,继续校验默认值
    // 如果不是相互指向,需要在获取了其它AliasDescriptor后校验
    if (this.isAliasPair) {
      validateDefaultValueConfiguration(this.aliasedAttribute);
    }
  }

  private void validateDefaultValueConfiguration(Method aliasedAttribute) {
    Object defaultValue = this.sourceAttribute.getDefaultValue();
    Object aliasedDefaultValue = aliasedAttribute.getDefaultValue();
    // 必须定义默认值
    if (defaultValue == null || aliasedDefaultValue == null) {
      String msg = String.format("Misconfigured aliases: attribute '%s' in annotation [%s] " +
                                 "and attribute '%s' in annotation [%s] must declare default values.",
                                 this.sourceAttributeName, this.sourceAnnotationType.getName(), aliasedAttribute.getName(),
                                 aliasedAttribute.getDeclaringClass().getName());
      throw new AnnotationConfigurationException(msg);
    }
    // 默认值必须相等
    if (!ObjectUtils.nullSafeEquals(defaultValue, aliasedDefaultValue)) {
      String msg = String.format("Misconfigured aliases: attribute '%s' in annotation [%s] " +
                                 "and attribute '%s' in annotation [%s] must declare the same default value.",
                                 this.sourceAttributeName, this.sourceAnnotationType.getName(), aliasedAttribute.getName(),
                                 aliasedAttribute.getDeclaringClass().getName());
      throw new AnnotationConfigurationException(msg);
    }
  }

创建AliasDescriptor对象的逻辑是比较简单的,校验的逻辑略微复杂一点,对于显式别名的第一种情况,它要求:

    AliasFor的value或attribute属性要互相指向对方互为别名的两个属性要有相同的返回类型互为别名的两个属性要有默认值,并且默认值也要相同

对于显式别名的第二种情况:

    AliasFor的attribute属性要指向meta annotation内的某个属性作为别名的属性要和它指向的meta annotation内的那个属性具有相同的返回类型AliasFor的annotation属性要正确指向某个meta annotation,并且这个meta annotation要meta present在注解上

对于隐式别名的情况:

    AliasFor的attribute属性要指向meta annotation内的某个属性,可以是直接指向也可以是传递指向互为隐式别名的属性要有相同的返回类型互为隐式别名的属性要有默认值,并且默认值也要相同AliasFor的annotation属性要正确指向某个meta annotation,并且这个meta annotation要meta present在注解上

创建并且校验通过以后,紧接着执行真正的getAttributeAliasNames(...)逻辑。

  
  public List getAttributeAliasNames() {
    // 相互指向,不涉及元注解
    if (this.isAliasPair) {
      // aliasedAttributeName就是所有的别名了
      // 因为同一注解内属性->别名是一对一的,一个属性不会有多个别名,否则通过不了校验
      return Collections.singletonList(this.aliasedAttributeName);
    }
    // 不是互相指向,因此是有meta annotation的情况
    // 遍历其它的AliasDescriptor
    List aliases = new ArrayList<>();
    for (AliasDescriptor descriptor : getOtherDescriptors()) {
      // this和descriptor互为别名
      if (this.isAliasFor(descriptor)) {
        // 绝大多数校验都在创建时执行了,除了默认值检验
        this.validateAgainst(descriptor);
        // 添加的是sourceAttributeName,也就是注解内的属性名
        aliases.add(descriptor.sourceAttributeName);
      }
    }
    return aliases;
  }

不涉及meta annotation的情况比较简单,有meta annotation的时候就需要遍历其它AliasDescriptor,逐个判断它们之间是否存在别名关系,这个判断逻辑就封装在isAliasFor(...)中。

  private boolean isAliasFor(AliasDescriptor otherDescriptor) {
    // 核心算法是沿着alias链循环比较
    for (AliasDescriptor lhs = this; lhs != null; lhs = lhs.getAttributeOverrideDescriptor()) {
      for (AliasDescriptor rhs = otherDescriptor; rhs != null; rhs = rhs.getAttributeOverrideDescriptor()) {
        if (lhs.aliasedAttribute.equals(rhs.aliasedAttribute)) {
          return true;
        }
      }
    }
    return false;
  }

isAliasFor(...)会沿着alias链逐层向上比较,这样就能处理有多层meta annotation时出现的别名传递,比如:

@Service
public @interface @TransitiveService {
  @AliasFor(annotation = Component.class, attribute = "value")
  String value() default "";
  
  @AliasFor(annotation = Service.class, attribute = "value")
  String beanName() default "";
}

TransitiveService#value()属性是Component#value()属性的别名, TransitiveService#beanName()属性是Service#value()属性的别名,而Service#value()属性也是Component#value()属性的别名,因此TransitiveService的value和beanName属性最终都指向了Component#value(),那么它们也应该互为别名,isAliasFor(...)的算法使得它可以正确处理这种情况。

  回到AnnotationUtils#isSynthesizable(...)方法,如果getAttributeAliasNames(...)返回了结果,说明注解属性上标注了AliasFor注解,也就有必要进行动态代理的包装了。继续回退,回到AnnotationUtils#synthesizeAnnotation(...)方法,现在万事俱备,着手对传入的注解进行动态代理。

  // AnnotationUtils#synthesizeAnnotation(...)片段
	// 创建动态代理来保证AliasFor语义
  DefaultAnnotationAttributeExtractor extractor = new DefaultAnnotationAttributeExtractor(annotation, annotatedElement);
  InvocationHandler handler = new SynthesizedAnnotationInvocationHandler(extractor);
  Class[] interfaces = new Class[]{annotationType, SynthesizedAnnotation.class};
  return (A) Proxy.newProxyInstance(annotation.getClass().getClassLoader(), interfaces, handler);

被代理的接口中包含SynthesizedAnnotation,前面提过它是用来防止重复代理的,继续看SynthesizedAnnotationInvocationHandler:

  // SynthesizedAnnotationInvocationHandler#invoke(...)
	@Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    // equals/hashCode/toString/annotationType
    // 上述四个方法是定义在Annotation接口中的,而所有的
    // 注解都隐式实现了Annotation接口,因此要特殊处理
    if (ReflectionUtils.isEqualsMethod(method)) {
      return annotationEquals(args[0]);
    }
    if (ReflectionUtils.isHashCodeMethod(method)) {
      return annotationHashCode();
    }
    if (ReflectionUtils.isToStringMethod(method)) {
      return annotationToString();
    }
    if (AnnotationUtils.isAnnotationTypeMethod(method)) {
      return annotationType();
    }
    // 除掉Annotation接口中的方法后,剩下的就都是属性方法了
    if (!AnnotationUtils.isAttributeMethod(method)) {
      throw new AnnotationConfigurationException(String.format(
        "Method [%s] is unsupported for synthesized annotation type [%s]", method, annotationType()));
    }
    return getAttributevalue(method);
  }


	// SynthesizedAnnotationInvocationHandler#getAttributevalue(...)
  private Object getAttributevalue(Method attributeMethod) {
    // 代理给attributeExtractor做真正的提取工作
    String attributeName = attributeMethod.getName();
    Object value = this.valueCache.get(attributeName);
    if (value == null) {
      value = this.attributeExtractor.getAttributevalue(attributeMethod);
      if (value == null) {
        String msg = String.format("%s returned null for attribute name [%s] from attribute source [%s]",
                                   this.attributeExtractor.getClass().getName(), attributeName, this.attributeExtractor.getSource());
        throw new IllegalStateException(msg);
      }

      // 如果返回的是另一个注解,尝试包装
      // 很可能返回的这个注解也有使用AliasFor标注
      if (value instanceof Annotation) {
        value = AnnotationUtils.synthesizeAnnotation((Annotation) value, this.attributeExtractor.getAnnotatedElement());
      } else if (value instanceof Annotation[]) {
        value = AnnotationUtils.synthesizeAnnotationArray((Annotation[]) value, this.attributeExtractor.getAnnotatedElement());
      }

      this.valueCache.put(attributeName, value);
    }

    // Clone arrays so that users cannot alter the contents of values in our cache.
    if (value.getClass().isArray()) {
      value = cloneArray(value);
    }

    return value;
  }

很遗憾SynthesizedAnnotationInvocationHandler只是一个骨架,实际的逻辑封装在AnnotationAttributeExtractor中。AnnotationAttributeExtractor是一个策略接口,它封装了从底层源中获取属性值的功能,底层源可能是一个注解或者Map,顺藤摸瓜,来到DefaultAnnotationAttributeExtractor,它的底层源就是传入的注解:

  @Override
  public final Object getAttributevalue(Method attribute) {
    String attributeName = attribute.getName();
    Object attributevalue = getRawAttributevalue(attribute);
    List aliasNames = this.attributeAliasMap.get(attributeName);
    if (aliasNames != null) {
      Object defaultValue = AnnotationUtils.getDefaultValue(this.annotationType, attributeName);
      for (String aliasName : aliasNames) {
        Object aliasValue = getRawAttributevalue(aliasName);
        // 存在多个别名的时候,只能设置其中一个的值
        // 否则的话,就不知道选取哪个作为最终的值了
        if (!ObjectUtils.nullSafeEquals(attributevalue, aliasValue) &&
            !ObjectUtils.nullSafeEquals(attributevalue, defaultValue) &&
            !ObjectUtils.nullSafeEquals(aliasValue, defaultValue)) {
          String elementName = (this.annotatedElement != null ? this.annotatedElement.toString() : "unknown element");
          throw new AnnotationConfigurationException(String.format(
            "In annotation [%s] declared on %s and synthesized from [%s], attribute '%s' and its " +
            "alias '%s' are present with values of [%s] and [%s], but only one is permitted.",
            this.annotationType.getName(), elementName, this.source, attributeName, aliasName,
            ObjectUtils.nullSafeToString(attributevalue), ObjectUtils.nullSafeToString(aliasValue)));
        }
        // 如果attributevalue没有设置的话
        // 让它同步别名的值,这样对外就保持了一致性
        if (ObjectUtils.nullSafeEquals(attributevalue, defaultValue)) {
          attributevalue = aliasValue;
        }
      }
    }

    return attributevalue;
  }

  @Override
  @Nullable
  protected Object getRawAttributevalue(Method attributeMethod) {
    // 通过方法调用获取值
    ReflectionUtils.makeAccessible(attributeMethod);
    return ReflectionUtils.invokeMethod(attributeMethod, getSource());
  }

  @Override
  @Nullable
  protected Object getRawAttributevalue(String attributeName) {
    Method attributeMethod = ReflectionUtils.findMethod(getAnnotationType(), attributeName);
    return (attributeMethod != null ? getRawAttributevalue(attributeMethod) : null);
  }

至此,属性值的提取水落石出,对注解的动态代理也告一段落,AliasFor的语义就在这不经意间被安排上了。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5710153.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存