前言
众所周知, 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.lang
、org.springframework.lang
包;JAVA
:类是否属于java
、javax
包;ALL
:任何类;
此处过滤器选择了 PLAIN
,即当查找的注解属于 java.lang
、 org.springframework.lang
包的时候就不进行查找,而是直接从被查找的元素直接声明的注解中获取。这个选择不难理解, java.lang
包下提供的都是诸如 @Resource
或者 @Target
这样的注解,而 springframework.lang
包下提供的则都是 @Nonnull
这样的注解,这些注解基本不可能作为有特殊业务意义的元注解使用,因此默认忽略也是合理的。
实际上, PLAIN
也是大部分情况下的使用的默认过滤器。
2、匹配元素
若要查找的注解不属于 java.lang
、 org.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.lang
、org.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.lang
、 org.springframework.lang
包的注解。
4、创建聚合注解
MergedAnnotations
本身实现了 Iterable
接口,用于表示一组处于聚合状态的 MergedAnnotation
,而 MergedAnnotation
就是对应我们实际上的合并注解,举个例子:
假如我们有个 Foo.class
,类上存在 @AnnotationA
与 @AnnotationB
两个注解,这两个注解又都有一大堆的元注解。此时 @AnnotationA
与 @AnnotationB
则各表示一个 MergedAnnotation
,而 MergedAnnotations
表示 Foo.class
上的两个 MergedAnnotation
。
MergedAnnotations
提供了四个比较重要的静态方法:
get stream isPresent from
TypeMappedAnnotations
则为该接口的主要实现类,这一步最终返回的就是一个 TypeMappedAnnotations
的实例。
小结
总结一下 findAnnotations
这一步操作,根本目的就是获得一个 TypeMappedAnnotations
实现,步骤如下:
- 配置重复注解容器:这里指定了一个
NONE