spring源碼解析:元註解功能的實現

前言

眾所周知,spring 從 2.5 版本以後開始支持使用註解代替繁瑣的 xml 配置,到了 springboot 更是全面擁抱了註解式配置。平時在使用的時候,點開一些常見的等註解,會發現往往在一個註解上總會出現一些其他的註解,比如 @Service

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component // @Component
public @interface Service {
    @AliasFor(annotation = Component.class)
    String value() default "";
}

大部分情況下,我們可以將 @Service 註解等同於 @Component 註解使用,則是因為 spring 基於其 JDK 對元註解的機制進行了擴展。

在 java 中,元註解是指可以註解在其他註解上的註解,spring 中通過對這個機制進行了擴展,實現了一些原生 JDK 不支持的功能,比如允許在註解中讓兩個屬性互為別名,或者將一個帶有元註解的子註解直接作為元註解看待,或者在這個基礎上,通過 @AlisaFor 或者同名策略讓子註解的值覆蓋元註解的值。

筆者今天將基於 spring 5.2.x 的源碼研究 spring 如何實現這套功能的。

一、查找註解

入口

我們從最常用的 AnnotatedElementUtils#findMergedAnnotation 方法開始。

在 spring 中, 常見的get表示從某個元素直接聲明的註解中獲取註解,而 find 語義表示從一個元素的直接以及間接聲明的註解中查找註解。換而言之,即從包括該元素的註解、註解的元註解、接口或類的複雜層級結構中查找。MergedAnnotation 則表示一個存在層級結構的根註解聚合得到的「合併註解」,這個註解的各項屬性將會因為根註解和元註解的層級結構而有所不同。

findMergedAnnotation 從語義上理解,就是從一個元素以及全部他的接口或父類中,獲取指定類型的註解,然後將這些註解和註解上可能存在的元註解聚合為合併註解並返回。

該方法實現如下:

public static <A extends Annotation> A findMergedAnnotation(AnnotatedElement element, Class<A> annotationType) {
    // 1、下述任意情況下直接獲取元素上聲明的註解:
    // a.查找的註解屬於java、javax或者org.springframework.lang包
    // b.被處理的元素屬於java包,或被java包中的對象聲明,或者就是Ordered.class
    if (AnnotationFilter.PLAIN.matches(annotationType) ||
        AnnotationsScanner.hasPlainJavaAnnotationsOnly(element)) {
        return element.getDeclaredAnnotation(annotationType);
    }

    // 2、將元素上的全部註解合成MergedAnnotation
    return findAnnotations(element)
        // 3、從MergedAnnotation獲取與該類型對應的MergedAnnotations
        .get(annotationType, null, MergedAnnotationSelectors.firstDirectlyDeclared())
        // 4、根據MergedAnnotation通過動態代理生成一個註解實例
        .synthesize(MergedAnnotation::isPresent).orElse(null);
}

此處實際一個四步:

  • 判斷是否可以直接獲取元素聲明的註解,否則則執行 find
  • 將元素上的註解聚合成一個 MergedAnnotations
  • 從元素上的聚合註解中獲得與查找的 annotationType 對應的合成 MergedAnnotations
  • 根據獲得的MergedAnnotation通過動態代理生成一個註解實例;

在第一步,確定是否可以直接從元素上直接獲取聲明的註解從而避免性能消耗較大的 find

1、匹配註解

此處通過註解過濾器 AnnotationFilter來過濾註解,該類是一個函數式接口,用於匹配傳入的註解實例、類型或名稱。

@FunctionalInterface
public interface AnnotationFilter {

    // 根據實例匹配
    default boolean matches(Annotation annotation) {
        return matches(annotation.annotationType());
    }

    // 根據類型匹配
    default boolean matches(Class<?> type) {
        return matches(type.getName());
    }

    // 根據名稱匹配
    boolean matches(String typeName);

}

AnnotationFilter默認提供三個可選的靜態實例:

  • PLAIN:類是否屬於 java.langorg.springframework.lang 包;
  • JAVA:類是否屬於 javajavax包;
  • ALL:任何類;

此處過濾器選擇了 PLAIN,即當查找的註解屬於 java.langorg.springframework.lang 包的時候就不進行查找,而是直接從被查找的元素直接聲明的註解中獲取。這個選擇不難理解,java.lang包下提供的都是諸如@Resource或者 @Target 這樣的註解,而springframework.lang包下提供的則都是 @Nonnull 這樣的註解,這些註解基本不可能作為有特殊業務意義的元註解使用,因此默認忽略也是合理的。

實際上,PLAIN 也是大部分情況下的使用的默認過濾器。

2、匹配元素

若要查找的註解不屬於 java.langorg.springframework.lang 包,還需要確認被處理的元素。

這裡使用了AnnotationsScanner工具類,它的作用跟名字一樣,就是從各種 AnnotatedElement 以及複雜的嵌套層級中掃描並解析註解。

此處AnnotationsScanner.hasPlainJavaAnnotationsOnly(element) 這一段代碼如下:

static boolean hasPlainJavaAnnotationsOnly(@Nullable Object annotatedElement) {
    if (annotatedElement instanceof Class) {
        // 1.1 如果是類,則聲明它不能是java包下的,或者Ordered.class
        return hasPlainJavaAnnotationsOnly((Class<?>) annotatedElement);
    }
    else if (annotatedElement instanceof Member) {
        // 1.2 如果是類成員,則聲明它的類不能是java包下的,或者Ordered.class
        return hasPlainJavaAnnotationsOnly(((Member) annotatedElement).getDeclaringClass());
    }
    else {
        return false;
    }
}

// 1.1
static boolean hasPlainJavaAnnotationsOnly(Class<?> type) {
    return (type.getName().startsWith("java.") || type == Ordered.class);
}

// 1.2
static boolean hasPlainJavaAnnotationsOnly(Class<?> type) {
    return (type.getName().startsWith("java.") || type == Ordered.class);
}

簡而言之,就是被查詢的元素如果是或者屬於 java 包下的類以及 Ordered.class,則不進行查詢。

由於 java 包下的代碼都是標準庫,自定義的元註解不可能加到源碼中,因此只要類屬於 java包,則我們實際上是可以認為它是不可能有符合 spring 語義的元註解的。

小結

總結一下查找註解這一步操作:

當任意下述任意條件時,不進行 find,則是直接從元素上聲明的註解中獲取註解:

  • 判斷要查找的註解是否屬於 java.langorg.springframework.lang 包;
  • 被查找的元素如果是否是或者屬於 java 包下的類以及 Ordered.class

當上述條件皆不符合時,繼續進行 find,也就是下述過程。

二、獲得聚合註解

findAnnotations會經過多層的調用,實際上最終目的是創建一個 MergedAnnotations ,並且確定它的四個屬性:

  • 註解源 element,即要被查找的元素;
  • 查找策略 searchStrategy,即 MergedAnnotations.SearchStrategy 枚舉;
  • 重複容器註解 repeatableContainers,即@Repeatable 指定的對應容器註解;
  • 註解過濾器 annotationFilter,同上文,用於過濾註解;

這裡返回的是 MergedAnnotations 的實現類 TypeMappedAnnotations

// AnnotatedElementUtils
private static MergedAnnotations findAnnotations(AnnotatedElement element) {
    // 1、配置重複註解容器:空容器
    // 2、配置查找策略:查找類、全部父類以及其父接口
    return MergedAnnotations.from(element, SearchStrategy.TYPE_HIERARCHY, RepeatableContainers.none());
}

// MergedAnnotations
static MergedAnnotations from(AnnotatedElement element, SearchStrategy searchStrategy,
                              RepeatableContainers repeatableContainers) {
    // 3、配置註解過濾器:過濾屬於`java`、`javax`或者`org.springframework.lang`包的註解
    return from(element, searchStrategy, repeatableContainers, AnnotationFilter.PLAIN);
}
static MergedAnnotations from(AnnotatedElement element, SearchStrategy searchStrategy,
                              RepeatableContainers repeatableContainers, AnnotationFilter annotationFilter) {
    Assert.notNull(repeatableContainers, "RepeatableContainers must not be null");
    Assert.notNull(annotationFilter, "AnnotationFilter must not be null");
    return TypeMappedAnnotations.from(element, searchStrategy, repeatableContainers, annotationFilter);
}

// TypeMappedAnnotations
// 4、創建聚合註解:TypeMappedAnnotations
static MergedAnnotations from(AnnotatedElement element, SearchStrategy searchStrategy,
                              RepeatableContainers repeatableContainers, AnnotationFilter annotationFilter) {
    // 該元素若符合下述任一情況,則直接返回空註解:
    // a.被處理的元素屬於java包、被java包中的對象聲明,或者就是Ordered.class
    // b.只查找元素直接聲明的註解,但是元素本身沒有聲明任何註解
    // c.查找元素的層級結構,但是元素本身沒有任何層級結構
    // d.元素是橋接方法
    if (AnnotationsScanner.isKnownEmpty(element, searchStrategy)) {
        return NONE;
    }
    // 5、返回一個具體的實現類實例
    return new TypeMappedAnnotations(element, searchStrategy, repeatableContainers, annotationFilter);
}

1、配置重複註解容器

RepeatableContainers抽象類表示某個可重複註解與他的某個容器註解之間的對應關係,即常見的下述寫法:

// 可重複的註解
@Repeatable(RepeatableContainerAnnotation.class)
@interface RepeatableAnnotation {}

// 可重複註解的容器註解
@interface RepeatableContainerAnnotation {
    RepeatableAnnotation[] value() default {};
}

此處 RepeatableContainerAnnotation 就是 RepeatableAnnotation 的容器註解,他們就對應一個RepeatableContainers實例。

實際場景中甚至可能還存在 RepeatableContainerAnnotation 的容器註解……以此類推,無限套娃。因此,RepeatableContainers 實際上是一個樹結構,通過 parent 變量持有當前容器註解與容器註解的容器註解的對應關係。

