在上一篇文章中,我们提到了 Spring 的四种注解编程模型:元注解、模式注解、组合注解、属性别名和覆盖。为了支持这四种注解编程模型,Spring 提供了 AnnotationUtils 和 AnnotatedElementUtils 工具类。注意,Spring-5.2.x 重构了这部分的 API,本文以 Spring-5.2.2 版本为基础进行分析。

  1. Spring 注解提供了那些功能?

    1. 功能说明

    Spring Annotation 位于 spring-core 工程中,是注解驱动的基石。其中最核心的类如下:
  • 核心组件**(API) - org.springframework.core.annotation._AnnotationUtils **_

    1. org.springframework.core.annotation._**AnnotatedElementUtils**_
  • 注解查找 - org.springframework.core.annotation.AnnotationsScanner

    • org.springframework.core.annotation.MergedAnnotations.SearchStrategy
    • org.springframework.core.annotation.AnnotationFilter
    • org.springframework.core.annotation.AnnotationsProcessor
    • org.springframework.core.annotation.RepeatableContainers
  • 注解解析**(属性别名和覆盖解析) - org.springframework.core.annotation.MergedAnnotation**
    • org.springframework.core.annotation.AnnotationTypeMapping
  • 注解代理
    • org.springframework.core.annotation.SynthesizedMergedAnnotationInvocationHandler

说明:AnnotationsScanner 将查找到的注解包装成 MergedAnnotation,并生成注解的 JDK 动态代理类。 动态代理 SynthesizedMergedAnnotationInvocationHandler 实际上最终是调用 MergedAnnotation.getValue 返回注解属性值。MergedAnnotation 会处理注解的别名及属性覆盖问题。

1.1 AnnotationUtils 和 AnnotationElementUtils

Spring 提供 AnnotationUtilsAnnotationElementUtils 用于简化操作。

