在上一篇文章中,我们提到了 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 进行解析。
**说明:**注解查找可心大致为注解查找和代理生成两个过程。
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。
**说明:**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)));
}