一個RepeatableContainers中會通過 parent 成員變量持有他的容器註解,而容器註解同樣被封裝為 RepeatableContainers,若它也存在對應容器註解,則它也會通過 parent 變量持有他的容器註解……以此類推。

它的結構大概如下:

// 頂層抽象類
public abstract class RepeatableContainers {

    @Nullable
    private final RepeatableContainers parent; // 容器註解

    @Nullable
    Annotation[] findRepeatedAnnotations(Annotation annotation) {
        if (this.parent == null) {
            return null;
        }
        return this.parent.findRepeatedAnnotations(annotation); // 返回父節點的findRepeatedAnnotations方法返回值
    }

}

// 實現類
private static class ExplicitRepeatableContainer extends RepeatableContainers {

    private final Class<? extends Annotation> repeatable; // 可重複的註解

    private final Class<? extends Annotation> container; // 容器註解

    private final Method valueMethod; // 容器註解的value方法

    // 獲取可重複註解
    @Override
    @Nullable
    Annotation[] findRepeatedAnnotations(Annotation annotation) {
        // 若容器註解的value方法返回值就是可重複註解,說明容器註解就是該可重複註解的直接容器
        if (this.container.isAssignableFrom(annotation.annotationType())) {
            return (Annotation[]) ReflectionUtils.invokeMethod(this.valueMethod, annotation);
        }
        // 否則說明存在嵌套結構,當前容器註解實際上放的也是一個容器註解,繼續遞歸直到找到符合條件的容器註解為止
        return super.findRepeatedAnnotations(annotation);
    }
}

// 實現類
private static class StandardRepeatableContainers extends RepeatableContainers {

    private static final Map<Class<? extends Annotation>, Object> cache = new ConcurrentReferenceHashMap<>();

    private static final Object NONE = new Object();

    private static StandardRepeatableContainers INSTANCE = new StandardRepeatableContainers();

    StandardRepeatableContainers() {
        super(null);
    }

    @Override
    @Nullable
    Annotation[] findRepeatedAnnotations(Annotation annotation) {
        Method method = getRepeatedAnnotationsMethod(annotation.annotationType());
        if (method != null) {
            return (Annotation[]) ReflectionUtils.invokeMethod(method, annotation);
        }
        return super.findRepeatedAnnotations(annotation);
    }

    @Nullable
    private static Method getRepeatedAnnotationsMethod(Class<? extends Annotation> annotationType) {
        Object result = cache.computeIfAbsent(annotationType,
                                              StandardRepeatableContainers::computeRepeatedAnnotationsMethod);
        return (result != NONE ? (Method) result : null);
    }

    private static Object computeRepeatedAnnotationsMethod(Class<? extends Annotation> annotationType) {
        AttributeMethods methods = AttributeMethods.forAnnotationType(annotationType);
        // 只有一個名為value的屬性
        if (methods.hasOnlyValueAttribute()) {
            Method method = methods.get(0);
            Class<?> returnType = method.getReturnType();
            // 返回值是可重複註解類型的數組,並且可重複註解上存在@Repeatable註解
            if (returnType.isArray()) {
                Class<?> componentType = returnType.getComponentType();
                if (Annotation.class.isAssignableFrom(componentType) &&
                    componentType.isAnnotationPresent(Repeatable.class)) {
                    return method;
                }
            }
        }
        return NONE;
    }
}

在默認情況下,返回一個名為 NONE 的實例,該容器註解實例表示查找的註解不存在對應容器註解。

2、配置查找策略

查找策略MergedAnnotations.SearchStrategy 是一個內部的枚舉類,他提供以下選項:

  • DIRECT:只查找元素上直接聲明的註解,不包括通過@Inherited繼承的註解;

  • INHERITED_ANNOTATIONS:只查找元素直接聲明或通過@Inherited繼承的註解;

  • SUPERCLASS:查找元素直接聲明或所有父類的註解;

  • TYPE_HIERARCHY:查找元素、所有父類以及實現的父接口的全部註解;

  • TYPE_HIERARCHY_AND_ENCLOSING_CLASSES:查找封閉類以及其子類。

    封閉類是 JDK17 的新特性,可參考 詳解 Java 17中的新特性:「密封類」,本章將不過多涉及該內容;

當不指定時,默認的查找策略為TYPE_HIERARCHY,即查找元素、所有父類以及實現的父接口的全部註解。

3、配置註解過濾器

同上,這裡使用了默認的 PLAIN 過濾器,用於過濾屬於 java.langorg.springframework.lang 包的註解。

4、創建聚合註解

MergedAnnotations本身實現了Iterable接口,用於表示一組處於聚合狀態的 MergedAnnotation,而MergedAnnotation 就是對應我們實際上的合併註解,舉個例子:

假如我們有個 Foo.class,類上存在 @AnnotationA@AnnotationB 兩個註解,這兩個註解又都有一大堆的元註解。此時 @AnnotationA@AnnotationB 則各表示一個 MergedAnnotation,而 MergedAnnotations 表示 Foo.class 上的兩個MergedAnnotation

MergedAnnotations提供了四個比較重要的靜態方法:

  • get:用於從聚合註解中獲取某個指定類型的合併註解;
  • stream:用於從聚合註解中獲取多個指定類型的合併註解構成的 stream 流;
  • isPresent:某個類型的合併註解是否在該聚合中存在;
  • from:解析某個帶有註解的元素獲得對應的聚合註解;

TypeMappedAnnotations 則為該接口的主要實現類,這一步最終返回的就是一個TypeMappedAnnotations的實例。

小結

總結一下findAnnotations這一步操作,根本目的就是獲得一個TypeMappedAnnotations 實現,步驟如下:

  1. 配置重複註解容器:這裡指定了一個 NONE ,即註解沒有對應的容器註解;
  2. 配置查找策略:這裡指定查找類、全部父類以及其父接口;
  3. 配置註解過濾器:這裡指定類型為 PLAIN,默認過濾屬於javajavax或者org.springframework.lang包的註解;
  4. 創建聚合註解:這裡根據上述配置創建了一個 TypeMappedAnnotations實例;

三、從聚合註解中獲取合併註解

在第三步,將通過 MergedAnnotations#get 方法獲得指定類型對應的合併註解 MergedAnnotation實例,這裡我們以實現類 TypeMappedAnnotations 為例:

@Override
public <A extends Annotation> MergedAnnotation<A> get(Class<A> annotationType,
                                                      @Nullable Predicate<? super MergedAnnotation<A>> predicate,
                                                      @Nullable MergedAnnotationSelector<A> selector) {
    // 1、若該註解無法通過過濾,即該註解若屬於 `java.lang`、`org.springframework.lang` 包,則直接返回空註解
    if (this.annotationFilter.matches(annotationType)) {
        return MergedAnnotation.missing();
    }
    // 2、使用MergedAnnotationFinder掃描並獲取註解
    MergedAnnotation<A> result = scan (
        annotationType, new MergedAnnotationFinder<>(annotationType, predicate, selector)
    );
    return (result != null ? result : MergedAnnotation.missing());
}

@Nullable
private <C, R> R scan(C criteria, AnnotationsProcessor<C, R> processor) {
    if (this.annotations != null) {
        // a.若指定了查找的註解,則掃描這些註解以及其元註解的層級結構
        R result = processor.doWithAnnotations(criteria, 0, this.source, this.annotations);
        return processor.finish(result);
    }
    if (this.element != null && this.searchStrategy != null) {
        // b.未指定查找的註解,則直接掃描元素以及其父類、父接口的層級結構
        return AnnotationsScanner.scan(criteria, this.element, this.searchStrategy, processor);
    }
    return null;
}

1、註解選擇器

在創建合併註解 MergedAnnotation 時,需要傳入一個選擇器MergedAnnotationSelector

MergedAnnotationSelector本質上就是一個比較器,用於從兩個註解中選擇出一個權重更高的註解,此處的「權重」實際就是指註解離被查找元素的距離,距離越近權重就越高,舉個例子:

假如現在有個被查找元素 Foo.class,他上面有一個註解@A@A上還有一個元註解 @B,此時@A距離Foo.class的距離是 0 ,即@A 是在 Foo.class 上直接聲明的,而@B距離Foo.class的距離就是 1 ,當 @A@B 二選一的時候,距離更近的@A的權重就更高,換而言之,就是更匹配。

他在MergedAnnotationSelectors中提供了NearestFirstDirectlyDeclared兩個默認的實現,也基本都遵循這個規則:

// 距離優先選擇器
private static class Nearest implements MergedAnnotationSelector<Annotation> {

    @Override
    public boolean isBestCandidate(MergedAnnotation<Annotation> annotation) {
        return annotation.getDistance() == 0; // 若註解是否被元素直接聲明
    }

    @Override
    public MergedAnnotation<Annotation> select(
        MergedAnnotation<Annotation> existing, MergedAnnotation<Annotation> candidate) {

        // 若候選註解離元素的距離比當前註解更近,則返回候選註解,否則返回當前註解
        if (candidate.getDistance() < existing.getDistance()) { 
            return candidate;
        }
        return existing;
    }

}

// 直接聲明註解選擇器
private static class FirstDirectlyDeclared implements MergedAnnotationSelector<Annotation> {

    @Override
    public boolean isBestCandidate(MergedAnnotation<Annotation> annotation) {
        return annotation.getDistance() == 0; // 若註解是否被元素直接聲明
    }

    // 若當前註解沒有被元素直接聲明,而候選註解被元素直接聲明時返回候選註解,否則返回已有註解
    @Override
    public MergedAnnotation<Annotation> select(
        MergedAnnotation<Annotation> existing, MergedAnnotation<Annotation> candidate) {

        if (existing.getDistance() > 0 && candidate.getDistance() == 0) {
            return candidate;
        }
        return existing;
    }

}

2、創建註解處理器

