用Java快三年了,注解算是一个常用的类型,特别是在一些框架里面会大量使用注解做组件标识、配置或者策略。但是一直没有深入去探究JDK中的注解到底是什么,底层是怎么实现了?于是参考了一些资料,做了一次稍微详细的分析。
参考JavaSE-8里面的JLS-9.6对注解的描述如下:
注解的声明如下:
{InterfaceModifier} @ interface Identifier AnnotationTypeBody
接口修饰符 @ interface 注解标识符 注解类型的内容
其中:
既然所有注解类型的父接口都是java.lang.annotation.Annotation
,那么我们可以看一下Annotation接口的文档:
JavaSE-8中的文档对Annotation的描述和JLS-9.6中差不多,不过最后指明了可重复注解的兼容性考虑的问题,可重复注解在JDK1.8中由元注解@Repeatable实现。下面基于JDK8的最后一个版本java version 1.8.0_181
探究一下注解在JDK中的底层实现。
我们先定义一个十分简单的Counter注解如下:
package club.throwable.annotation;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.TYPE)
public @interface Counter {
int count() default 0;
}
我们先从直接使用@Counter
注解,从直观上观察@Counter
实例的类型:
@Counter(count = 1)
public class Main {
public static void main(String[] args) throws Exception{
Counter counter = Main.class.getAnnotation(Counter.class);
System.out.println(counter.count());
}
}
@Counter
实例从Debug过程中观察发现是JDK的一个代理类(并且InvocationHandler的实例是sun.reflect.annotation.AnnotationInvocationHandler,它是一个修饰符为default的sun包内可用的类),为了验证这一点我们使用JDK的反编译命令查看@Counter
的字节码:
javap -c -v D:\Projects\rxjava-seed\target\classes\club\throwable\annotation\Counter.class
@Counter
反编译后的字节码如下:
Classfile /D:/Projects/rxjava-seed/target/classes/club/throwable/annotation/Counter.class
Last modified 2018-10-6; size 487 bytes
MD5 checksum 83cee23f426e5b51a096281068d8b555
Compiled from "Counter.java"
public interface club.throwable.annotation.Counter extends java.lang.annotation.Annotation
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
Constant pool:
#1 = Class #19 // club/throwable/annotation/Counter
#2 = Class #20 // java/lang/Object
#3 = Class #21 // java/lang/annotation/Annotation
#4 = Utf8 count
#5 = Utf8 ()I
#6 = Utf8 AnnotationDefault
#7 = Integer 0
#8 = Utf8 SourceFile
#9 = Utf8 Counter.java
#10 = Utf8 RuntimeVisibleAnnotations
#11 = Utf8 Ljava/lang/annotation/Retention;
#12 = Utf8 value
#13 = Utf8 Ljava/lang/annotation/RetentionPolicy;
#14 = Utf8 RUNTIME
#15 = Utf8 Ljava/lang/annotation/Documented;
#16 = Utf8 Ljava/lang/annotation/Target;
#17 = Utf8 &nbs