01-01.工具类-注解相关

AnnotationUtils

org.springframework.core.annotation.AnnotationUtils

findAnnotation与getAnnotation、getRepeatableAnnotations、findAnnotationDeclaringClass、findAnnotationDeclaringClassForTypes区别

getAnnotation只找本类的

findAnnotation 会查本类(包括重复注解(注解上加注解那种))、父接口、父类、父类的父接口(这个顺序也很重要,能保证你先找到本级的),参见570行,572行,577行,590行。 支持对class找注解(708行),也支持对method找注解,也对于泛型化时的bridge方法上的注解也做了良好支持,还支持对注解上加的注解进行查找(参见case findClassAnnotationOnMetaCycleAnnotatedClassWithMissingTargetMetaAnnotation),且测试了对循环注解进行查找 不错的case 注解3上有注解2 注解2上有注解1 注解1上有注解3…

findAnnotation查找顺序问题可以参见findClassAnnotationFavorsMoreLocallyDeclaredComposedAnnotationsOverAnnotationsOnInterfaces 这个case

getRepeatableAnnotations 这个能查出所有的,findAnnotation是找到一个即退出不找了。getRepeatableAnnotations能找到所有的,包括注解中的使用了目标注解字段的。具体可以参见下面的重复注解获取一节

findAnnotationDeclaringClass 支持查找出注解加在哪个class上,因为上面这些API支持往父层级搜索。

findAnnotationDeclaringClassForTypes支持查找多个注解中其中一个声明在哪个class上。

isAnnotationDeclaredLocally与isAnnotationInherited区别

isAnnotationDeclaredLocally 判断注解是否在当前class上。对于注解在父类或者父接口上但不在当前class上的,此方法返回false

isAnnotationInherited 判断注解是否在父类或者父接口上。对于注解在当前class上的,此方法返回false。具体可以参加case isAnnotationInheritedForAllScenarios

重复注解获取

方法getRepeatableAnnotations

重复注解的获取方法 送入 被注解的元素,比如method,送入需要查找的注解,需要查找的注解容器等,最后返回-需要查找的注解-的列表 例子参见AnnotationUtilsTests.InterfaceWithRepeated的foo方法

测试case getRepeatableAnnotationsDeclaredOnMethod

重复注解的重复包括:

  1. 注解上面有注解的,比如MyRepeatableMeta1
  2. 注解里面有注解的,比如MyRepeatableContainer
  3. java元素自身的注解,比如foo方法

例子可以参见 org.springframework.core.annotation.AnnotationUtilsTests.InterfaceWithRepeated的foo方法

interface InterfaceWithRepeated {

  // 对应上面情况3
1796		@MyRepeatable("A")
1797		@MyRepeatableContainer({@MyRepeatable("B"), @MyRepeatable("C")})
1798		@MyRepeatableMeta1
		void foo();
	}

// MyRepeatableMeta1对应上面重复情况1
@Retention(RetentionPolicy.RUNTIME)
	@Inherited
	@MyRepeatable("meta1")
	@interface MyRepeatableMeta1 {
	}

// MyRepeatableContainer对应上面重复情况2
@Retention(RetentionPolicy.RUNTIME)
	@Inherited
	@interface MyRepeatableContainer {

		MyRepeatable[] value();
	}

@Retention(RetentionPolicy.RUNTIME)
	@Inherited
	@Repeatable(MyRepeatableContainer.class)
	@interface MyRepeatable {

		String value();
	}

通过java.lang.reflect.AnnotatedElement的getDeclaredAnnotations方法能获取到的注解就是foo方法上加的3个,即1796-1798三行的三个注解;

synthesizeAnnotation的问题

synthesizeAnnotation 是复合注解的意思,怎样的算复合注解?

参见org.springframework.core.annotation.AnnotationUtils.isSynthesizable(Class<? extends Annotation>) 方法

大致是:

  1. 如果存在标志了AliasFor注解的属性,那么是synthesizable。下面会讲AliasFor。
  2. 如果注解的属性的get方法的返回值是Annotation数组(含其子类),那么是synthesizable
  3. 如果注解的属性的get方法的返回值是Annotation(含其子类),那么是synthesizable
关联的AnnotationUtils.getValue

org.springframework.core.annotation.AnnotationUtils.getValue(Annotation)

