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中被允许的。
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的注释):
理解了这几个概念后,以上6个方法返回的注解对象可用下表来表示
方法 | directly present | indirectly present | present | associated |
getAnnotation | * | |||
getAnnotations | * | |||
getAnnotationsByType | * | |||
getDeclaredAnnotation | * | |||
getDeclaredAnnotations | * | |||
getDeclaredAnnotationsByType | * | * |
举个例子getAnnotation返回的是"直接修饰"注解和继承的注解的合集,不包括容器注解里包含的注解;而getDeclaredAnnotation仅仅返回"直接修饰"注解。
如果一个注解是可重复的,当它在同一个元素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()})
所以:
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次的时候并不会默认生成容器注解