回到 MergedAnnotations#get() 方法,這裡出現了一個新類 MergedAnnotationFinder,它是TypeMappedAnnotations 中的一個內部類,它實現了 AnnotationsProcessor 接口,是註解處理器的一種,會在查找到註解後被回調。

當調用他的 doWithAnnotations 方法時,他將會把入參的註解包括對應的所有元註解解析為一堆 AnnotationTypeMapping,然後遍歷並篩選出所需要的註解類型對應的 AnnotationTypeMapping,再封裝為一堆對應的 MergedAnnotation,最後再用選擇器從裏面選擇出最匹配的 MergedAnnotation並返回。

對應代碼如下:

private class MergedAnnotationFinder<A extends Annotation>
    implements AnnotationsProcessor<Object, MergedAnnotation<A>> {

    // 要查找的註解類型
    private final Object requiredType;

    // 過濾器
    @Nullable
    private final Predicate<? super MergedAnnotation<A>> predicate;

    // 選擇器,作用類似於比較器,用於從兩個註解中獲得一個權重更高的註解實例
    private final MergedAnnotationSelector<A> selector;

    // 最終的返回結構
    @Nullable
    private MergedAnnotation<A> result;

    MergedAnnotationFinder(Object requiredType, @Nullable Predicate<? super MergedAnnotation<A>> predicate,
                           @Nullable MergedAnnotationSelector<A> selector) {

        this.requiredType = requiredType;
        this.predicate = predicate;
        // 若不指定選擇器,則默認使用MergedAnnotationSelectors.Nearest
        // 當存在兩個相同註解式,選擇層級更低的,即離根註解更近的註解
        this.selector = (selector != null ? selector : MergedAnnotationSelectors.nearest());
    }

    @Override
    @Nullable
    public MergedAnnotation<A> doWithAggregate(Object context, int aggregateIndex) {
        return this.result;
    }

    @Override
    @Nullable
    public MergedAnnotation<A> doWithAnnotations(Object type, int aggregateIndex,
                                                 @Nullable Object source, Annotation[] annotations) {

        for (Annotation annotation : annotations) {
            // 找到至少一個不被過濾的、並且可以合成合併註解的註解實例
            if (annotation != null && !annotationFilter.matches(annotation)) {
                MergedAnnotation<A> result = process(type, aggregateIndex, source, annotation);
                if (result != null) {
                    return result;
                }
            }
        }
        return null;
    }

    @Nullable
    private MergedAnnotation<A> process(
        Object type, int aggregateIndex, @Nullable Object source, Annotation annotation) {

        // 1、若要查找的註解可重複,則先找到其容器註解,然後獲取容器中的可重複註解並優先處理
        Annotation[] repeatedAnnotations = repeatableContainers.findRepeatedAnnotations(annotation);
        if (repeatedAnnotations != null) {
            return doWithAnnotations(type, aggregateIndex, source, repeatedAnnotations);
        }

        // 2、解析註解與註解的映射關係
        AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(
            annotation.annotationType(), repeatableContainers, annotationFilter);
        // 遍歷已解析好的AnnotationTypeMapping實例,並找到相同註解類型的AnnotationTypeMapping接着將其封裝為MergedAnnotation
        // 然後繼續下一次尋找,若還有匹配的結果,則根據選擇器從中找到更合適的結果,最終返回一個最匹配結果
        for (int i = 0; i < mappings.size(); i++) {
            AnnotationTypeMapping mapping = mappings.get(i);
            if (isMappingForType(mapping, annotationFilter, this.requiredType)) {
                // 3、嘗試創建一個合併註解
                MergedAnnotation<A> candidate = TypeMappedAnnotation.createIfPossible(
                    mapping, source, annotation, aggregateIndex, IntrospectionFailureLogger.INFO);
                // 4、若合併註解創建成功,且過濾器匹配通過
                if (candidate != null && (this.predicate == null || this.predicate.test(candidate))) {
                    // a.合併註解是最匹配的結果
                    if (this.selector.isBestCandidate(candidate)) {
                        return candidate;
                    }
                    // b.使用選擇器從上一結果和當前結果中選擇一個權重更高的註解,做為新的結果
                    updateLastResult(candidate);
                }
            }
        }
        return null;
    }

    private void updateLastResult(MergedAnnotation<A> candidate) {
        MergedAnnotation<A> lastResult = this.result;
        this.result = (lastResult != null ? this.selector.select(lastResult, candidate) : candidate);
    }

    @Override
    @Nullable
    public MergedAnnotation<A> finish(@Nullable MergedAnnotation<A> result) {
        return (result != null ? result : this.result);
    }
}

3、解析註解與註解的映射關係

MergedAnnotationFinder#process()方法中出現了 AnnotationTypeMappings ,該類型表示一個註解與其元註解之間關聯關係 AnnotationTypeMapping的集合。直白點說,AnnotationTypeMappings用於描述一個註解有哪些元註解,元註解又有哪些元註解。

AnnotationTypeMapping是整個元註解機制實現的核心,除了註解關係的映射外,它還為屬性別名等機制提供支持,這部分內容將在後文更詳細的介紹。

AnnotationTypeMappings.forAnnotationType靜態方法用於創建一個AnnotationTypeMappings實例:

static AnnotationTypeMappings forAnnotationType(Class<? extends Annotation> annotationType,
                                                RepeatableContainers repeatableContainers, AnnotationFilter annotationFilter) {

    // 針對可重複註解的容器緩存
    if (repeatableContainers == RepeatableContainers.standardRepeatables()) {
        return standardRepeatablesCache.computeIfAbsent(annotationFilter,
                                                        key -> new Cache(repeatableContainers, key)).get(annotationType);
    }
    // 針對不可重複註解的容器緩存
    if (repeatableContainers == RepeatableContainers.none()) {
        return noRepeatablesCache.computeIfAbsent(annotationFilter,
                                                  key -> new Cache(repeatableContainers, key)).get(annotationType);
    }
    // 創建一個AnnotationTypeMappings實例
    return new AnnotationTypeMappings(repeatableContainers, annotationFilter, annotationType);
}

出於減少重複的解析操作的目的,AnnotationTypeMappings 類維護了 noRepeatablesCachestandardRepeatablesCache 兩個 Map 集合用於存儲已經解析好的註解類和其可重複註解容器的映射關係。

最終調用的 AnnotationTypeMappings 實例構造方法如下:

private AnnotationTypeMappings(RepeatableContainers repeatableContainers,
                               AnnotationFilter filter, Class<? extends Annotation> annotationType) {

    this.repeatableContainers = repeatableContainers; // 可重複註解的容器
    this.filter = filter; // 過濾
    this.mappings = new ArrayList<>(); // 映射關係
    addAllMappings(annotationType); // 解析當前類以及其元註解的層次結構中涉及到的全部映射關係
    this.mappings.forEach(AnnotationTypeMapping::afterAllMappingsSet); // 映射關係解析完後對別名的一些校驗
}

顯而易見,addAllMappings() 方法就是最關鍵的步驟,這個方法將用於將元註解的類型跟聲明元註解的數據源進行綁定。

舉個例子,假如現在有一個註解 @A,上面還有一個元註解 @B@B上又存在一個元註解 @C則解析流程如下:

  • 解析註解 @A,由於其已經是根註解了,故此時數據源為 null ,將數據源與他的元註解 @A 封裝為一個AnnotationTypeMapping,這裡稱為 M1。則 M1 即為元註解 @A 與數據源的映射;
  • 解析上一步得到的數據源,也就是M1,然後獲其中元註解 @A 上的元註解 @B,然後將數據源 M1@B 再封裝為一個AnnotationTypeMapping,這裡稱為 M2。則 M2 即為元註解 @BM1 ——或者說 @A ——的映射;
  • 以此類推,廣度優先遍歷到最後一層;

最終,所有的註解的映射 M1M2M3都被添加到了 AnnotationTypeMappingsmapping集合,並且 M1M2 以及 M3也按照 @A@B的關係建立了關係,就像一個 LinkedHashMap

image-20220616175049158

具體代碼如下:

private void addAllMappings(Class<? extends Annotation> annotationType) {
    // 廣度優先遍歷註解和元註解
    Deque<AnnotationTypeMapping> queue = new ArrayDeque<>();
    addIfPossible(queue, null, annotationType, null); // 1.1 添加待解析的元註解
    while (!queue.isEmpty()) {
        AnnotationTypeMapping mapping = queue.removeFirst();
        this.mappings.add(mapping);
        // 繼續解析下一層
        addMetaAnnotationsToQueue(queue, mapping);  // 1.2 解析的元註解
    }
}

// 1.1 添加待解析的元註解
private void addIfPossible(Deque<AnnotationTypeMapping> queue, @Nullable AnnotationTypeMapping source,
                           Class<? extends Annotation> annotationType, @Nullable Annotation ann) {
    try {
        // 將數據源、元註解類型和元註解實例封裝為一個AnnotationTypeMapping,作為下一次處理的數據源
        queue.addLast(new AnnotationTypeMapping(source, annotationType, ann));
    }
    catch (Exception ex) {
        AnnotationUtils.rethrowAnnotationConfigurationException(ex);
        if (failureLogger.isEnabled()) {
            failureLogger.log("Failed to introspect meta-annotation " + annotationType.getName(),
                              (source != null ? source.getAnnotationType() : null), ex);
        }
    }
}