从容器类的注解中获取其内部注解,比如我们对org.springframework.core.annotation.AnnotationUtilsTests.MyRepeatableContainer获取其内部的MyRepeatable。

关联的AnnotationCollector分析

主要是getResult process 方法,不复杂

根据是否是寻找的annotationType和寻找的containerAnnotationType和其他类型进行区分查找。

如果是其他这种情况的,会把这个注解当成java element进行递归。

private void process(AnnotatedElement element) {
			if (this.visited.add(element)) {
				try {
					Annotation[] annotations = (this.declaredMode ? element.getDeclaredAnnotations() : element.getAnnotations());
					for (Annotation ann : annotations) {
						Class<? extends Annotation> currentAnnotationType = ann.annotationType();
						if (ObjectUtils.nullSafeEquals(this.annotationType, currentAnnotationType)) {
							this.result.add(synthesizeAnnotation((A) ann, element));
						}
						else if (ObjectUtils.nullSafeEquals(this.containerAnnotationType, currentAnnotationType)) {
							this.result.addAll(getValue(element, ann));
						}
						else if (!isInJavaLangAnnotationPackage(currentAnnotationType)) {
							process(currentAnnotationType); // 如果是其他这种情况的,会把这个注解当成java element进行递归。
						}
					}
				}
				catch (Throwable ex) {
					handleIntrospectionFailure(element, ex);
				}
			}
		}

bridge方法上的注解获取

参见org.springframework.core.annotation.AnnotationUtilsTests.SimpleFoo

和org.springframework.core.annotation.AnnotationUtilsTests.findMethodAnnotationOnBridgedMethod()和findMethodAnnotationOnBridgedMethod 这个case

SimpleFoo 重写了父类Foo的something方法,Foo的something方法是个类泛型方法,在子类SimpleFoo中指定了String作为其泛型化参数。代码如下:

public static abstract class Foo<T> {

		@Order(1)
		public abstract void something(T arg);
	}

	public static class SimpleFoo extends Foo<String> {

		@Override
		@Transactional
		public void something(final String arg) {
		}
	}

此时我们用这种方式:

SimpleFoo.class.getMethod("something", Object.class).getAnnotation(Order.class)

是不能拿到Order这个注解的,注意此时Object.class

用这种方式2

SimpleFoo.class.getMethod("something", String.class).getAnnotation(Order.class)

也是不能拿到Order这个注解的,注意此时String.class

在spring中用AnnotationUtils 的getAnnotation方式对上述两个method对象也是不能拿到Order这个注解。

怎么能拿到Order注解? spring的AnnotationUtils的findAnnotation对其做了支持,对方式1中的method(bridge方法)用AnnotationUtils.findAnnotation能拿到。

对于Transactional注解,不论是spring AnnotationUtils的getAnnotation还是findAnnotation,不论是方式1的method(bridge方法)还是方式2(bridged方法)的,都能拿到。

对于Transactional注解,用JDK的method对象的getAnnotation方法,对于方式2的method(bridged方法)可以拿到,对于方式1的method要看用哪个编译器,javac跟ejc的行为不一样。

有点绕,注意区分bridge和bridged方法,总结一下:

1. 对于父类泛型方法上的注解,用子类class的bridge方法(Object作为参数那种),通过spring的AnnotationUtils的findAnnotation能拿到;其他方式诸如JDK的method对象的getAnnotation都不行

2. 对于子类泛型方法上的注解,用JDK的method对象的getAnnotation方法针对bridged方法能拿到;用spring AnnotationUtils的getAnnotation还是findAnnotation对bridge和bridged方法(具体类型作为参数那种)都能拿到

注解属性的获取

该工具类对注解属性的获取做了封装 getAnnotationAttributes

将获取到的注解属性结果封装到AnnotationAttributes中,并提供了对一些Class类型的属性做了是否转成字符串的可选支持

获取的细节参见 retrieveAnnotationAttributes 和 adaptValue

AliasFor的支持

可以参见 org.springframework.core.annotation.AnnotationUtilsTests.ImplicitAliasesWithImpliedAliasNamesOmittedContextConfig

@AliasFor(annotation = ContextConfig.class, attribute = "location")
String xmlFile() default "";

在一个注解中定义一个属性,对另一个注解的属性做别名。有点软链接的意思。