1 @Repeatable
jdk8之前不存在可重複註解,即一個註解不能出現在同一個元素(Class、Method、Constructor、Field)2次以上。下面的寫法是被禁止的:
// @ComponentScan重複使用
@ComponentScan(basePackages = "com.focuse.component1")
@ComponentScan(basePackages = "com.focuse.component2")
public class RepeatableTest {
}
如果想添加多個相同註解,只能用容器註解。容器註解提供了一個數組,來承載多個註解
@ComponentScans(value = { @ComponentScan(basePackages = "com.focuse.component1"),
@ComponentScan(basePackages = "com.focuse.component2") })
public class RepeatableTest {
}
jdk8引入了@Repeatable元註解。被@Repeatable修飾的註解即是可重複使用在同一個元素上的。在jdk8中@ComponentScan就被@Repeatable修飾了,這樣第一種寫法在jdk8中被允許的。
2 get[Declared]Annotation、get[Declared]Annotations、get[Declared]AnnotationsByType
java的反射包中獲取一個元素(Class、 Method、Constructor、Field)上的註解對象主要有這6個方法getAnnotation(Class<T> annotationClass)、getAnnotations()、getAnnotationsByType(Class<T> annotationClass)、getDeclaredAnnotation(Class<T> annotationClass)、getDeclaredAnnotations()、getDeclaredAnnotationsByType(Class<T> annotationClass)。這些方法獲取的都是運行時註解。
在介紹這6個方法前,先描述下幾個概念(翻譯自jdk的註釋):
- directly present:"直接修飾"註解是指元素的RuntimeVisibleAnnotations、RuntimeVisibleParameterAnnotations或RuntimeVisibleTypeAnnotations屬性包含的註解(RuntimeVisibleAnnotations、RuntimeVisibleParameterAnnotations、RuntimeVisibleTypeAnnotations是classfile結構的屬性),
說白了就是指直接修飾在某個元素上的註解; - indirectly present:"間接修飾"註解就是指得容器註解的數組中指定的註解;
- present:並不是"直接修飾"註解和"間接修飾"註解的合集,而是"直接修飾"註解和父類繼承下來的"直接註解"的合集;
- associated:"關聯"是"直接修飾"註解、"間接修飾"註解以及父類繼承下來的註解的合集;
理解了這幾個概念後,以上6個方法返回的註解對象可用下表來表示
方法 | directly present | indirectly present | present | associated |
getAnnotation | * | |||
getAnnotations | * | |||
getAnnotationsByType | * | |||
getDeclaredAnnotation | * | |||
getDeclaredAnnotations | * | |||
getDeclaredAnnotationsByType | * | * |
舉個例子getAnnotation返回的是"直接修飾"註解和繼承的註解的合集,不包括容器註解裏包含的註解;而getDeclaredAnnotation僅僅返回"直接修飾"註解。
3 @Repeatable對上述方法返回值的影響
如果一個註解是可重複的,當它在同一個元素E上出現2次以上時(注意出現1次的時候並不會默認生成容器註解),javac編譯以後,class文件裏面實際是該註解對應的容器註解修飾在元素E上,而容器註解中的value存儲多個註解的值。舉個例子:
a 自定義可重複的註解@RepeatAn
@RepeatAn的容器是@RepeatAns
package com.focuse.jdkdemo.annation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Target(ElementType.METHOD)
@Retention(RUNTIME)
@Repeatable(RepeatAns.class)
public @interface RepeatAn {
}
b 定義容器註解@RepeatAns
package com.focuse.jdkdemo.annation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Target(ElementType.METHOD)
@Retention(RUNTIME)
public @interface RepeatAns {
RepeatAn[] value();
}
c 使用註解@RepeatAn
package com.focuse.jdkdemo.annation;
import java.lang.reflect.Method;
/**
* @author :focuse
* @date :Created in 2020/2/16 上午11:10
* @description:
* @modified By:
*/
public class RepeatableTest<T extends String> {
@RepeatAn()
@RepeatAn()
public void method(String s) {
}
public static void main(String[] args) throws Exception{
Method method = RepeatableTest.class.getDeclaredMethod("method", String.class);
RepeatAn an = method.getAnnotation(RepeatAn.class);
System.out.println(an);
System.out.println("****************************************");
RepeatAns ans = method.getAnnotation(RepeatAns.class);
System.out.println(ans);
System.out.println("****************************************");
RepeatAn[] anArr = method.getAnnotationsByType(RepeatAn.class);
System.out.println(anArr);
}
}
運行結果
因爲可重複註解出現2次以上時,編譯以後相當於
@RepeatAns(value = {@RepeatAn(), @RepeatAn()})
所以:
- getAnnotation獲取的是"直接修飾"註解和繼承下來的註解,這其中沒有@RepeatAn所以爲null;
- 同理,getAnnotation(RepeatAns.class)卻可以獲取到;
- getAnnotationsByType(RepeatAn.class)獲取的是"關聯"註解(包括"間接"註解),所以獲取@RepeatAn的數組;
d 編譯之後的classfile文件
最後我們來看看編譯之後的classfile文件到底是什麼樣的,用命令"javap -c -v -l RepeatableTest.class"反編譯class文件得到如下信息
"method"方法最後有個屬性RuntimeVisibleAnnotations就是存放註解的,#24、#25、#26是類文件常量池中索引,具體含義如下
可以看到#24對應Lcom/focuse/jdkdemo/annation/RepeatAns即@RepeatAns,#25對應"value"字符串,#26對應@RepeatAn。n那麼RuntimeVisibleAnnotations中信息組合起來就是@RepeatAns(value=[@RepeatAn,@RepeatAn]),與例子中現象一致。
PS:注意可重複註解在同一個元素上只出現1次的時候並不會默認生成容器註解