// 1.2 解析的元註解
private void addMetaAnnotationsToQueue(Deque<AnnotationTypeMapping> queue, AnnotationTypeMapping source) {
    // 獲取當前註解上直接聲明的元註解
    Annotation[] metaAnnotations = AnnotationsScanner.getDeclaredAnnotations(source.getAnnotationType(), false);
    for (Annotation metaAnnotation : metaAnnotations) {
        // 若已經解析過了則跳過,避免「循環引用」
        if (!isMappable(source, metaAnnotation)) {
            continue;
        }
        // a.若當前正在解析的註解是容器註解,則將內部的可重複註解取出解析
        Annotation[] repeatedAnnotations = this.repeatableContainers.findRepeatedAnnotations(metaAnnotation);
        if (repeatedAnnotations != null) {
            for (Annotation repeatedAnnotation : repeatedAnnotations) {
                // 1.2.1 判斷是否已經完成映射
                if (!isMappable(source, repeatedAnnotation)) {
                    continue;
                }
                addIfPossible(queue, source, repeatedAnnotation);
            }
        }
        // b.若當前正在解析的註解不是容器註解,則將直接解析
        else {
            addIfPossible(queue, source, metaAnnotation);
        }
    }
}

// 1.2.1 判斷是否已經完成映射
private boolean isMappable(AnnotationTypeMapping source, @Nullable Annotation metaAnnotation) {
    return (metaAnnotation != null && !this.filter.matches(metaAnnotation) &&
            !AnnotationFilter.PLAIN.matches(source.getAnnotationType()) &&
            !isAlreadyMapped(source, metaAnnotation));
}
private boolean isAlreadyMapped(AnnotationTypeMapping source, Annotation metaAnnotation) {
    Class<? extends Annotation> annotationType = metaAnnotation.annotationType();
    // 遞歸映射表,確定這個註解類型是否在映射表的樹結構中存在
    // 這個做法相當於在循環引用中去重
    AnnotationTypeMapping mapping = source;
    while (mapping != null) {
        if (mapping.getAnnotationType() == annotationType) {
            return true;
        }
        mapping = mapping.getSource();
    }
    return false;
}

小結

image-20220620110545880

當獲得聚合註解 MergedAnnotations 後,再通過 get 方法獲取到指定的合併註解這個過程,需要經過下述四個過程。

  1. 在聚合註解 MergedAnnotations 中根據傳入的註解過濾器AnnotationFilter以及一些校驗,從指定的註解或數據源的層級結構中獲取待解析的註解;
  2. 創建一個註解處理器MergedAnnotationFinder,然後再為其創建一個註解選擇器 MergedAnnotationSelector,該處理器將用於處理上一個步驟掃描到的待解析註解;
  3. 遍歷待解析註解,然後使用註解處理器 MergedAnnotationFinder 處理:
    • 先根據註解的類型創建一個聚合的註解類型映射表 AnnotationTypeMappings,該表用於表示一個數據源上所有註解以及元註解之間的層級關係;
    • 聚合的註解類型映射表 AnnotationTypeMappings會根據廣度優先遍歷待解析的註解的層級結構,然後依次以數據源上的某個註解作為根註解,然後它和它的某個元註解的映射關係封裝為AnnotationTypeMapping
    • AnnotationTypeMapping對應的註解仍然存在元註解,就繼續一層一層的遞歸,直到整個層級結構都被轉為AnnotationTypeMapping並且記錄到 AnnotationTypeMappings為止,此時每個根註解對應的AnnotationTypeMappings都維持着一個類似鏈表的接口,元註解的AnnotationTypeMapping會持有聲明它的數據源註解的AnnotationTypeMapping引用;
  4. MergedAnnotationFinder 將遍歷並遞歸所有AnnotationTypeMapping,篩選出所有匹配的AnnotationTypeMapping,接着再將其封裝為 MergedAnnotation,最後使用MergedAnnotationSelector從中選擇出一個最合適的——總體來說離根註解越近越合適——作為 get 的查詢結果。

四、註解中的屬性映射

由於 spring 提供了基於 @AliasFor 註解的別名機制,允許註解內部的屬性互為別名,或者與它的元註解中的屬性互為別名,這一步同樣在AnnotationTypeMapping創建時完成,由於涉及到內容較多,因此作為單獨的一節描述。

根據@AlisaFor 作用與註解內和註解外,造成的效果可以簡單分為兩種:

  • 鏡像:當同一註解類中的兩個屬性互為別名時,則對兩者任一屬性賦值,等同於對另一屬性賦值;
  • 覆寫:當子註解和元註解中的兩個屬性互為別名時,對子註解中的屬性賦值,將覆蓋元註解中的屬性;

其中,鏡像效果依賴於 MirrorSet,而覆寫的效果依賴於 AnnotationTypeMapping 中各種 mapping 結尾的變量,這些數據結構共同維護的同註解與不同註解間的屬性映射關係,這些都在AnnotationTypeMapping的構造方法中完成:

AnnotationTypeMapping(@Nullable AnnotationTypeMapping source,
                      Class<? extends Annotation> annotationType, @Nullable Annotation annotation) {

    this.source = source; // 聲明當前元註解類型的數據源
    this.root = (source != null ? source.getRoot() : this); // 根節點
    this.distance = (source == null ? 0 : source.getDistance() + 1); // 距離根節點的距離
    this.metaTypes = merge( // 到當前元註解為止前面合併了多少元註解
        source != null ? source.getMetaTypes() : null,
        annotationType);

    // 當前元註解與類型
    this.annotationType = annotationType;
    this.annotation = annotation;
    // 當前元註解的屬性
    this.attributes = AttributeMethods.forAnnotationType(annotationType);
    // 屬性別名與相關的值緩存
    this.mirrorSets = new MirrorSets();
    this.aliasMappings = filledIntArray(this.attributes.size());
    this.conventionMappings = filledIntArray(this.attributes.size());
    this.annotationValueMappings = filledIntArray(this.attributes.size());
    this.annotationValueSource = new AnnotationTypeMapping[this.attributes.size()];
    this.aliasedBy = resolveAliasedForTargets();

    // 初始化別名屬性,為所有存在別名的屬性建立MirrorSet
    processAliases();
    // 為當前註解內互為併名的屬性建立屬性映射
    addConventionMappings();
    // 為跨註解互為別名的屬性建立屬性映射
    addConventionAnnotationValues();
    this.synthesizable = computeSynthesizableFlag();
}

1、解析註解屬性

AnnotationTypeMapping 創建時,將會通過反射把當前的元註解全部屬性的獲取方法解析出來,然後封裝為一個聚合屬性 AttributeMethods 並賦值給同名變量,在構造函數中對應代碼 :

this.attributes = AttributeMethods.forAnnotationType(annotationType)

AttributeMethods 內部維護了一個方法的數組,並以此提供基於下標或方法名稱訪問註解屬性的能力。

這裡尤其需要注意的是,或許是出於性能考慮,spring 在元註解屬性映射這邊的代碼實現,幾乎全部都是依靠數組記錄變量,然後依靠下標來傳遞引用關係。

所以這裡需要特別記住一點,在後續幾乎所有數組的下標,都與AttributeMethods中屬性方法的數組下標對應,即某個屬性在屬性方法數組中的下標 index = 1 ,則後續所有相關數組下標為 1 的位置,都與該屬性有關。

2、解析@AliasFor註解的屬性

首先,spring 將AttributeMethods中所有的帶有@AliasFor註解的屬性方法取出,然後解析註解並生成別名屬性映射表 aliasedBy,這一段在構造函數中對應:

this.aliasedBy = resolveAliasedForTargets();

resolveAliasedForTargets 對應實現如下:

private Map<Method, List<Method>> resolveAliasedForTargets() {
    Map<Method, List<Method>> aliasedBy = new HashMap<>();
    for (int i = 0; i < this.attributes.size(); i++) {
        // 遍歷當前註解的屬性方法,並獲取其中的帶有@AliasFor的方法
        Method attribute = this.attributes.get(i);
        AliasFor aliasFor = AnnotationsScanner.getDeclaredAnnotation(attribute, AliasFor.class);
        if (aliasFor != null) {
            // 獲取別名指定的註解類中的方法,並建立別名屬性 -> [屬性1]的映射集合
            Method target = resolveAliasTarget(attribute, aliasFor);
            aliasedBy.computeIfAbsent(target, key -> new ArrayList<>()).add(attribute);
        }
    }
    return Collections.unmodifiableMap(aliasedBy);
}

private Method resolveAliasTarget(Method attribute, AliasFor aliasFor) {
    return resolveAliasTarget(attribute, aliasFor, true);
}

resolveAliasTarget 最終將獲得@AlisaFor註解所指定的別名方法,具體如下:

private Method resolveAliasTarget(Method attribute, AliasFor aliasFor, boolean checkAliasPair) {
    if (StringUtils.hasText(aliasFor.value()) && StringUtils.hasText(aliasFor.attribute())) {
        throw new AnnotationConfigurationException(String.format(
            "In @AliasFor declared on %s, attribute 'attribute' and its alias 'value' " +
            "are present with values of '%s' and '%s', but only one is permitted.",
            AttributeMethods.describe(attribute), aliasFor.attribute(),
            aliasFor.value()));
    }

    // 1、若Annotation指定的是Annotation,則認為目標就是當前註解類
    Class<? extends Annotation> targetAnnotation = aliasFor.annotation();
    if (targetAnnotation == Annotation.class) {
        targetAnnotation = this.annotationType;
    }

    // 2、獲取alisaFrom#attribute,若為空則再獲取alisaFrom#value
    String targetAttributeName = aliasFor.attribute();
    if (!StringUtils.hasLength(targetAttributeName)) {
        targetAttributeName = aliasFor.value();
    }
    if (!StringUtils.hasLength(targetAttributeName)) {
        targetAttributeName = attribute.getName();
    }

    // 3、從指定類中獲得別名指定指定的註解屬性對應的方法
    Method target = AttributeMethods.forAnnotationType(targetAnnotation).get(targetAttributeName);
    if (target == null) {
        // a.校驗是否能找到別名方法
        if (targetAnnotation == this.annotationType) {
            throw new AnnotationConfigurationException(String.format(
                "@AliasFor declaration on %s declares an alias for '%s' which is not present.",
                AttributeMethods.describe(attribute), targetAttributeName));
        }
        throw new AnnotationConfigurationException(String.format(
            "%s is declared as an @AliasFor nonexistent %s.",
            StringUtils.capitalize(AttributeMethods.describe(attribute)),
            AttributeMethods.describe(targetAnnotation, targetAttributeName)));
    }
    // b.校驗別名與原屬性對應的方法是否不為一個方法
    if (target.equals(attribute)) {
        throw new AnnotationConfigurationException(String.format(
            "@AliasFor declaration on %s points to itself. " +
            "Specify 'annotation' to point to a same-named attribute on a meta-annotation.",
            AttributeMethods.describe(attribute)));
    }
    // c.校驗別名與原屬性對應的方法返回值是否一致
    if (!isCompatibleReturnType(attribute.getReturnType(), target.getReturnType())) {
        throw new AnnotationConfigurationException(String.format(
            "Misconfigured aliases: %s and %s must declare the same return type.",
            AttributeMethods.describe(attribute),
            AttributeMethods.describe(target)));
    }
    // d.若有必要,則再校驗聲明別名方法的註解是@AlisaFor指定的註解類型
    if (isAliasPair(target) && checkAliasPair) {
        AliasFor targetAliasFor = target.getAnnotation(AliasFor.class);
        if (targetAliasFor != null) {
            Method mirror = resolveAliasTarget(target, targetAliasFor, false);
            if (!mirror.equals(attribute)) {
                throw new AnnotationConfigurationException(String.format(
                    "%s must be declared as an @AliasFor %s, not %s.",
                    StringUtils.capitalize(AttributeMethods.describe(target)),
                    AttributeMethods.describe(attribute), AttributeMethods.describe(mirror)));
            }
        }
    }
    return target;
}

