在上一篇文章中,我们提到了 Spring 的四种注解编程模型:元注解、模式注解、组合注解、属性别名和覆盖。为了支持这四种注解编程模型,Spring 提供了 AnnotationUtils 和 AnnotatedElementUtils 工具类。注意,Spring-5.2.x 重构了这部分的 API,本文以 Spring-5.2.2 版本为基础进行分析。
核心组件**(API) - org.springframework.core.annotation._AnnotationUtils **_
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 提供 AnnotationUtils 和 AnnotationElementUtils 用于简化操作。
AnnotationUtils
:解决元注解查找、继承查找以及注解别名三大功能,但不能解决元注解属性覆盖的问题。其中注解别名包括显示别名、隐式别名、传递的隐式别名。AnnotationUtils 主要包含 getAnnotation 和 findAnnotation 两类 API,它们都会解析属性别名。- getAnnotation:基本上遵循 JDK 注解的语义。元注解最多只查找到一级元注解(包括直接注解和一级元注解),继承则只查找 @Inherited 注解。
- findAnnotation:扩展了 JDK 注解的语义。元注解递归查找所有的元注解,继承则递归查找父类和接口,包括类和方法上注解的继承,不必使用 @Inherited 注解。
AnnotatedElementUtils
:在 AnnotationUtils 的基础上,为 Spring 的元注解编程模型定义了公共 API,并支持注解属性覆盖。包括 getMergedAnnotation 和 findMergedAnnotation 两类 API,这两个方法的 get 和 find 语义基本上和 AnnotationUtils 类似,此外它们都支持注解属性覆盖。 ```java // AnnotationUtils public static A getAnnotation(AnnotatedElement annotatedElement, Class annotationType) { // Shortcut: directly present on the element, with no merging needed? if (AnnotationFilter.PLAIN.matches(annotationType) ||
} // Exhaustive retrieval of merged annotations… return MergedAnnotations.from(annotatedElement, SearchStrategy.INHERITED_ANNOTATIONS, RepeatableContainers.none())AnnotationsScanner.hasPlainJavaAnnotationsOnly(annotatedElement)) {
return annotatedElement.getAnnotation(annotationType);
} public static A findAnnotation(Method method, @Nullable Class annotationType) {.get(annotationType).withNonMergedAttributes()
.synthesize(AnnotationUtils::isSingleLevelPresent).orElse(null);
// Shortcut: directly present on the element, with no merging needed? if (AnnotationFilter.PLAIN.matches(annotationType) ||...
} // Exhaustive retrieval of merged annotations… return MergedAnnotations.from(method, SearchStrategy.TYPE_HIERARCHY, RepeatableContainers.none())AnnotationsScanner.hasPlainJavaAnnotationsOnly(method)) {
return method.getDeclaredAnnotation(annotationType);
}.get(annotationType).withNonMergedAttributes()
.synthesize(MergedAnnotation::isPresent).orElse(null);
// 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()); }
**说明:**这三类 API 元注解、查找策略、属性覆盖区别如下:
- AnnotationUtils._getAnnotation_:元注解使用 MergedAnnotation::isSingleLevelPresent,即只查找直接注解和一级元注解。查找策略是 SearchStrategy.INHERITED_ANNOTATIONS,即 JDK 语义。注解属性覆盖使用 withNonMergedAttributes 方法,即不支持注解属性覆盖。
- AnnotationUtils._findAnnotation_:元注解使用 MergedAnnotation::isPresent,即递归查找所有元注解。查找策略是 SearchStrategy.TYPE_HIERARCHY,即递归查找父类和接口。注解属性覆盖使用 withNonMergedAttributes 方法,即不支持注解属性覆盖。
- AnnotatedElementUtils._getAnnotation_:元注解使用 MergedAnnotationSelectors.firstDirectlyDeclared(),即优先查找直接注解。查找策略是 SearchStrategy.INHERITED_ANNOTATIONS,即 JDK 语义。注解属性覆盖没有使用 withNonMergedAttributes 方法,即默认为注解属性覆盖。
<a name="ugogn"></a>
## 1.2 处理逻辑
接下来,我们大致看一下,Spring 注解是如何实现继承查找、元注解查找、属性别名、属性覆盖等功能。
<a name="kEuQ0"></a>
### 1.2.1 **查找策略**
**(1)继承查找**<br />MergedAnnotations 内部定义了一个枚举类 SearchStrategy,表示查找注解的搜索策略。
- **DIRECT**:仅查找直接声明的注释,而无需考虑 @Inherited 注释,也无需搜索超类或已实现的接口。
- **INHERITED_ANNOTATIONS**:查找所有直接声明的注解以及任何 @Inherited 超类注解。 该策略仅在与 Class 类型一起使用时才真正有用,因为所有其他带注解的元素都将忽略 @Inherited 注释。 此策略不搜索已实现的接口。
- **SUPERCLASS**:查找所有直接声明的注解和超类注解。 该策略与 INHERITED_ANNOTATIONS 相似,不同之处在于注解不需要使用 @Inherited 进行元注解。 此策略不搜索已实现的接口。
- **TYPE_HIERARCHY**:对整个类型层次进行完整搜索,包括超类和已实现的接口。 超类注解不需要使用 @Inherited 进行元注解。
- **TYPE_HIERARCHY_AND_ENCLOSING_CLASSES**:对来源和所有封闭的类执行整个类型层次结构的完整搜索。 该策略与 TYPE_HIERARCHY 相似,不同之处在于还搜索了封闭类。 超类注解不需要使用 @Inherited 进行元注解。 搜索方法源时,此策略与 TYPE_HIERARCHY 相同。
**(2)过滤规则**<br />AnnotationFilter 用于过滤调用指定注解类型的接口,内部定义了 4 个常用的 AnnotationFilter 对象的静态变量:
- **PLAIN**:与 java.lang 和 org.springframework.lang 包及其子包中的注解匹配。
- **JAVA**:与 java 和 javax 包及其子包中的注解匹配。
- **ALL**:始终匹配,可以在根本不存在任何相关注释类型时使用。
- **NONE**:永远不匹配,可以在不需要过滤时使用(允许存在任何注释类型)。
**(3)可重复注解**<br />RepeatableContainers 用于处理可重复注解,findRepeatedAnnotations 方法查找 container 注解中的可重复注解。
- **NoRepeatableContainers**:没有可重复注解,findRepeatedAnnotations 方法始终返回 null。
- **StandardRepeatableContainers**:这是 JDK 中标准的 @Repeatable 可重复注解,findRepeatedAnnotations 方法返回 container.value()。
- **ExplicitRepeatableContainer**:这是非标准的可重复注解,需要传递 container 和 repeatable 两个注解类型参数。参数需要满足以下三个条件:一是 container 注解中有 vlaue 属性,二是 value 方法的返回值类型为数组,三是数组的类型为 repeatable 注解类型。也就是说 container 注解中除了 value 属性外还可以有其它属性,findRepeatedAnnotations 方法返回 container.value()。
<a name="aFc3b"></a>
### 1.2.2 元注解
- MergedAnnotation::isSingleLevelPresent:查找直接注解和一级元注解
- MergedAnnotation::isPresent:递归查找所有注解
<a name="NdSDN"></a>
### 1.2.3 属性**别名**
AnnotationUtils 和 AnnotationElementUtils 都支持注解别名解析。AnnotationTypeMapping.MirrorSets 解析属性别名,其 resolve 方法解析最终使用那个别名。TypeMappedAnnotation#getValue 获取属性别名的值时,调用 **MirrorSets#resolve** 方法解析多个别名,最终确定从那个别名中获取属性值。
<a name="VW1wU"></a>
### 1.2.4 属性覆盖
TypeMappedAnnotation#getValue 获取属性别名的值时,如果 **useMergedValues=true** 则会进行属性覆盖,否则直接从当前注解获取属性值。
<a name="q54Rw"></a>
# 2. 注解查找
<a name="730Us"></a>
## 2.1 整体流程
注解查找时,会根据 SearchStrategy、AnnotationFilter、RepeatableContainers 三个规则进行查找。
- _**MergedAnnotations**_:查找注解,返回 MergedAnnotation。
- TypeMappedAnnotations:具体实现,返回 TypeMappedAnnotation。
- **_AnnotationsScanner_**:根据查找策略 SearchStrategy 递归查找父类或接口。如果是类上的注解,则递归查找父类或接口上的注解。如果是方法上的注解,则递归查找父类或接口中重写方法上的注解。最后,查找到的注解由 AnnotationsProcessor 处理后返回。
- AnnotationsProcessor:有 MergedAnnotationFinder 、IsPresent、AggregatesCollector 三种实现,它们都会处理 RepeatableContainers 可重复注解。get 方法调用 MergedAnnotationFinder,isDirectlyPresent 方法调用 IsPresent,stream 方法调用 AggregatesCollector。
- SynthesizedMergedAnnotationInvocationHandler:代理对象,获取注解属性值实际委托给 MergedAnnotation.getValue 方法。也就是,最后属性别名和覆盖都是由 MergedAnnotation 进行解析。
![](https://cdn.nlark.com/yuque/__puml/ed592b766a3d5843e923fa1151631cc5.svg#lake_card_v2=eyJjb2RlIjoiQHN0YXJ0dW1sXG5hdXRvbnVtYmVyXG50aXRsZSDlm74x77ya5rOo6Kej5p-l5om-5pe25bqP5Zu-XG5cbnBhcnRpY2lwYW50IEFubm90YXRpb25VdGlsc1xucGFydGljaXBhbnQgTWVyZ2VkQW5ub3RhdGlvbnNcbnBhcnRpY2lwYW50IEFubm90YXRpb25zU2Nhbm5lclxucGFydGljaXBhbnQgTWVyZ2VkQW5ub3RhdGlvblxucGFydGljaXBhbnQgU3ludGhlc2l6ZWQuLi5JbnZvY2F0aW9uSGFuZGxlciAvJ1N5bnRoZXNpemVkTWVyZ2VkQW5ub3RhdGlvbkludm9jYXRpb25IYW5kbGVyJy9cblxuQW5ub3RhdGlvblV0aWxzIC0-IEFubm90YXRpb25VdGlsczogZ2V0QW5ub3RhdGlvblxuYWN0aXZhdGUgQW5ub3RhdGlvblV0aWxzXG5Bbm5vdGF0aW9uVXRpbHMgLT4gTWVyZ2VkQW5ub3RhdGlvbnM6IGdldFxuYWN0aXZhdGUgTWVyZ2VkQW5ub3RhdGlvbnNcbk1lcmdlZEFubm90YXRpb25zIC0-IEFubm90YXRpb25zU2Nhbm5lcjogc2NhblxuTWVyZ2VkQW5ub3RhdGlvbnMgLS0-IEFubm90YXRpb25VdGlsczogcmV0dXJuIE1lcmdlZEFubm90YXRpb25cbmRlYWN0aXZhdGUgTWVyZ2VkQW5ub3RhdGlvbnNcblxuQW5ub3RhdGlvblV0aWxzIC0-IE1lcmdlZEFubm90YXRpb246IHN5bnRoZXNpemUoKSDliqjmgIHku6PnkIbvvIzop6PmnpDlsZ7mgKfliKvlkI1cbk1lcmdlZEFubm90YXRpb24gLT4gU3ludGhlc2l6ZWQuLi5JbnZvY2F0aW9uSGFuZGxlcjogY3JlYXRlUHJveHlcbmRlYWN0aXZhdGUgQW5ub3RhdGlvblV0aWxzXG5cblxuXG5AZW5kdW1sIiwidHlwZSI6InB1bWwiLCJtYXJnaW4iOnRydWUsImlkIjoiNHNwc0giLCJ1cmwiOiJodHRwczovL2Nkbi5ubGFyay5jb20veXVxdWUvX19wdW1sL2VkNTkyYjc2NmEzZDU4NDNlOTIzZmExMTUxNjMxY2M1LnN2ZyIsImhlaWdodCI6MTc5LCJjYXJkIjoiZGlhZ3JhbSJ9)**说明:**注解查找可心大致为注解查找和代理生成两个过程。
1. 查找过程:MergedAnnotations 调用 AnnotationsScanner.scan 方法查找,将查找到的注解包装成 MergedAnnotation。
1. 代理过程:最终调用 JDK 动态代理注解。SynthesizedMergedAnnotationInvocationHandler 最终也是调用 MergedAnnotation.getValue 处理注解属性别名和覆盖。
<a name="PhVDN"></a>
## 2.2 AnnotationsScanner
我们从 AnnotationsScanner.scan 方法入手,分析注解的查找过程。涉及以下三个类:
- _**AnnotationsScanner**_:分别处理 _processClass、processMethod、processElement_ 三种场景_。_对于 _processClass、processMethod _二种场景会根据查找策略 SearchStrategy 递归查找父类或接口,代码会复杂一些。这里我们重点分析 _processElement_ 方法。
- _**MergedAnnotationFinder**_:处理查找到的 annotations 注解。
- doWithAggregate 方法:相当于快速查找,直接返回上次缓存的结果。
- doWithAnnotations 方法:则用于处理 annotations 注解,如果 MergedAnnotationSelector 有最佳的结果,比如,查找到的是根注解(distance=0),则无需递归查找元注解,直接返回,否则更新上一次的查找结果。
- finish 方法:没有最佳的结果,则返回最后一新更新的结果。
- MergedAnnotationSelector:从多个结果中 pk 出一个最佳的结果返回,有 Nearest 和 FirstDirectlyDeclared 二种实现。isBestCandidate 方法返回 true 则说明是最佳结果,直接返回即可;select 方法则将两个结果进行 pk。
![](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 注解,返回一个最佳的结果。
```java
// AnnotationsScanner
static <C, R> R scan(C context, AnnotatedElement source, SearchStrategy searchStrategy,
AnnotationsProcessor<C, R> processor, @Nullable BiPredicate<C, Class<?>> classFilter) {
R result = process(context, source, searchStrategy, processor, classFilter);
return processor.finish(result);
}
private static <C, R> R process(C context, AnnotatedElement source,
SearchStrategy searchStrategy, AnnotationsProcessor<C, R> processor,
@Nullable BiPredicate<C, Class<?>> classFilter) {
if (source instanceof Class) { // 递归查找父类或接口上的注解
return processClass(context, (Class<?>) source, searchStrategy, processor, classFilter);
}
if (source instanceof Method) { // 递归查找父类或接口中方法的注解
return processMethod(context, (Method) source, searchStrategy, processor, classFilter);
}
return processElement(context, source, processor, classFilter); // 查找直接注解
}
private static <C, R> R processElement(C context, AnnotatedElement source,
AnnotationsProcessor<C, R> processor, @Nullable BiPredicate<C, Class<?>> classFilter) {
R result = processor.doWithAggregate(context, 0);
return (result != null ? result : processor.doWithAnnotations(
context, 0, source, getDeclaredAnnotations(context, source, classFilter, false)));
}