// AnnotatedElementUtils public static A getMergedAnnotation(AnnotatedElement element, Class annotationType) { // Shortcut: directly present on the element, with no merging needed? if (AnnotationFilter.PLAIN.matches(annotationType) || AnnotationsScanner.hasPlainJavaAnnotationsOnly(element)) { return element.getDeclaredAnnotation(annotationType); } // Exhaustive retrieval of merged annotations… return getAnnotations(element) .get(annotationType, null, MergedAnnotationSelectors.firstDirectlyDeclared()) .synthesize(MergedAnnotation::isPresent).orElse(null); } private static MergedAnnotations getAnnotations(AnnotatedElement element) { return MergedAnnotations.from(element, SearchStrategy.INHERITED_ANNOTATIONS, RepeatableContainers.none()); }

  1. **说明:**这三类 API 元注解、查找策略、属性覆盖区别如下:
  2. - AnnotationUtils._getAnnotation_:元注解使用 MergedAnnotation::isSingleLevelPresent,即只查找直接注解和一级元注解。查找策略是 SearchStrategy.INHERITED_ANNOTATIONS,即 JDK 语义。注解属性覆盖使用 withNonMergedAttributes 方法,即不支持注解属性覆盖。
  3. - AnnotationUtils._findAnnotation_:元注解使用 MergedAnnotation::isPresent,即递归查找所有元注解。查找策略是 SearchStrategy.TYPE_HIERARCHY,即递归查找父类和接口。注解属性覆盖使用 withNonMergedAttributes 方法,即不支持注解属性覆盖。
  4. - AnnotatedElementUtils._getAnnotation_:元注解使用 MergedAnnotationSelectors.firstDirectlyDeclared(),即优先查找直接注解。查找策略是 SearchStrategy.INHERITED_ANNOTATIONS,即 JDK 语义。注解属性覆盖没有使用 withNonMergedAttributes 方法,即默认为注解属性覆盖。
  5. <a name="ugogn"></a>
  6. ## 1.2 处理逻辑
  7. 接下来,我们大致看一下,Spring 注解是如何实现继承查找、元注解查找、属性别名、属性覆盖等功能。
  8. <a name="kEuQ0"></a>
  9. ### 1.2.1 **查找策略**
  10. **(1)继承查找**<br />MergedAnnotations 内部定义了一个枚举类 SearchStrategy,表示查找注解的搜索策略。
  11. - **DIRECT**:仅查找直接声明的注释,而无需考虑 @Inherited 注释,也无需搜索超类或已实现的接口。
  12. - **INHERITED_ANNOTATIONS**:查找所有直接声明的注解以及任何 @Inherited 超类注解。 该策略仅在与 Class 类型一起使用时才真正有用,因为所有其他带注解的元素都将忽略 @Inherited 注释。 此策略不搜索已实现的接口。
  13. - **SUPERCLASS**:查找所有直接声明的注解和超类注解。 该策略与 INHERITED_ANNOTATIONS 相似,不同之处在于注解不需要使用 @Inherited 进行元注解。 此策略不搜索已实现的接口。
  14. - **TYPE_HIERARCHY**:对整个类型层次进行完整搜索,包括超类和已实现的接口。 超类注解不需要使用 @Inherited 进行元注解。
  15. - **TYPE_HIERARCHY_AND_ENCLOSING_CLASSES**:对来源和所有封闭的类执行整个类型层次结构的完整搜索。 该策略与 TYPE_HIERARCHY 相似,不同之处在于还搜索了封闭类。 超类注解不需要使用 @Inherited 进行元注解。 搜索方法源时,此策略与 TYPE_HIERARCHY 相同。
  16. **(2)过滤规则**<br />AnnotationFilter 用于过滤调用指定注解类型的接口,内部定义了 4 个常用的 AnnotationFilter 对象的静态变量:
  17. - **PLAIN**:与 java.lang org.springframework.lang 包及其子包中的注解匹配。
  18. - **JAVA**:与 java javax 包及其子包中的注解匹配。
  19. - **ALL**:始终匹配,可以在根本不存在任何相关注释类型时使用。
  20. - **NONE**:永远不匹配,可以在不需要过滤时使用(允许存在任何注释类型)。
  21. **(3)可重复注解**<br />RepeatableContainers 用于处理可重复注解,findRepeatedAnnotations 方法查找 container 注解中的可重复注解。
  22. - **NoRepeatableContainers**:没有可重复注解,findRepeatedAnnotations 方法始终返回 null
  23. - **StandardRepeatableContainers**:这是 JDK 中标准的 @Repeatable 可重复注解,findRepeatedAnnotations 方法返回 container.value()。
  24. - **ExplicitRepeatableContainer**:这是非标准的可重复注解,需要传递 container repeatable 两个注解类型参数。参数需要满足以下三个条件:一是 container 注解中有 vlaue 属性,二是 value 方法的返回值类型为数组,三是数组的类型为 repeatable 注解类型。也就是说 container 注解中除了 value 属性外还可以有其它属性,findRepeatedAnnotations 方法返回 container.value()。
  25. <a name="aFc3b"></a>
  26. ### 1.2.2 元注解
  27. - MergedAnnotation::isSingleLevelPresent:查找直接注解和一级元注解
  28. - MergedAnnotation::isPresent:递归查找所有注解
  29. <a name="NdSDN"></a>
  30. ### 1.2.3 属性**别名**
  31. AnnotationUtils AnnotationElementUtils 都支持注解别名解析。AnnotationTypeMapping.MirrorSets 解析属性别名,其 resolve 方法解析最终使用那个别名。TypeMappedAnnotation#getValue 获取属性别名的值时,调用 **MirrorSets#resolve** 方法解析多个别名,最终确定从那个别名中获取属性值。
  32. <a name="VW1wU"></a>
  33. ### 1.2.4 属性覆盖
  34. TypeMappedAnnotation#getValue 获取属性别名的值时,如果 **useMergedValues=true** 则会进行属性覆盖,否则直接从当前注解获取属性值。
  35. <a name="q54Rw"></a>
  36. # 2. 注解查找
  37. <a name="730Us"></a>
  38. ## 2.1 整体流程
  39. 注解查找时,会根据 SearchStrategyAnnotationFilterRepeatableContainers 三个规则进行查找。
  40. - _**MergedAnnotations**_:查找注解,返回 MergedAnnotation
  41. - TypeMappedAnnotations:具体实现,返回 TypeMappedAnnotation
  42. - **_AnnotationsScanner_**:根据查找策略 SearchStrategy 递归查找父类或接口。如果是类上的注解,则递归查找父类或接口上的注解。如果是方法上的注解,则递归查找父类或接口中重写方法上的注解。最后,查找到的注解由 AnnotationsProcessor 处理后返回。
  43. - AnnotationsProcessor:有 MergedAnnotationFinder IsPresentAggregatesCollector 三种实现,它们都会处理 RepeatableContainers 可重复注解。get 方法调用 MergedAnnotationFinderisDirectlyPresent 方法调用 IsPresentstream 方法调用 AggregatesCollector
  44. - SynthesizedMergedAnnotationInvocationHandler:代理对象,获取注解属性值实际委托给 MergedAnnotation.getValue 方法。也就是,最后属性别名和覆盖都是由 MergedAnnotation 进行解析。
  45. ![](https://cdn.nlark.com/yuque/__puml/ed592b766a3d5843e923fa1151631cc5.svg#lake_card_v2=eyJjb2RlIjoiQHN0YXJ0dW1sXG5hdXRvbnVtYmVyXG50aXRsZSDlm74x77ya5rOo6Kej5p-l5om-5pe25bqP5Zu-XG5cbnBhcnRpY2lwYW50IEFubm90YXRpb25VdGlsc1xucGFydGljaXBhbnQgTWVyZ2VkQW5ub3RhdGlvbnNcbnBhcnRpY2lwYW50IEFubm90YXRpb25zU2Nhbm5lclxucGFydGljaXBhbnQgTWVyZ2VkQW5ub3RhdGlvblxucGFydGljaXBhbnQgU3ludGhlc2l6ZWQuLi5JbnZvY2F0aW9uSGFuZGxlciAvJ1N5bnRoZXNpemVkTWVyZ2VkQW5ub3RhdGlvbkludm9jYXRpb25IYW5kbGVyJy9cblxuQW5ub3RhdGlvblV0aWxzIC0-IEFubm90YXRpb25VdGlsczogZ2V0QW5ub3RhdGlvblxuYWN0aXZhdGUgQW5ub3RhdGlvblV0aWxzXG5Bbm5vdGF0aW9uVXRpbHMgLT4gTWVyZ2VkQW5ub3RhdGlvbnM6IGdldFxuYWN0aXZhdGUgTWVyZ2VkQW5ub3RhdGlvbnNcbk1lcmdlZEFubm90YXRpb25zIC0-IEFubm90YXRpb25zU2Nhbm5lcjogc2NhblxuTWVyZ2VkQW5ub3RhdGlvbnMgLS0-IEFubm90YXRpb25VdGlsczogcmV0dXJuIE1lcmdlZEFubm90YXRpb25cbmRlYWN0aXZhdGUgTWVyZ2VkQW5ub3RhdGlvbnNcblxuQW5ub3RhdGlvblV0aWxzIC0-IE1lcmdlZEFubm90YXRpb246IHN5bnRoZXNpemUoKSDliqjmgIHku6PnkIbvvIzop6PmnpDlsZ7mgKfliKvlkI1cbk1lcmdlZEFubm90YXRpb24gLT4gU3ludGhlc2l6ZWQuLi5JbnZvY2F0aW9uSGFuZGxlcjogY3JlYXRlUHJveHlcbmRlYWN0aXZhdGUgQW5ub3RhdGlvblV0aWxzXG5cblxuXG5AZW5kdW1sIiwidHlwZSI6InB1bWwiLCJtYXJnaW4iOnRydWUsImlkIjoiNHNwc0giLCJ1cmwiOiJodHRwczovL2Nkbi5ubGFyay5jb20veXVxdWUvX19wdW1sL2VkNTkyYjc2NmEzZDU4NDNlOTIzZmExMTUxNjMxY2M1LnN2ZyIsImhlaWdodCI6MTc5LCJjYXJkIjoiZGlhZ3JhbSJ9)**说明:**注解查找可心大致为注解查找和代理生成两个过程。
  46. 1. 查找过程:MergedAnnotations 调用 AnnotationsScanner.scan 方法查找,将查找到的注解包装成 MergedAnnotation
  47. 1. 代理过程:最终调用 JDK 动态代理注解。SynthesizedMergedAnnotationInvocationHandler 最终也是调用 MergedAnnotation.getValue 处理注解属性别名和覆盖。
  48. <a name="PhVDN"></a>
  49. ## 2.2 AnnotationsScanner
  50. 我们从 AnnotationsScanner.scan 方法入手,分析注解的查找过程。涉及以下三个类:
  51. - _**AnnotationsScanner**_:分别处理 _processClassprocessMethodprocessElement_ 三种场景__对于 _processClassprocessMethod _二种场景会根据查找策略 SearchStrategy 递归查找父类或接口,代码会复杂一些。这里我们重点分析 _processElement_ 方法。
  52. - _**MergedAnnotationFinder**_:处理查找到的 annotations 注解。
  53. - doWithAggregate 方法:相当于快速查找,直接返回上次缓存的结果。
  54. - doWithAnnotations 方法:则用于处理 annotations 注解,如果 MergedAnnotationSelector 有最佳的结果,比如,查找到的是根注解(distance=0),则无需递归查找元注解,直接返回,否则更新上一次的查找结果。
  55. - finish 方法:没有最佳的结果,则返回最后一新更新的结果。
  56. - MergedAnnotationSelector:从多个结果中 pk 出一个最佳的结果返回,有 Nearest FirstDirectlyDeclared 二种实现。isBestCandidate 方法返回 true 则说明是最佳结果,直接返回即可;select 方法则将两个结果进行 pk
  57. ![](https://cdn.nlark.com/yuque/__puml/2e7daf24bab2fb38904aedc27df4f1b8.svg#lake_card_v2=eyJjb2RlIjoiQHN0YXJ0dW1sXG5hdXRvbnVtYmVyXG50aXRsZSDlm74y77yaQW5ub3RhdGlvbnNTY2FubmVyLnNjYW7ms6jop6Pmn6Xmib7ml7bluo_lm75cblxucGFydGljaXBhbnQgTWVyZ2VkQW5ub3RhdGlvbnNcbnBhcnRpY2lwYW50IEFubm90YXRpb25zU2Nhbm5lclxucGFydGljaXBhbnQgTWVyZ2VkQW5ub3RhdGlvbkZpbmRlclxucGFydGljaXBhbnQgTWVyZ2VkQW5ub3RhdGlvblNlbGVjdG9yXG5cbk1lcmdlZEFubm90YXRpb25zIC0-IEFubm90YXRpb25zU2Nhbm5lcjogc2NhblxuYWN0aXZhdGUgQW5ub3RhdGlvbnNTY2FubmVyXG5Bbm5vdGF0aW9uc1NjYW5uZXIgLT4gQW5ub3RhdGlvbnNTY2FubmVyOiBwcm9jZXNzQ2xhc3Mg6YCS5b2S6Kej5p6Q54i257G75oiW5o6l5Y-j5LiK55qEXG5Bbm5vdGF0aW9uc1NjYW5uZXIgLT4gQW5ub3RhdGlvbnNTY2FubmVyOiBwcm9jZXNzTWV0aG9kIOmAkuW9kuino-aekOeItuexu-aIluaOpeWPo-S4reeahOmHjeWGmeaWueazlVxuQW5ub3RhdGlvbnNTY2FubmVyIC0-IEFubm90YXRpb25zU2Nhbm5lcjogcHJvY2Vzc0VsZW1lbnQg5p-l5om-55u05o6l5rOo6KejXG5hY3RpdmF0ZSBBbm5vdGF0aW9uc1NjYW5uZXJcbkFubm90YXRpb25zU2Nhbm5lciAtPiBNZXJnZWRBbm5vdGF0aW9uRmluZGVyOiBkb1dpdGhBZ2dyZWdhdGVcbkFubm90YXRpb25zU2Nhbm5lciAtPiA6IGdldERlY2xhcmVkQW5ub3RhdGlvbnNcbkFubm90YXRpb25zU2Nhbm5lciAtPiBNZXJnZWRBbm5vdGF0aW9uRmluZGVyOiBkb1dpdGhBbm5vdGF0aW9uc1xuYWN0aXZhdGUgTWVyZ2VkQW5ub3RhdGlvbkZpbmRlclxuTWVyZ2VkQW5ub3RhdGlvbkZpbmRlciAtPiBNZXJnZWRBbm5vdGF0aW9uU2VsZWN0b3I6IGlzQmVzdENhbmRpZGF0ZVxuTWVyZ2VkQW5ub3RhdGlvbkZpbmRlciAtPiBNZXJnZWRBbm5vdGF0aW9uU2VsZWN0b3I6IHNlbGVjdFxuZGVhY3RpdmF0ZSBNZXJnZWRBbm5vdGF0aW9uRmluZGVyXG5kZWFjdGl2YXRlIEFubm90YXRpb25zU2Nhbm5lclxuQW5ub3RhdGlvbnNTY2FubmVyIC0-IE1lcmdlZEFubm90YXRpb25GaW5kZXI6IGZpbmlzaFxuQW5ub3RhdGlvbnNTY2FubmVyIC0tPiBNZXJnZWRBbm5vdGF0aW9uczogcmV0cnVuIE1lcmdlZEFubm90YXRpb25cbmRlYWN0aXZhdGUgQW5ub3RhdGlvbnNTY2FubmVyXG5cbkBlbmR1bWwiLCJ0eXBlIjoicHVtbCIsIm1hcmdpbiI6dHJ1ZSwiaWQiOiI5UkFSNSIsInVybCI6Imh0dHBzOi8vY2RuLm5sYXJrLmNvbS95dXF1ZS9fX3B1bWwvMmU3ZGFmMjRiYWIyZmIzODkwNGFlZGMyN2RmNGYxYjguc3ZnIiwiaGVpZ2h0IjoyMTQsImNhcmQiOiJkaWFncmFtIn0=)**说明:**AnnotationsScanner._processElement_ 调用 _getDeclaredAnnotations_ 获取注解 annotations。MergedAnnotationFinder 处理查找到的 annotations 注解,返回一个最佳的结果。
  58. ```java
  59. // AnnotationsScanner
  60. static <C, R> R scan(C context, AnnotatedElement source, SearchStrategy searchStrategy,
  61. AnnotationsProcessor<C, R> processor, @Nullable BiPredicate<C, Class<?>> classFilter) {
  62. R result = process(context, source, searchStrategy, processor, classFilter);
  63. return processor.finish(result);
  64. }
  65. private static <C, R> R process(C context, AnnotatedElement source,
  66. SearchStrategy searchStrategy, AnnotationsProcessor<C, R> processor,
  67. @Nullable BiPredicate<C, Class<?>> classFilter) {
  68. if (source instanceof Class) { // 递归查找父类或接口上的注解
  69. return processClass(context, (Class<?>) source, searchStrategy, processor, classFilter);
  70. }
  71. if (source instanceof Method) { // 递归查找父类或接口中方法的注解
  72. return processMethod(context, (Method) source, searchStrategy, processor, classFilter);
  73. }
  74. return processElement(context, source, processor, classFilter); // 查找直接注解
  75. }
  76. private static <C, R> R processElement(C context, AnnotatedElement source,
  77. AnnotationsProcessor<C, R> processor, @Nullable BiPredicate<C, Class<?>> classFilter) {
  78. R result = processor.doWithAggregate(context, 0);
  79. return (result != null ? result : processor.doWithAnnotations(
  80. context, 0, source, getDeclaredAnnotations(context, source, classFilter, false)));
  81. }

3. 注解解析

Spring 注解驱动(06) - 图1Spring注解属性别名
Spring注解编程基石(一)