0.
之前的一篇博客中写了写关于Annotation使用相关的,今天来分析下Annotation是实现原理
由于RetentionPolicy.SOURCE RetentionPolicy.CLASS的两种注解是JVM不可见的(在程序运行时,就无法使用到该注解)所以不做分析。
Test.java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {
String color() default "red";
}
public class Test {
@MyAnnotation(color = "blue")
private String text;
}
javac编译之后,产生Annotation.class和MyAnnotation.calss两个字节码文件
用javap分析如下:
1.注解的字节码 :MyAnnotion.class
- interface MyAnnotation extends java.lang.annotation.Annotation 这段表示我自定义的MyAnnotation注解最终是被编译成一个继承java.lang.annotation.Annotation的接口
- 关于常量池也就不做过多解释了
- 可以看到我们在注解里定义的color属性,最终是编译成了接口中的一个方法 public abstract java.lang.String color(); 这个方法的method_info中包含了:descriptior, flags, AnnotionDefault。AnnotationDefault记录了我们定义的默认值:#7(red)。在代码中可以通过反射来获取这些info
- 最后这个MyAnnotation接口包含了一个RuntimeVisibleAnnotations属性,其中中保存了两条信息:分别就对应到MyAnnotation注解的元注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
Classfile /home/MyAnnotation.class
Last modified 2018-9-9; size 448 bytes
MD5 checksum 7586f606bb84bd2ad4a880c0a50907a0
Compiled from "Annotation.java"
interface MyAnnotation extends java.lang.annotation.Annotation
minor version: 0
major version: 52
flags: ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
Constant pool:
#1 = Class #18 // MyAnnotation
#2 = Class #19 // java/lang/Object
#3 = Class #20 // java/lang/annotation/Annotation
#4 = Utf8 color
#5 = Utf8 ()Ljava/lang/String;
#6 = Utf8 AnnotationDefault
#7 = Utf8 red
#8 = Utf8 SourceFile
#9 = Utf8 Annotation.java
#10 = Utf8 RuntimeVisibleAnnotations
#11 = Utf8 Ljava/lang/annotation/Target;
#12 = Utf8 value
#13 = Utf8 Ljava/lang/annotation/ElementType;
#14 = Utf8 FIELD
#15 = Utf8 Ljava/lang/annotation/Retention;
#16 = Utf8 Ljava/lang/annotation/RetentionPolicy;
#17 = Utf8 RUNTIME
#18 = Utf8 MyAnnotation
#19 = Utf8 java/lang/Object
#20 = Utf8 java/lang/annotation/Annotation
{
public abstract java.lang.String color();
descriptor: ()Ljava/lang/String;
flags: ACC_PUBLIC, ACC_ABSTRACT
AnnotationDefault:
default_value: s#7}
SourceFile: "Annotation.java"
RuntimeVisibleAnnotations:
0: #11(#12=[e#13.#14])
1: #15(#12=e#16.#17)
2.关于Test.class
1.重点关注我们使用注解的text变量对应的部分。
可以看到text变量的包含了一个RuntimeVisibleAnnotations属性,这个属性他记录了一条信息
0: #7(#8=s#9)
结合上面的常量池(Constant pool)中的值翻译过来也就是
LMyAnnotation (color=blue)
这样一看,跟我们代码里写的是不是都没多少区别?
Test.class
Classfile /home/Test.class
Last modified 2018-9-9; size 295 bytes
MD5 checksum 84648beabb79541d40d7aca24e044c7b
Compiled from "Test.java"
public class Test
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #3.#16 // java/lang/Object."<init>":()V
#2 = Class #17 // Test
#3 = Class #18 // java/lang/Object
#4 = Utf8 text
#5 = Utf8 Ljava/lang/String;
#6 = Utf8 RuntimeVisibleAnnotations
#7 = Utf8 LMyAnnotation;
#8 = Utf8 color
#9 = Utf8 blue
#10 = Utf8 <init>
#11 = Utf8 ()V
#12 = Utf8 Code
#13 = Utf8 LineNumberTable
#14 = Utf8 SourceFile
#15 = Utf8 Test.java
#16 = NameAndType #10:#11 // "<init>":()V
#17 = Utf8 Test
#18 = Utf8 java/lang/Object
{
private java.lang.String text;
descriptor: Ljava/lang/String;
flags: ACC_PRIVATE
RuntimeVisibleAnnotations:
0: #7(#8=s#9)
public Test();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 14: 0
}
SourceFile: "Test.java"
3.总结
OK!两个字节码文件分析完了,总结下Annotation的实现:
1. 注解本质就是一个接口
2. 注解中自定义的属性本质就是一个方法,不过这个方法是abstract的(没有任何实现)
3. 注解编译后的生成文件中包含了RuntimeVisibleAnnotations字段,该字段记录了该注解的元注解的信息
4. 对于使用到注解的地方,编译后,会包含RuntimeVisibleAnnotations字段,来记录对该注解的使用
所以,说来说去,注解的本质就是打个标签/标记
最终可以在JVM中运行时,通过反射机制获取到RuntimeVisibleAnnotations字段中保存的信息,然后做出相应的处理。
The RuntimeVisibleAnnotations attribute is a variable length attribute in the attributes table of the ClassFile, field_info, and method_info structures. The RuntimeVisibleAnnotations attribute records runtime-visible Java programming language annotations on the corresponding class, method, or field. Each ClassFile, field_info, and method_info structure may contain at most one RuntimeVisibleAnnotations attribute, which records all the runtime-visible Java programming language annotations on the corresponding program element. The JVM must make these annotations available so they can be returned by the appropriate reflective APIs.
翻译下:
1. RuntimeVisibleAnnotations是ClassFile、field_info、method_info结构中的一个可变长属性。
2. RuntimeVisibleAnnotations属性记录了class,method,field中使用到的运行时注解
3. 每个ClassFile、field_info、method_info结构中只能包含一个RuntimeVisibleAnnotations属性,这个属性中记录了所有的运行时注解
4. 这些注解是对JVM可见的,这样就能通反射API来获取注解信息
END
至于JVM是如何通过反射或获取的,另外写个博客来说。