Java Annotation实现原理浅析(上)

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

  1. interface MyAnnotation extends java.lang.annotation.Annotation 这段表示我自定义的MyAnnotation注解最终是被编译成一个继承java.lang.annotation.Annotation的接口
  2. 关于常量池也就不做过多解释了
  3. 可以看到我们在注解里定义的color属性,最终是编译成了接口中的一个方法 public abstract java.lang.String color(); 这个方法的method_info中包含了:descriptior, flags, AnnotionDefault。AnnotationDefault记录了我们定义的默认值:#7(red)。在代码中可以通过反射来获取这些info
  4. 最后这个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是如何通过反射或获取的,另外写个博客来说。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值