用Java快三年了,注解算是一个常用的类型,特别是在一些框架里面会大量使用注解做组件标识、配置或者策略。但是一直没有深入去探究JDK中的注解到底是什么,底层是怎么实现了?于是参考了一些资料,做了一次稍微详细的分析。
JDK的注解描述参考JavaSE-8里面的对注解的描述如下:
注解的声明如下:
{InterfaceModifier} @ interface Identifier AnnotationTypeBody
接口修饰符 @ interface 注解标识符 注解类型的内容
其中:
注解类型声明中的标识符指定了注解类型的名称。
如果注解类型与它的任何封闭类或接口具有相同的简单名称,则编译时会出现错误。
每个注解类型的直接父接口都是java.lang.annotation.Annotation。
既然所有注解类型的父接口都是java.lang.annotation.Annotation,那么我们可以看一下Annotation接口的文档:
public interface Annotation
The common interface extended by all annotation types. Note that an interface that manually extends this one does not define an annotation type. Also note that this interface does not itself define an annotation type. More information about annotation types can be found in section 9.6 of The Java™ Language Specification. The AnnotatedElement interface discusses compatibility concerns when evolving an annotation type from being non-repeatable to being repeatable.
Since: 1.5
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 Ljava/lang/annotation/ElementType;
#18 = Utf8 TYPE
#19 = Utf8 club/throwable/annotation/Counter
#20 = Utf8 java/lang/Object
#21 = Utf8 java/lang/annotation/Annotation
{
public abstract int count();
descriptor: ()I
flags: ACC_PUBLIC, ACC_ABSTRACT
AnnotationDefault:
default_value: I#7}
SourceFile: "Counter.java"
RuntimeVisibleAnnotations:
0: #11(#12=e#13.#14)
1: #15()
2: #16(#12=[e#17.#18])
如果熟悉字节码,从直观上可以得到下面的信息:
注解是一个接口,它继承自java.lang.annotation.Annotation父接口。
@Counter对应的接口接口除了继承了java.lang.annotation.Annotation中的抽象方法,自身定义了一个抽象方法public abstract int count();。