在這一步,他做了以下邏輯處理:

  1. 確定別名屬性所在的註解類:若@AlisaFor#annotation屬性保持默認值Annotation.class,則認為別名屬性所在的註解就是當前解析的註解;
  2. 確定別名屬性對應的方法名:優先獲取alisaFrom#attribute同名屬性,若alisaFrom#attribute為空則獲取alisaFrom#value同名方法;
  3. 獲取別名屬性對應的方法;
  4. 校驗該別名方法對應方法是否不是當前註解屬性的方法;
  5. 校驗別名方法返回值類型與當前註解屬性的方法返回值類型是否一致;
  6. 校驗聲明該方法的類就是註解指定的註解類;

最終,完成這一步後,將構建出以別名方法作為 key,當前註解中對應的原始屬性的方法作為 value的別名屬性-原始屬性映射表 aliasedBy

3、處理註解屬性

在第二步在aliasedBy中加載了當前註解中所有別名屬性與註解中原始屬性的映射關係後,將根據此進一步組織註解屬性與別名的映射關係。此步驟對應構造函數中的代碼片段如下:

this.mirrorSets = new MirrorSets();
this.aliasMappings = filledIntArray(this.attributes.size());
this.conventionMappings = filledIntArray(this.attributes.size());
this.annotationValueMappings = filledIntArray(this.attributes.size());
this.annotationValueSource = new AnnotationTypeMapping[this.attributes.size()];

// 初始化別名屬性,為所有存在別名的屬性建立MirrorSet
processAliases();

processAliases 是直接入口:

private void processAliases() {
    List<Method> aliases = new ArrayList<>();
    // 遍歷當前註解中的屬性,處理屬性與其相關的別名
    for (int i = 0; i < this.attributes.size(); i++) {
        aliases.clear(); // 復用集合避免重複創建
        aliases.add(this.attributes.get(i));
        // 1.收集註解
        collectAliases(aliases); 
        if (aliases.size() > 1) {
            // 2.處理註解
            processAliases(i, aliases);
        }
    }
}

從功能來說,這段代碼分為採集註解和處理註解兩部分:

搜集以當前註解屬性作為別名的子註解屬性

private void collectAliases(List<Method> aliases) {
    AnnotationTypeMapping mapping = this;
    while (mapping != null) {
        int size = aliases.size();
        for (int j = 0; j < size; j++) {
            List<Method> additional = mapping.aliasedBy.get(aliases.get(j)); // 獲取以該屬性作為別名的子類屬性
            if (additional != null) {
                aliases.addAll(additional);
            }
        }
        mapping = mapping.source; // 繼續向聲明當前元註解的子註解遞歸
    }
}

收集註解這一步,將以當前元註解的某個屬性為根屬性,從當前元註解向子註解遞歸,並最終收集到全部直接或間接以當前根屬性作為別名的子類屬性。

比如,假如A.name的別名是 B.nameB.name的別名是 C.name,則從 C.name 開始向子註解遞歸,最終在 aliases 集合中收集到的就是 [C.name, B.name, A.name]

當然,加入 A.name 還存在一個別名 A.alisaName,則實際最終在 aliases 集合中收集到的就是 [C.name, C.alisaName, B.name, A.name]

處理註解

處理註解的 processAliases 增體流程依然是從當前元註解遞歸向子註解進行,並且處理過程中的邏輯大體分為三部分:

  • 第一部分,若根註解——即最小的子註解——存在以元註解屬性作為別名的原始屬性,則以根註解屬性覆蓋元註解中的屬性,並在該元註解的成員變量aliasMappings 中記錄根註解原始屬性的下標;
  • 第二部分,為各級註解中同一註解內互為別名的字段,以及根註解中不存在的、且不同註解間互為別名的字段建立鏡像映射關係表 MirrorSet
  • 第三部分,根據MirrorSet,構建各級註解中被作為別名屬性的屬性,與調用時實際對應的註解屬性及子類註解實例的映射表annotationValueMappings annotationValueSource
private void processAliases(int attributeIndex, List<Method> aliases) {
    // 1.若根註解——即最小的子註解——存在以元註解屬性作為別名的原始屬性,則以根註解屬性覆蓋元註解中的屬性,並在該元註解的成員變量`aliasMappings` 中記錄根註解原始屬性的下標;
    int rootAttributeIndex = getFirstRootAttributeIndex(aliases); // 若根註解中存在以aliases任意屬性作為別名的屬性,則返回跟註解的屬性方法下標
    // 從當前元註解向子註解遞歸
    AnnotationTypeMapping mapping = this;
    while (mapping != null) {
        // 若根註解中存在以aliases任意屬性作為別名的屬性,且當前處理的註解不是根註解
        // 則將當前處理的註解aliasMappings與設置為根註解中對應屬性的值
        // 即使用子註解的值覆蓋元註解的值
        if (rootAttributeIndex != -1 && mapping != this.root) {
            for (int i = 0; i < mapping.attributes.size(); i++) {
                if (aliases.contains(mapping.attributes.get(i))) {
                    mapping.aliasMappings[i] = rootAttributeIndex; // 在aliasMappings記錄根註解元素屬性下標
                }
            }
        }

        // 2.為各級註解中同一註解內互為別名的字段,以及根註解中不存在的、且不同註解間互為別名的字段建立鏡像映射關係表MirrorSet
        mapping.mirrorSets.updateFrom(aliases);
        mapping.claimedAliases.addAll(aliases);


        // 3.根據MirrorSet,構建各級註解中被作為別名屬性的屬性,與調用時實際對應的註解屬性及子類註解實例的映射表annotationValueMappings和annotationValueSource
        if (mapping.annotation != null) {
            int[] resolvedMirrors = mapping.mirrorSets.resolve(null, mapping.annotation, ReflectionUtils::invokeMethod); 
            for (int i = 0; i < mapping.attributes.size(); i++) {
                if (aliases.contains(mapping.attributes.get(i))) {
                    this.annotationValueMappings[attributeIndex] = resolvedMirrors[i];
                    this.annotationValueSource[attributeIndex] = mapping;
                }
            }
        }
        mapping = mapping.source; // 向子註解遞歸
    }
}

private int getFirstRootAttributeIndex(Collection<Method> aliases) {
    // 獲取根註解的屬性,若根註解中存在以aliases任意屬性作為別名的屬性,則返回跟註解的屬性方法下標
    AttributeMethods rootAttributes = this.root.getAttributes();
    for (int i = 0; i < rootAttributes.size(); i++) {
        if (aliases.contains(rootAttributes.get(i))) {
            return i;
        }
    }
    return -1;
}

第一部分沒什麼好說的,第二與第三部分存在嵌套的邏輯,因此將在下一小結詳細介紹。

4、構建同註解內屬性與別名的顯式映射關係

承接上文,在 processAliases 中關於根註解中不存在的屬性的映射,在元註解的邏輯中是基於 MirrorSet 實現的,同一個註解類中不同屬性的映射關係的構建過程實際上就是構建 MirrorSet 的過程:

private void processAliases(int attributeIndex, List<Method> aliases) {
    // 從當前元註解向子註解遞歸
    AnnotationTypeMapping mapping = this;
    while (mapping != null) {
        // 1.若根註解——即最小的子註解——存在以元註解屬性作為別名的原始屬性,則以根註解屬性覆蓋元註解中的屬性,並在該元註解的成員變量`aliasMappings` 中記錄根註解原始屬性的下標;
        // ... ... 

        // 構建 MirrorSet
        mapping.mirrorSets.updateFrom(aliases);
        mapping.claimedAliases.addAll(aliases);

        // 3.根據MirrorSet,構建各級註解中被作為別名屬性的屬性,與調用時實際對應的註解屬性及子類註解實例的映射表annotationValueMappings和annotationValueSource
        // ... ...
        mapping = mapping.source; // 向子註解遞歸
    }
}

由於 AnnotationTypeMapping 本身在初始化時也一併初始化了一個 MirrorSets 實例用於管理 MirrorSet,因此在代碼中直接調用updateFrom 即可:

