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
重复注解的重复包括:
- 注解上面有注解的,比如MyRepeatableMeta1
- 注解里面有注解的,比如MyRepeatableContainer
- 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>) 方法
大致是:
- 如果存在标志了AliasFor注解的属性,那么是synthesizable。下面会讲AliasFor。
- 如果注解的属性的get方法的返回值是Annotation数组(含其子类),那么是synthesizable
- 如果注解的属性的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 "";
在一个注解中定义一个属性,对另一个注解的属性做别名。有点软链接的意思。
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!