from:http://blog.csdn.net/lylwo317/article/details/52163304

注解在中到底是什么样的东西?具体是如何实现的? 
本文将一层一层深入探究注解的实现原理。为了尽可能的将分析的过程呈现出来,所以文章包含了大量的截图和代码。(ps:如果图片看不清楚,请将网页放大来看,chrome可以通过ctrl 鼠标滚轮放大


知识方面

开始分析前,提醒一下,下面的分析必须具备以下知识 
1. 知道如何自定义注解 
2. 理解java动态代理机制 
3. 了解java常量池 
如果不具备以上的知识,会看得云里雾里的。上面提到的知识点谷歌百度都可以找到许多相关的文章。

工具方面

  1. intellij 2016

首先写一个简单的自定义注解小程序。

先自定义一个运行时注解

@target(elementtype.type) @retention(retentionpolicy.runtime) public @interface helloannotation {      string say() default "hi";  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

然后在main函数中解析注解

@helloannotation(say = "do it!") public class testmain {     public static void main(string[] args) {         helloannotation annotation = testmain.class.getannotation(helloannotation.class);//获取testmain类上的注解对象         system.out.println(annotation.say());//调用注解对象的say方法,并打印到控制台     } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

运行程序,输出结果如下:

do it!
  • 1
  • 1

下面将围绕上面的代码来研究java注解(annotation)的实现原理


1. 注解对象具体是什么?

首先,我们先在main函数第一行断点,看看helloannotation具体是什么类的对象

可以看到helloannotation注解的实例是jvm生成的动态代理类的对象。

这个运行时生成的动态代理对象是可以导出到文件的,方法有两种

  1. 在代码中加入system.setproperty("sun.misc.proxygenerator.savegeneratedfiles", "true");
  2. 在运行时加入jvm 参数 -dsun.misc.proxygenerator.savegeneratedfiles=true

这里使用第一种,↓

然后运行程序。

可以看到,已经导出了运行时生成的代理类。↑

helloannotation的动态代理类是$proxy1.class,intellij自带了反编译工具,直接双击打开,得到如下的java代码

// // source code recreated from a .class file by intellij idea // (powered by fernflower decompiler) //  package com.sun.proxy;  import com.kevin.java.annotation.runtimeannotation.helloannotation; import java.lang.reflect.invocationhandler; import java.lang.reflect.method; import java.lang.reflect.proxy; import java.lang.reflect.undeclaredthrowableexception;  public final class $proxy1 extends proxy implements helloannotation {     private static method m1;     private static method m2;     private static method m4;     private static method m3;     private static method m0;      public $proxy1(invocationhandler var1) throws  {         super(var1);     }      public final boolean equals(object var1) throws  {         try {             return ((boolean)super.h.invoke(this, m1, new object[]{var1})).booleanvalue();         } catch (runtimeexception | error var3) {             throw var3;         } catch (throwable var4) {             throw new undeclaredthrowableexception(var4);         }     }      public final string tostring() throws  {         try {             return (string)super.h.invoke(this, m2, (object[])null);         } catch (runtimeexception | error var2) {             throw var2;         } catch (throwable var3) {             throw new undeclaredthrowableexception(var3);         }     }      public final class annotationtype() throws  {         try {             return (class)super.h.invoke(this, m4, (object[])null);         } catch (runtimeexception | error var2) {             throw var2;         } catch (throwable var3) {             throw new undeclaredthrowableexception(var3);         }     }      public final string say() throws  {         try {             return (string)super.h.invoke(this, m3, (object[])null);         } catch (runtimeexception | error var2) {             throw var2;         } catch (throwable var3) {             throw new undeclaredthrowableexception(var3);         }     }      public final int hashcode() throws  {         try {             return ((integer)super.h.invoke(this, m0, (object[])null)).intvalue();         } catch (runtimeexception | error var2) {             throw var2;         } catch (throwable var3) {             throw new undeclaredthrowableexception(var3);         }     }      static {         try {             m1 = class.forname("java.lang.object").getmethod("equals", new class[]{class.forname("java.lang.object")});             m2 = class.forname("java.lang.object").getmethod("tostring", new class[0]);             m4 = class.forname("com.kevin.java.annotation.runtimeannotation.helloannotation").getmethod("annotationtype", new class[0]);             m3 = class.forname("com.kevin.java.annotation.runtimeannotation.helloannotation").getmethod("say", new class[0]);             m0 = class.forname("java.lang.object").getmethod("hashcode", new class[0]);         } catch (nosuchmethodexception var2) {             throw new nosuchmethoderror(var2.getmessage());         } catch (classnotfoundexception var3) {             throw new noclassdeffounderror(var3.getmessage());         }     } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89

从第14行我们可以看到,我们自定义的注解helloannotation是一个接口,而$proxy1这个java生成的动态代理类就是它的实现类

我们接着看一下helloannotation的字节码

 $ javap -verbose helloannotation  warning: binary file helloannotation contains com.kevin.java.annotation.runtimeannotation.helloannotation classfile /home/kevin/workspace/ideaprojects/javalearn/out/production/javalearn/com/kevin/java/annotation/runtimeannotation/helloannotation.class   last modified aug 6, 2016; size 496 bytes   md5 checksum a6c87f863669f6ab9050ffa310160ea5   compiled from "helloannotation.java" public interface com.kevin.java.annotation.runtimeannotation.helloannotation extends java.lang.annotation.annotation   minor version: 0   major version: 52   flags: acc_public, acc_interface, acc_abstract, acc_annotation constant pool:    #1 = class              #18            // com/kevin/java/annotation/runtimeannotation/helloannotation    #2 = class              #19            // java/lang/object    #3 = class              #20            // java/lang/annotation/annotation    #4 = utf8               say    #5 = utf8               ()ljava/lang/string;    #6 = utf8               annotationdefault    #7 = utf8               hi    #8 = utf8               sourcefile    #9 = utf8               helloannotation.java   #10 = utf8               runtimevisibleannotations   #11 = utf8               ljava/lang/annotation/target;   #12 = utf8               value   #13 = utf8               ljava/lang/annotation/elementtype;   #14 = utf8               type   #15 = utf8               ljava/lang/annotation/retention;   #16 = utf8               ljava/lang/annotation/retentionpolicy;   #17 = utf8               runtime   #18 = utf8               com/kevin/java/annotation/runtimeannotation/helloannotation   #19 = utf8               java/lang/object   #20 = utf8               java/lang/annotation/annotation {   public abstract java.lang.string say();     descriptor: ()ljava/lang/string;     flags: acc_public, acc_abstract     annotationdefault:       default_value: s#7} sourcefile: "helloannotation.java" runtimevisibleannotations:   0: #11(#12=[e#13.#14])   1: #15(#12=e#16.#17) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

看到第7行。很明显,helloannotation就是继承了annotation的接口。再看第10行,flag字段中,我们可以看到,有个acc_annotation标记,说明是一个注解,所以注解本质是一个继承了annotation的特殊接口。

而annotation接口声明了以下方法。

package java.lang.annotation;  public interface annotation {     boolean equals(object var1);      int hashcode();      string tostring();      class annotationtype(); } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

这些方法,已经被$proxy1实现了。(这就是动态代理的机制)

小结

现在我们知道了helloannotation注解(接口)是一个继承了annotation接口的特殊接口,而我们通过反射获取注解时,返回的是java运行时生成的动态代理对象$proxy1,该类就是helloannotation注解(接口)的具体实现类。


2. 动态代理类$proxy1是如何处理annotation.say()方法的调用?

无论是否了解动态代理,这里只需要明确一点,动态代理方法的调用最终会传递给绑定的invocationhandler实例的invoke方法处理。我们可以看看源码

$proxy1.java

public final class $proxy1 extends proxy implements helloannotation {    .....    public final string say() throws  {         try {             return (string)super.h.invoke(this, m3, (object[])null);         } catch (runtimeexception | error var2) {             throw var2;         } catch (throwable var3) {             throw new undeclaredthrowableexception(var3);         }     }     .... }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

proxy.java

public class proxy implements java.io.serializable {      /**      * the invocation handler for this proxy instance.      * @serial      */     protected invocationhandler h;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

从上面不难看出,say方法最终会执行(string)super.h.invoke(this, m3, (object[])null);,而这其中的h对象类型就是invocationhandler接口的某个实现类

断点调试,看看invocationhandler具体实现类是哪个。

可以看到h对象是annotationinvocationhandler的实例。让我们来看看该实现类的invoke方法。

class annotationinvocationhandler implements invocationhandler, serializable {     private static final long serialversionuid = 6182022883658399397l;     private final classextends annotation> type;     private final map<string, object> membervalues;     private transient volatile method[] membermethods = null;      annotationinvocationhandler(classextends annotation> var1, map<string, object> var2) {         class[] var3 = var1.getinterfaces();         if(var1.isannotation() && var3.length == 1 && var3[0] == annotation.class) {             this.type = var1;             this.membervalues = var2;         } else {             throw new annotationformaterror("attempt to create proxy for a non-annotation type.");         }     }      public object invoke(object var1, method var2, object[] var3) {         string var4 = var2.getname();         class[] var5 = var2.getparametertypes();         if(var4.equals("equals") && var5.length == 1 && var5[0] == object.class) {             return this.equalsimpl(var3[0]);         } else if(var5.length != 0) {             throw new assertionerror("too many parameters for an annotation method");         } else {             byte var7 = -1;             switch(var4.hashcode()) {             case -1776922004:                 if(var4.equals("tostring")) {                     var7 = 0;                 }                 break;             case 147696667:                 if(var4.equals("hashcode")) {                     var7 = 1;                 }                 break;             case 1444986633:                 if(var4.equals("annotationtype")) {                     var7 = 2;                 }             }              switch(var7) {             case 0:                 return this.tostringimpl();             case 1:                 return integer.valueof(this.hashcodeimpl());             case 2:                 return this.type;             default:                 object var6 = this.membervalues.get(var4);                 if(var6 == null) {                     throw new incompleteannotationexception(this.type, var4);                 } else if(var6 instanceof exceptionproxy) {                     throw ((exceptionproxy)var6).generateexception();                 } else {                     if(var6.getclass().isarray() && array.getlength(var6) != 0) {                         var6 = this.clonearray(var6);                     }                      return var6;                 }             }         }     }     ....... }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67

我们直接从invoke方法第一行开始单步调试,看看invoke方法是如何处理我们annotation.say()方法的调用的。

这里再贴一次代码,不然就得翻到前面了

@helloannotation(say = "do it!") public class testmain {     public static void main(string[] args) {         helloannotation annotation = testmain.class.getannotation(helloannotation.class);         system.out.println(annotation.say());     } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

可以看到,say方法的返回值是从一个map中获取到的。这个map以key(注解方法名)—value(注解方法对应的值)存储testmain类上的注解

那membervalues这个map对象是怎么生成的,继续调试 
通过方法调用栈找到membervalues的本源

我们继续跟进parsemembervalue()方法

在parsemembervalue()中会调用parseconst方法,继续跟进到parseconst方法

可以看到,membervalues是通过常量池获取到,return var2.getutf8at(var3);中的var3就是常量池中的序号。继续执行返回到parsemembervalue()方法

可以看到获取的就是我们定义在testmain类上注解的say的值——“do it!”

这里可以通过javap -verbose testmain查看testmain字节码中的常量池

$ javap -verbose testmain                                            warning: binary file testmain contains com.kevin.java.annotation.runtimeannotation.testmain classfile /home/kevin/workspace/ideaprojects/javalearn/out/production/javalearn/com/kevin/java/annotation/runtimeannotation/testmain.class   last modified aug 10, 2016; size 1117 bytes   md5 checksum 610b7176c7dfdad08bc4862247df7123   compiled from "testmain.java" public class com.kevin.java.annotation.runtimeannotation.testmain   minor version: 0   major version: 52   flags: acc_public, acc_super constant pool://常量池    #1 = methodref          #11.#30        // java/lang/object."":()v    #2 = string             #31            // sun.misc.proxygenerator.savegeneratedfiles    #3 = string             #32            // true    #4 = methodref          #33.#34        // java/lang/system.setproperty:(ljava/lang/string;ljava/lang/string;)ljava/lang/string;    #5 = class              #35            // com/kevin/java/annotation/runtimeannotation/testmain    #6 = class              #36            // com/kevin/java/annotation/runtimeannotation/helloannotation    #7 = methodref          #37.#38        // java/lang/class.getannotation:(ljava/lang/class;)ljava/lang/annotation/annotation;    #8 = fieldref           #33.#39        // java/lang/system.out:ljava/io/printstream;    #9 = interfacemethodref #6.#40         // com/kevin/java/annotation/runtimeannotation/helloannotation.say:()ljava/lang/string;   #10 = methodref          #41.#42        // java/io/printstream.println:(ljava/lang/string;)v   #11 = class              #43            // java/lang/object   #12 = utf8               <init>   #13 = utf8               ()v   #14 = utf8               code   #15 = utf8               linenumbertable   #16 = utf8               localvariabletable   #17 = utf8               this   #18 = utf8               lcom/kevin/java/annotation/runtimeannotation/testmain;   #19 = utf8               main   #20 = utf8               ([ljava/lang/string;)v   #21 = utf8               args   #22 = utf8               [ljava/lang/string;   #23 = utf8               annotation   #24 = utf8               lcom/kevin/java/annotation/runtimeannotation/helloannotation;   #25 = utf8               sourcefile   #26 = utf8               testmain.java   #27 = utf8               runtimevisibleannotations   #28 = utf8               say   #29 = utf8               do it!   #30 = nameandtype        #12:#13        // "":()v   #31 = utf8               sun.misc.proxygenerator.savegeneratedfiles   #32 = utf8               true   #33 = class              #44            // java/lang/system   #34 = nameandtype        #45:#46        // setproperty:(ljava/lang/string;ljava/lang/string;)ljava/lang/string;   #35 = utf8               com/kevin/java/annotation/runtimeannotation/testmain   #36 = utf8               com/kevin/java/annotation/runtimeannotation/helloannotation   #37 = class              #47            // java/lang/class   #38 = nameandtype        #48:#49        // getannotation:(ljava/lang/class;)ljava/lang/annotation/annotation;   #39 = nameandtype        #50:#51        // out:ljava/io/printstream;   #40 = nameandtype        #28:#52        // say:()ljava/lang/string;   #41 = class              #53            // java/io/printstream   #42 = nameandtype        #54:#55        // println:(ljava/lang/string;)v   #43 = utf8               java/lang/object   #44 = utf8               java/lang/system   #45 = utf8               setproperty   #46 = utf8               (ljava/lang/string;ljava/lang/string;)ljava/lang/string;   #47 = utf8               java/lang/class   #48 = utf8               getannotation   #49 = utf8               (ljava/lang/class;)ljava/lang/annotation/annotation;   #50 = utf8               out   #51 = utf8               ljava/io/printstream;   #52 = utf8               ()ljava/lang/string;   #53 = utf8               java/io/printstream   #54 = utf8               println   #55 = utf8               (ljava/lang/string;)v {   public com.kevin.java.annotation.runtimeannotation.testmain();     descriptor: ()v     flags: acc_public     code:       stack=1, locals=1, args_size=1          0: aload_0          1: invokespecial #1                  // method java/lang/object."":()v          4: return       linenumbertable:         line 10: 0       localvariabletable:         start  length  slot  name   signature             0       5     0  this   lcom/kevin/java/annotation/runtimeannotation/testmain;    public static void main(java.lang.string[]);     descriptor: ([ljava/lang/string;)v     flags: acc_public, acc_static     code:       stack=2, locals=2, args_size=1          0: ldc           #2                  // string sun.misc.proxygenerator.savegeneratedfiles          2: ldc           #3                  // string true          4: invokestatic  #4                  // method java/lang/system.setproperty:(ljava/lang/string;ljava/lang/string;)ljava/lang/string;          7: pop          8: ldc           #5                  // class com/kevin/java/annotation/runtimeannotation/testmain         10: ldc           #6                  // class com/kevin/java/annotation/runtimeannotation/helloannotation         12: invokevirtual #7                  // method java/lang/class.getannotation:(ljava/lang/class;)ljava/lang/annotation/annotation;         15: checkcast     #6                  // class com/kevin/java/annotation/runtimeannotation/helloannotation         18: astore_1         19: getstatic     #8                  // field java/lang/system.out:ljava/io/printstream;         22: aload_1         23: invokeinterface #9,  1            // interfacemethod com/kevin/java/annotation/runtimeannotation/helloannotation.say:()ljava/lang/string;         28: invokevirtual #10                 // method java/io/printstream.println:(ljava/lang/string;)v         31: return       linenumbertable:         line 13: 0         line 14: 8         line 15: 19         line 16: 31       localvariabletable:         start  length  slot  name   signature             0      32     0  args   [ljava/lang/string;            19      13     1 annotation   lcom/kevin/java/annotation/runtimeannotation/helloannotation; } sourcefile: "testmain.java" runtimevisibleannotations:   0: #24(#28=s#29) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114

仔细看第40行#29 = utf8 do it!,可以看到#29与var3的29对应(也就常量池的索引),对应的值就是do it!

以上就是say方法调用的细节。

注解本质是一个继承了annotation的特殊接口,其具体实现类是java运行时生成的动态代理类。通过代理对象调用自定义注解(接口)的方法,会最终调用annotationinvocationhandler的invoke方法。该方法会从membervalues这个map中索引出对应的值。而membervalues的来源是java常量池。