// code in MirrorSets
void updateFrom(Collection<Method> aliases) {
    MirrorSet mirrorSet = null;
    int size = 0;
    int last = -1;
    
    // 遍歷當前元註解的全部屬性
    for (int i = 0; i < attributes.size(); i++) {
        Method attribute = attributes.get(i);
        // 若當前元註解的屬性有被作為別名,則在MirrorSets.assigned數組中與當前屬性方法相同下標的位置設置一個MirrorSet實例
        if (aliases.contains(attribute)) {
            size++;
            if (size > 1) {
                if (mirrorSet == null) {
                    mirrorSet = new MirrorSet();
                    this.assigned[last] = mirrorSet;
                }
                this.assigned[i] = mirrorSet; 
            }
            last = i;
        }
    }
    
    // 參數mirrorSet,並更新集合
    if (mirrorSet != null) {
        mirrorSet.update();
        Set<MirrorSet> unique = new LinkedHashSet<>(Arrays.asList(this.assigned));
        unique.remove(null);
        this.mirrorSets = unique.toArray(EMPTY_MIRROR_SETS);
    }
}

updateFrom的邏輯簡單的概況一下,就是遍歷所有的元註解對應的AnnotationTypeMapping,然後如果每個註解中存在作為別名的屬性,則在 “AnnotationTypeMapping.MirrorSets.assigned數組中與該屬性方法對應的數組下標處設置一個MirrorSet` 實例,表示該屬性是別名屬性。

然後再調用 MirrorSet#update 方法:

void update() {
    this.size = 0;
    Arrays.fill(this.indexes, -1);
    for (int i = 0; i < MirrorSets.this.assigned.length; i++) {
        if (MirrorSets.this.assigned[i] == this) {
            this.indexes[this.size] = i;
            this.size++;
        }
    }
}

可見 MirrorSet 會記錄該實例在 assigned 數組中出現的位置,然後將其記錄在 indexes 數組中,由於 assigned 數組與 AttributeMethod 中屬性方法數組一一對應,因此 indexes 數組實際就是相同註解中互為別名的屬性,互為別名的屬性通過同一個 MirrorSet 實例綁定在了一起

image-20220617104800021

5、構建不同註解下屬性與別名的顯式映射關係

仍然承接上文,繼續解析 processAliases 的第三部分邏輯,這裡實際上還是分為兩部分:

  • 第一部分:從同一註解中互為別名的屬性裏面選出一個最終有效的屬性,作為他們實際屬性值的來源;
  • 第二部分:處理不同註解中互為別名的屬性,為其設置好對應的數據源與數據源中原始屬性的下標

源碼如下:

private void processAliases(int attributeIndex, List<Method> aliases) {
    // 1.若根註解——即最小的子註解——存在以元註解屬性作為別名的原始屬性,則以根註解屬性覆蓋元註解中的屬性,並在該元註解的成員變量`aliasMappings` 中記錄根註解原始屬性的下標;
    // ... ... 

    // 2.為各級註解中同一註解內互為別名的字段,以及根註解中不存在的、且不同註解間互為別名的字段建立鏡像映射關係表MirrorSet
    // ... ...


    // 3.根據MirrorSet,構建各級註解中被作為別名屬性的屬性,與調用時實際對應的註解屬性及子類註解實例的映射表annotationValueMappings和annotationValueSource
    if (mapping.annotation != null) {
        int[] resolvedMirrors = mapping.mirrorSets.resolve(null, mapping.annotation, ReflectionUtils::invokeMethod); 
        for (int i = 0; i < mapping.attributes.size(); i++) {
            if (aliases.contains(mapping.attributes.get(i))) {
                this.annotationValueMappings[attributeIndex] = resolvedMirrors[i];
                this.annotationValueSource[attributeIndex] = mapping;
            }
        }
    }
    mapping = mapping.source; // 向子註解遞歸
}

處理同註解內互為別名的字段

這一步主要依靠 resolve 方法完成,先調用了 MirrorSetsresolve

int[] resolve(@Nullable Object source, @Nullable Object annotation, ValueExtractor valueExtractor) {
    // 獲取當前註解的各屬性下標,默認每個屬性都從自身取值
    int[] result = new int[attributes.size()];
    for (int i = 0; i < result.length; i++) {
        result[i] = i;
    }

    // 遍歷所有MirrorSet實例
    for (int i = 0; i < size(); i++) {
        MirrorSet mirrorSet = get(i);
         // 從同一個註解中一堆互為別名的屬性中獲取一個最終有效的屬性的方法下標,然後所有的屬性都以這個屬性的值為準
        int resolved = mirrorSet.resolve(source, annotation, valueExtractor);
        for (int j = 0; j < mirrorSet.size; j++) {
            result[mirrorSet.indexes[j]] = resolved;
        }
    }
    return result;
}

// 1.2.2.1 獲取別名映射 in MirrorSets
Method get(int index) {
    int attributeIndex = this.indexes[index];
    return attributes.get(attributeIndex);
}

然後調用 MirrorSetresolve

<A> int resolve(@Nullable Object source, @Nullable A annotation, ValueExtractor valueExtractor) {
    int result = -1;
    Object lastValue = null; // 最近一個的有效屬性值
    
    // 遍歷與當前註解屬性屬性互為別名的全部屬性
    for (int i = 0; i < this.size; i++) {
        // 獲取屬性值
        Method attribute = attributes.get(this.indexes[i]);
        Object value = valueExtractor.extract(attribute, annotation);
        boolean isDefaultValue = (value == null ||
                                  isEquivalentToDefaultValue(attribute, value, valueExtractor));
        
        // 如果屬性值是默認值,或者與最後有效值相同,則記錄該屬性下標後返回
        // 以此類推,如果一組互為別名的屬性全部都是默認值,則前面的屬性——即離根註解最近的——的默認值會作為最終有效值
        if (isDefaultValue || ObjectUtils.nullSafeEquals(lastValue, value)) {
            if (result == -1) {
                result = this.indexes[i];
            }
            continue;
        }
        
        // 如果屬性值不是默認值,並且與最近一個的有效屬性值不同, 則拋出異常
        // 這裡實際要求一組互為別名的屬性中,只允許一個屬性的值是非默認值
        if (lastValue != null && !ObjectUtils.nullSafeEquals(lastValue, value)) {
            String on = (source != null) ? " declared on " + source : "";
            throw new AnnotationConfigurationException(String.format(
                "Different @AliasFor mirror values for annotation [%s]%s; attribute '%s' " +
                "and its alias '%s' are declared with values of [%s] and [%s].",
                getAnnotationType().getName(), on,
                attributes.get(result).getName(),
                attribute.getName(),
                ObjectUtils.nullSafeToString(lastValue),
                ObjectUtils.nullSafeToString(value)));
        }
        result = this.indexes[i];
        lastValue = value;
    }
    return result;
}

這裡的邏輯應該是比較清晰的,首先,如果同一個註解內存在多個互為別名的屬性,則需要有一個唯一有效的最終屬性,所有互為別名的屬性應當以這個最終屬性的值為準。

對應到代碼中,則就是通過遍歷 MirrorSet 中互為別名的字段,然後根據下述規則找到最終屬性:

  • 如果所有屬性都只有默認值,則離根註解最近的屬性最為最終屬性;
  • 如果所有屬性中存在屬性有非默認值,則該屬性就作為默認屬性,若出現多個有非默認值的屬性,則直接報錯;

然後返回這個最終屬性的下標。

MirrorSets 中的全部 MirrorSet 按上述過程處理後,我們會得到這個註解中每個屬性的最終屬性,對應到代碼:

int[] resolvedMirrors = mapping.mirrorSets.resolve(null, mapping.annotation, ReflectionUtils::invokeMethod); 

實際上得到的 resolvedMirrors 就是與註解中屬性方法對應的最終屬性集合。

我們舉個例子,假如現在有 A,B,C,D,E 五個屬性,其中 A 和 B、C 和 D 互為別名,則經過 MirrorSets#resolve 方法最終得到的 resolvedMirrors 如下圖:

image-20220618155125929

resolvedMirrors翻譯一下,就是 A 和 B 取值時都取 A 的值,C 和 D 取值時都取 C 的值,而 E 取值照樣取 E 的值。

處理不同註解中互為別名的屬性

理解了 resolvedMirrors 是個什麼玩意後,我們繼續回到 processAliases 方法的代碼:

// 獲取當前元註解中互為別名的屬性對應的實際取值的最終屬性
// aliases表示從元註解到子註解中所有跨註解互為別名的屬性
int[] resolvedMirrors = mapping.mirrorSets.resolve(null, mapping.annotation, ReflectionUtils::invokeMethod); 
for (int i = 0; i < mapping.attributes.size(); i++) {
    // 若當前註解中存在別名屬性 i
    if (aliases.contains(mapping.attributes.get(i))) {
        // 則該屬性取值時,從annotationValueSource[i]獲取目標註解,然後再從目標註解的resolvedMirrors[i]屬性獲取對應的值
        this.annotationValueMappings[attributeIndex] = resolvedMirrors[i];
        this.annotationValueSource[attributeIndex] = mapping;
    }
}

這裡的邏輯也很清晰,假如子註解 @A 的 x 屬性是其元註解 @B 的屬性 y 的別名時,當我們獲取 @B.y(),則實際到的是 @A.x()

回到代碼中,為了實現這個效果,這裡通過下標 i 表示別名屬性,然後再在 annotationValueSource[i] 記錄的實際取值的註解實例,接着又在 annotationValueMappings[i] 記錄了要從實際取值的註解實例中的那個屬性取值。

舉個例子,假如現在有註解 @B 和其元註解 @A@A 的屬性 X 是元註解 @B 屬性 C 的別名,則從 @A 開始解析後, 最終我們可以在 A.class 中得到下圖結果:

image-20220618160932095

由於 @A.X() 存在跨註解別名 @B.C(),則 X 的對應 AttributeMethods[0] ,則其下標0 對應的 annotationValueSource[0] 存放的就是 @B 的實例,然後 annotationValueMappings[0] 存放的就是 @B.C()@B 中的方法索引 1

6.構建不同註解下同名屬性的隱式映射關係

現在,通過 annotationValueMappingsannotationValueSource以及 AttributeMethods這三個成員變量,任何一個使用@AlisaFor 註解配置了別名的屬性都可以找到真正對應的值。

使用 @AlisaFor 註解在 spring 中稱為顯式別名,對應的還有一個隱式別名,也就是只要子註解和元註解的屬性名稱相同,則就會使用子註解的屬性值覆蓋元註解的屬性值,即子註解的屬性會強製作為元註解屬性的別名。

這個隱式映射的優先級高於顯式映射,換而言之,如果你在子註解為一個元註解通過@AlisaFor 指定了顯式別名,但是偏偏子註解中海油一個屬性與這個元註解中的屬性同名,則最終取值時,優先取子註解中的同名字段,而不是通過 @AlisaFor 指定的別名字段。

AnnotationTypeMapping 的構造函數中,共分為兩步:

// 為元註解與根註解同名的屬性強制設置別名
addConventionMappings();
// 為元註解與非根註解的子註解的同名的屬性設置別名
addConventionAnnotationValues();

為元註解與根註解同名的屬性強制設置別名

這一步將遍歷當前註解中的屬性,然後判斷是否在根註解中存在同名屬性,若存則直接將 conventionMappings 中對應下標的位置設置為根註解對應屬性的下標。

private void addConventionMappings() {
    if (this.distance == 0) {
        return;
    }
    AttributeMethods rootAttributes = this.root.getAttributes();
    int[] mappings = this.conventionMappings;
    for (int i = 0; i < mappings.length; i++) {
        // 遍歷當前註解的屬性,判斷是否在根註解存在
        String name = this.attributes.get(i).getName();
        int mapped = rootAttributes.indexOf(name);

        // 若存在,並且該屬性不為「value」
        MirrorSet mirrors = getMirrorSets().getAssigned(i);
        if (!MergedAnnotation.VALUE.equals(name) && mapped != -1) {
            mappings[i] = mapped;
            // 若該屬性還有別名,則讓該屬性和全部別名屬性都從根註解取值
            if (mirrors != null) {
                for (int j = 0; j < mirrors.size(); j++) {
                    mappings[mirrors.getAttributeIndex(j)] = mapped;
                }
            }
        }
    }
}

為元註解與非根註解的子註解的同名的屬性設置別名

這一步將從當前註解向不包括根註解在內的子註解遞歸:

  • 若自註解中存在同名字段,則將與當前屬性對應位置的 annotationValueSourceannotationValueMappings 設置為該子註解和該註解中同名屬性的方法下標;
  • 若子註解的子註解中仍然存在同名註解,則選擇一個離根註解最近的子註解,重複上述過程;
  • 重複上述兩步直到全部子註解遞歸完畢;
private void addConventionAnnotationValues() {
    // 遍歷當前註解的全部屬性
    for (int i = 0; i < this.attributes.size(); i++) {
        Method attribute = this.attributes.get(i);
        boolean isValueAttribute = MergedAnnotation.VALUE.equals(attribute.getName());
        AnnotationTypeMapping mapping = this;
        // 從當前註解向非根註解的子註解遞歸
        while (mapping != null && mapping.distance > 0) {
            // 若當前方法在子註解中存在,則將annotationValueMappings和annotationValueSource替換為該子註解和子註解的屬性
            // 由於替換前會比較annotationValueSource中註解距離根註解的距離,
            // 所以之前設置的根註解屬性不受影響,因為跟註解距離為0,優先級總是最高的
            int mapped = mapping.getAttributes().indexOf(attribute.getName());
            if (mapped != -1 && isBetterConventionAnnotationValue(i, isValueAttribute, mapping)) {
                this.annotationValueMappings[i] = mapped;
                this.annotationValueSource[i] = mapping;
            }
            mapping = mapping.source;
        }
    }
}

private boolean isBetterConventionAnnotationValue(int index, boolean isValueAttribute,
                                                  AnnotationTypeMapping mapping) {

    if (this.annotationValueMappings[index] == -1) {
        return true;
    }
    int existingDistance = this.annotationValueSource[index].distance;
    return !isValueAttribute && existingDistance > mapping.distance;
}

設置當前註解的可合成標記

這一步很簡單,就是判斷當前註解是否可以被用於合成 MergedAnnotation ,依據是三點:

  • 當前註解是否存在別名字段;
  • 當前註解是否是元註解,並且子類註解中有屬性是當前註解屬性的別名;
  • 如果當前註解的屬性中有為註解類型的屬性,那麼這個屬性對應的類型是否符合上述兩點;
private boolean computeSynthesizableFlag() {
    // 是否有屬性存在@AlisaFor註解
    for (int index : this.aliasMappings) {
        if (index != -1) {
            return true;
        }
    }
    if (!this.aliasedBy.isEmpty()) {
        return true;
    }

    // 是否有屬性被子註解的別名覆蓋
    for (int index : this.conventionMappings) {
        if (index != -1) {
            return true;
        }
    }

    // 是否存在註解類型的屬性字段
    if (getAttributes().hasNestedAnnotation()) {
        AttributeMethods attributeMethods = getAttributes();
        for (int i = 0; i < attributeMethods.size(); i++) {
            Method method = attributeMethods.get(i);
            Class<?> type = method.getReturnType();
            if (type.isAnnotation() || (type.isArray() && type.getComponentType().isAnnotation())) {
                Class<? extends Annotation> annotationType =
                    (Class<? extends Annotation>) (type.isAnnotation() ? type : type.getComponentType());
                AnnotationTypeMapping mapping = AnnotationTypeMappings.forAnnotationType(annotationType).get(0);
                if (mapping.isSynthesizable()) {
                    return true;
                }
            }
        }
    }

    return false;
}

小結

屬性映射實際上是在 AnnotationTypeMapping 被創建時完成的,這過程分為下述五個步驟:

  1. 先通過反射獲取當前註解的全部屬性方法,然後封裝為聚合屬性 AttributeMethods 對象,該對象獲取並通過下標來訪問屬性方法;
  2. 然後,AnnotationTypeMapping 將會遍歷 AttributeMethods 中的方法,若屬性方法上存在 @AliasFor 註解,則會解析註解,並通過反射獲取註解指定的類上的別名屬性對應的方法,並與當前註解中的對應屬性方法一併添加到名為 aliasBy 的 Map 集合中建立別名屬性和當前註解屬性的映射關係;
  3. 遍歷當前註解中已經註冊到 aliasBy 中的別名屬性,然後拿着這個屬性繼續向子註解遞歸,一直到將子類中直接或間接作為該屬性別名的屬性全部收集完畢;
  4. 拿着收集到的別名屬性,繼續從當前元註解項子註解遞歸,然後在處理每一層的註解時:
    • 同一註解中互為別名的屬性建立 MirrorSet,然後從中選擇出最後實際用於取值的最終屬性,MirrorSet 關聯的一組互為別名的屬性取值時都從該最終屬性獲取值;
    • 遍歷全部屬性,分別在 annotationValueSourceannotationValueMappings 中與該屬性在 AttributeMethods 中下標對應的位置,記錄要調用哪個註解實例和該要在註解實例中最終調用的屬性;
  5. 處理完 @AlisaFor 聲明的顯示別名後,將會為子註解與元註解中的同名屬性設置隱式別名:
    • 遍歷屬性,若元註解中存在與根註解同名的屬性,則將根註解中同名屬性的對應下標設置到 conventionMappings 中;
    • 遍歷屬性,將元註解中的 annotationValueSourceannotationValueMappings ,分別替換為存在同名屬性,且距離根註解最近的非根子註解與該子註解同名屬性的下標,;

五、通過動態代理生成註解

1、動態代理生成註解

這一步對應的 MergedAnnotation.synthesize() 方法,藉助 JDK 的動態代理根據 MergedAnnotation 生成對應的註解實例:

@Override
public Optional<A> synthesize(Predicate<? super MergedAnnotation<A>> condition)
    throws NoSuchElementException {

    return (condition.test(this) ? Optional.of(synthesize()) : Optional.empty());
}

@Override
public A synthesize() {
    if (!isPresent()) {
        throw new NoSuchElementException("Unable to synthesize missing annotation");
    }
    A synthesized = this.synthesizedAnnotation;
    if (synthesized == null) { // 只合成一次,後續合成都直接使用第一次的結果
        synthesized = createSynthesized();
        this.synthesizedAnnotation = synthesized;
    }
    return synthesized;
}

@Override
@SuppressWarnings("unchecked")
protected A createSynthesized() {
    // 如果查找的類型本身就已經是代理類了,就返回註解返回它本身
    if (getType().isInstance(this.rootAttributes) && !isSynthesizable()) {
        return (A) this.rootAttributes;
    }
    // 使用動態代理生成代理類
    return SynthesizedMergedAnnotationInvocationHandler.createProxy(this, getType());
}

static <A extends Annotation> A createProxy(MergedAnnotation<A> annotation, Class<A> type) {
    ClassLoader classLoader = type.getClassLoader();
    InvocationHandler handler = new SynthesizedMergedAnnotationInvocationHandler<>(annotation, type);
    // 為註解通過動態代理生成對象,生成的代理類實現SynthesizedAnnotation.class接口作為標識
    Class<?>[] interfaces = isVisible(classLoader, SynthesizedAnnotation.class) ?
        new Class<?>[] {type, SynthesizedAnnotation.class} : new Class<?>[] {type};
    return (A) Proxy.newProxyInstance(classLoader, interfaces, handler);
}

2、代理註解屬性值的獲取

方法代理

SynthesizedMergedAnnotationInvocationHandler 本身實現了 InvocationHandler,當調用動態代理生成的註解實例的屬性方法時,將會通過 invoke 方法獲得代理的屬性方法:

@Override
public Object invoke(Object proxy, Method method, Object[] args) {
    if (ReflectionUtils.isEqualsMethod(method)) { // 代理equals方法
        return annotationEquals(args[0]);
    }
    if (ReflectionUtils.isHashCodeMethod(method)) { // 代理hashCode方法
        return annotationHashCode();
    }
    if (ReflectionUtils.isToStringMethod(method)) { // 代理toString方法
        return annotationToString();
    }
    if (isAnnotationTypeMethod(method)) { // 代理annotationType方法
        return this.type;
    }
    if (this.attributes.indexOf(method.getName()) != -1) { // 獲取註解屬性
        return getAttributeValue(method);
    }
    throw new AnnotationConfigurationException(String.format(
        "Method [%s] is unsupported for synthesized annotation type [%s]", method, this.type));
}

這裡 SynthesizedMergedAnnotationInvocationHandler 分別提供了equalshashCodetoStringannotationType、註解的屬性方法等五個不同類的代理方法。

獲取屬性值

這裡我們重點關注代理類是如何通過 getAttributeValue 獲取註解的屬性的。

private Object getAttributeValue(Method method) {
    // 緩存屬性值
    Object value = this.valueCache.computeIfAbsent(method.getName(), attributeName -> {
        // 獲取方法返回值類型
        Class<?> type = ClassUtils.resolvePrimitiveIfNecessary(method.getReturnType());
        // 根據方法名與方法返回值確定要獲取的屬性
        return this.annotation.getValue(attributeName, type).orElseThrow(
            () -> new NoSuchElementException("No value found for attribute named '" + attributeName +
                                             "' in merged annotation " + this.annotation.getType().getName()));
    });

    // Clone non-empty arrays so that users cannot alter the contents of values in our cache.
    if (value.getClass().isArray() && Array.getLength(value) > 0) {
        value = cloneArray(value);
    }

    return value;
}

該方法實際調用的是 MergedAnnotationgetAttributeValue 方法,這裡我們以其實現類 TypeMappedAnnotation 為例:

@Override
@Nullable
protected <T> T getAttributeValue(String attributeName, Class<T> type) {
    int attributeIndex = getAttributeIndex(attributeName, false); // 1.通過屬性名獲取屬性在AttributeMethods中的下標
    return (attributeIndex != -1 ? getValue(attributeIndex, type) : null); // 2.根據下標獲取屬性值
}

@Nullable
private <T> T getValue(int attributeIndex, Class<T> type) {
    // 獲取屬性方法
    Method attribute = this.mapping.getAttributes().get(attributeIndex);
    // 調用屬性方法,並且允許子註解使用別名機制覆蓋元註解的屬性
    Object value = getValue(attributeIndex, true, false);
    if (value == null) {
        value = attribute.getDefaultValue();
    }
    return adapt(attribute, value, type); // 類型轉換
}

@Nullable
private Object getValue(int attributeIndex, boolean useConventionMapping, boolean forMirrorResolution) {
    AnnotationTypeMapping mapping = this.mapping; // 默認從當前註解開始獲取對應都屬性
    if (this.useMergedValues) {
        // 1.嘗試從根註解中獲取值
        // 1.1.a 若根註解中存在@AlisaFor顯式指定別名屬性,則獲取該屬性下標
        int mappedIndex = this.mapping.getAliasMapping(attributeIndex);
        if (mappedIndex == -1 && useConventionMapping) {
            // 1.1.b 若根註解中不存在@AlisaFor顯式指定別名屬性,則嘗試查找作為隱式別名的同名屬性的下標
            mappedIndex = this.mapping.getConventionMapping(attributeIndex);
        }
        // 1.2 若根註解中存在作為元註解別名的屬性,則從跟註解中獲取對應的屬性
        if (mappedIndex != -1) {
            mapping = mapping.getRoot();
            attributeIndex = mappedIndex;
        }
    }

    // 2.如果當前註解內存在互為別名的屬性,則從映射關係中獲取實際用於取值的最終屬性
    if (!forMirrorResolution) {
        attributeIndex =
            (mapping.getDistance() != 0 ? this.resolvedMirrors : this.resolvedRootMirrors)[attributeIndex];
    }

    // 3. 根據下標從註解中獲取屬性值
    if (attributeIndex == -1) {
        return null;
    }
    // 3.a 從根註解中取值
    if (mapping.getDistance() == 0) {
        Method attribute = mapping.getAttributes().get(attributeIndex);
        Object result = this.valueExtractor.extract(attribute, this.rootAttributes);
        return (result != null ? result : attribute.getDefaultValue());
    }
    // 3.b 從元註解中取值
    return getValueFromMetaAnnotation(attributeIndex, forMirrorResolution);
}

@Nullable
private Object getValueFromMetaAnnotation(int attributeIndex, boolean forMirrorResolution) {
    Object value = null;
    if (this.useMergedValues || forMirrorResolution) {
        // 根據該註解的屬性映射獲取值
        // 即從annotationValueSource獲取對應註解實例,然後再從annotationValueMappings中獲取該註解對應的屬性下標
        // 最終返回調用結果
        value = this.mapping.getMappedAnnotationValue(attributeIndex, forMirrorResolution);
    }
    // 如果根據屬性映射獲取到的值為null,再嘗試直接調用本身
    if (value == null) {
        Method attribute = this.mapping.getAttributes().get(attributeIndex);
        value = ReflectionUtils.invokeMethod(attribute, this.mapping.getAnnotation());
    }
    return value;
}

這邊的邏輯很清晰,即算上別名屬性的處理後共分為三步:

  • 先嘗試從根註解中獲取別名值,即通過 AnnotationTypeMapping.rootAnnotationTypeMapping.conventionMapping獲取;
  • 如果不成功,再嘗試通過常規的屬性映射獲取別名值,即 AnnotationTypeMapping.annotationValueSourceAnnotationTypeMapping.annotationValueMappings 獲取;
  • 如果還是不成功,就直接從當前註解本身獲取對應屬性值;

小結

當我們希望通過一個已經組裝好的 MergedAnnotation 中獲取在註解層級中存在的某個特定的註解時,spring 會通過 JDK 代理將該 MergedAnnotation 變為接口代理類實例,這個代理類實現了我們指定的註解對應的接口。

而當我們向正常的註解那樣去獲取註解的屬性時,實際上代理類會將方法改為通過 MergedAnnotation#getAttribute 實現,該實現基於構建 MergedAnnotationAnnotationTypeMapping 實例,它將根據屬性名獲取對應的屬性在 AttributeMethods 中的下標,然後根據下標以及之前解析得到的各種屬性映射關係,確定最終要調用哪個註解實例的哪個屬性方法,然後最終再返回改屬性值;

六、總結

回顧整個流程,當我們打算從某個元素上獲取 spring 所支持的元註解時,大體步驟如下:

  1. 從指定元素上解析直接聲明的註解,然後聚合為 MergedAnnotations,然後調用 MergedAnnotations#get方法,嘗試獲取一個 MergedAnnotation

  2. 此時,針對元素上的每一個根註解,都會按廣度優先掃描並解析註解和其元註解間的映射關係,並將每一個註解都封裝為 AnnotationTypeMapping

    並通過成員變量 rootsource 分別維護對子註解和根註解的引用;

  3. AnnotationTypeMapping 創建時,會先解析註解屬性與屬性間的映射關係,步驟包括:

    • 將註解中的屬性封裝為 AttributeMethods,此後註解中的屬性即對應AttributeMethods中的方法下標;

    • 解析帶有 @AlisaFor 註解的屬性,然後將收集在名為 alisaBy 的 Map 集合變量中,

      再解析 alisaBy 變量中每一個存在別名的方法,從元註解遞歸到根註解,獲得註解間存在直接或間接別名關係的屬性,

      同一註解內的屬性,通過 MirrorSet 建立屬性在AttributeMethods中對應下標的映射關係,全部互為別名的屬性最終聚合為 MirrorSets

      遍歷 MirrorSets 中的 MirrorSet,然後最終從每一組互為別名的屬性中,選擇出其中最終用於取值的最終屬性;

    • 在非根子註解中存在別名——包括@AlisaFor 指定或屬性名相同——的元註解屬性,通過 annotationValueSourceannotationValueMappings 數組在對應元註解屬性的下標處,記錄子註解與子註解指定的別名屬性下標;

    • 在根註解中存在對應同名屬性的元註解屬性,通過 conventionMappings 數組在對應元註解屬性的下標處,記錄根註解指定的別名屬性下標;

  4. 根註解與其元註解都解析為 AnnotationTypeMapping 後,以根註解為單位將 AnnotationTypeMapping 聚合為 AnnotationTypeMappings

  5. 根據要獲取的註解類型,從 AnnotationTypeMappings 中篩選出對應的一批 AnnotationTypeMapping,然後將其全部轉為 MergedAnnotation,再使用選擇器 MergedAnnotationSelector 從這一批 MergedAnnotation 選擇出最終——一般是離根註解最近的那個——的結果;

  6. 調用 MergedAnnotation#synthesize 方法,藉助動態代理,生成指定註解類型的代理實例,此時即獲取到了所需的「元註解」;

  7. 當從代理實例中獲取屬性值時,對應的方法會被代理到 MergedAnnotation#getAttributeValue 方法,該方法將根據屬性名從 MergedAnnotation 對應的 AnnotationTypeMappingAttributeMethods 得到指定屬性的下標,然後根據下標再從 AnnotationTypeMapping 中獲取真正的屬性值:

    • 若該屬性在根註解中存在,則通過下標取出conventionMappings 數組對應位置存放的根註解屬性下標,然後從根註解中獲取對應屬性值;
    • 若該屬性不做根註解中存在,則嘗試通過下標取出annotationValueSourceannotationValueMappings 數組存放的對應別名註解和要獲取的別名註解屬性下標,最後返回該別名註解中的對應別名屬性;
    • 若上述操作獲取到的屬性值為空,或該屬性不存在別名,則直接從該註解中獲取對應的屬性值;