Java注解(Annotation)原理详解

转载自:http://blog.csdn.net/lylwo317/article/details/52163304

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

// 先自定义一个运行时注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface HelloAnnotation {
String <span class="hljs-title function_">say</span><span class="hljs-params">()</span> <span class="hljs-keyword">default</span> <span class="hljs-string">"Hi"</span>;

}

  然后在 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 方法,并打印到控制台
    }
}
输出结果:Do it!

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

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

  首先,我们先在 main 函数第一行断点,看看 HelloAnnotation 具体是什么类的对象
动态代理类
  可以看到 HelloAnnotation 注解的实例是 jvm 生成的动态代理类的对象。

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

  • 在代码中加入 System.setProperty(“sun.misc.ProxyGenerator.saveGeneratedFiles”, “true”);
  • 在运行时加入 jvm 参数 -Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
    我们使用第一种如下:
@HelloAnnotation(say = "Do it!")
public class TestMain {
    public static void main(String[] args) {
        /* 设置此系统属性, 让 JVM 生成的 Proxy 类写入文件. 保存路径为:com/sun/proxy(如果不存在请手工创建) */
        System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        HelloAnnotation annotation = TestMain.class.getAnnotation(HelloAnnotation.class);// 获取 TestMain 类上的注解对象
        System.out.println(annotation.say());// 调用注解对象的 say 方法,并打印到控制台
    }
}

然后运行程序。
这里写图片描述
  可以看到,已经导出了运行时生成的代理类,而且每个分别实现了一个接口。
HelloAnnotation 的动态代理类是 $Proxy3.class,Intellij 自带了反编译工具,直接双击打开,得到如下的 Java 代码:

public final class $Proxy3 extends Proxy implements HelloAnnotation {
    private static Method m3;
    private static Method m1;
    private static Method m0;
    private static Method m4;
    private static Method m2;
<span class="hljs-keyword">public</span> $Proxy3(InvocationHandler var1) <span class="hljs-keyword">throws</span>  {
    <span class="hljs-built_in">super</span>(var1);
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> String <span class="hljs-title function_">say</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span>  {
    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">return</span> (String)<span class="hljs-built_in">super</span>.h.invoke(<span class="hljs-built_in">this</span>, m3, (Object[])<span class="hljs-literal">null</span>);
    } <span class="hljs-keyword">catch</span> (RuntimeException | Error var2) {
        <span class="hljs-keyword">throw</span> var2;
    } <span class="hljs-keyword">catch</span> (Throwable var3) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">UndeclaredThrowableException</span>(var3);
    }
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">equals</span><span class="hljs-params">(Object var1)</span> <span class="hljs-keyword">throws</span>  {
    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">return</span> ((Boolean)<span class="hljs-built_in">super</span>.h.invoke(<span class="hljs-built_in">this</span>, m1, <span class="hljs-keyword">new</span> <span class="hljs-title class_">Object</span>[]{var1})).booleanValue();
    } <span class="hljs-keyword">catch</span> (RuntimeException | Error var3) {
        <span class="hljs-keyword">throw</span> var3;
    } <span class="hljs-keyword">catch</span> (Throwable var4) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">UndeclaredThrowableException</span>(var4);
    }
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-type">int</span> <span class="hljs-title function_">hashCode</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span>  {
    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">return</span> ((Integer)<span class="hljs-built_in">super</span>.h.invoke(<span class="hljs-built_in">this</span>, m0, (Object[])<span class="hljs-literal">null</span>)).intValue();
    } <span class="hljs-keyword">catch</span> (RuntimeException | Error var2) {
        <span class="hljs-keyword">throw</span> var2;
    } <span class="hljs-keyword">catch</span> (Throwable var3) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">UndeclaredThrowableException</span>(var3);
    }
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> Class <span class="hljs-title function_">annotationType</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span>  {
    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">return</span> (Class)<span class="hljs-built_in">super</span>.h.invoke(<span class="hljs-built_in">this</span>, m4, (Object[])<span class="hljs-literal">null</span>);
    } <span class="hljs-keyword">catch</span> (RuntimeException | Error var2) {
        <span class="hljs-keyword">throw</span> var2;
    } <span class="hljs-keyword">catch</span> (Throwable var3) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">UndeclaredThrowableException</span>(var3);
    }
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> String <span class="hljs-title function_">toString</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span>  {
    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">return</span> (String)<span class="hljs-built_in">super</span>.h.invoke(<span class="hljs-built_in">this</span>, m2, (Object[])<span class="hljs-literal">null</span>);
    } <span class="hljs-keyword">catch</span> (RuntimeException | Error var2) {
        <span class="hljs-keyword">throw</span> var2;
    } <span class="hljs-keyword">catch</span> (Throwable var3) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">UndeclaredThrowableException</span>(var3);
    }
}

<span class="hljs-keyword">static</span> {
    <span class="hljs-keyword">try</span> {
        m3 = Class.forName(<span class="hljs-string">"HelloAnnotation"</span>).getMethod(<span class="hljs-string">"say"</span>, <span class="hljs-keyword">new</span> <span class="hljs-title class_">Class</span>[<span class="hljs-number">0</span>]);
        m1 = Class.forName(<span class="hljs-string">"java.lang.Object"</span>).getMethod(<span class="hljs-string">"equals"</span>, <span class="hljs-keyword">new</span> <span class="hljs-title class_">Class</span>[]{Class.forName(<span class="hljs-string">"java.lang.Object"</span>)});
        m0 = Class.forName(<span class="hljs-string">"java.lang.Object"</span>).getMethod(<span class="hljs-string">"hashCode"</span>, <span class="hljs-keyword">new</span> <span class="hljs-title class_">Class</span>[<span class="hljs-number">0</span>]);
        m4 = Class.forName(<span class="hljs-string">"HelloAnnotation"</span>).getMethod(<span class="hljs-string">"annotationType"</span>, <span class="hljs-keyword">new</span> <span class="hljs-title class_">Class</span>[<span class="hljs-number">0</span>]);
        m2 = Class.forName(<span class="hljs-string">"java.lang.Object"</span>).getMethod(<span class="hljs-string">"toString"</span>, <span class="hljs-keyword">new</span> <span class="hljs-title class_">Class</span>[<span class="hljs-number">0</span>]);
    } <span class="hljs-keyword">catch</span> (NoSuchMethodException var2) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">NoSuchMethodError</span>(var2.getMessage());
    } <span class="hljs-keyword">catch</span> (ClassNotFoundException var3) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">NoClassDefFoundError</span>(var3.getMessage());
    }
}

}

  从第一行我们可以看到,我们自定义的注解 HelloAnnotation 是一个接口,而 $Proxy1 这个 Java 生成的动态代理类就是它的实现类。
  我们接着看一下 HelloAnnotation 的字节码

 $ javap -verbose HelloAnnotation 
 public interface HelloAnnotation extends java.lang.annotation.Annotation
  SourceFile: "HelloAnnotation.java"
  RuntimeVisibleAnnotations:
    0: #11(#12=[e#13.#14])
    1: #15(#12=e#16.#17)
  minor version: 0
  major version: 51
  flags: ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
Constant pool:
   #1 = Class              #18            //  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               HelloAnnotation
  #19 = Utf8               java/lang/Object
  #20 = Utf8               java/lang/annotation/Annotation
{
  public abstract java.lang.String say();
    flags: ACC_PUBLIC, ACC_ABSTRACT
    AnnotationDefault:
      default_value: s#7}

  HelloAnnotation 就是继承了 Annotation 的接口。再看第 10 行,flag 字段中,我们可以看到,有个 ACC_ANNOTATION 标记,说明是一个注解,所以注解本质是一个继承了 Annotation 的特殊接口。
  而 Annotation 接口声明了以下方法。

public interface Annotation {
    boolean equals(Object var1);
    int hashCode();
    String toString();
    Class<? extends Annotation> annotationType();
}

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

小结

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

二、动态代理类 $Proxy3 如何处理 annotation.say() 方法的调用

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

$Proxy3.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);
        }
    }
    ....
}

  从上面不难看出,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 Class<? extends Annotation> type;
    private final Map<String, Object> memberValues;
    private transient volatile Method[] memberMethods = null;
AnnotationInvocationHandler(Class&lt;? <span class="hljs-keyword">extends</span> <span class="hljs-title class_">Annotation</span>&gt; var1, Map&lt;String, Object&gt; var2) {
    Class[] var3 = var1.getInterfaces();
    <span class="hljs-keyword">if</span>(var1.isAnnotation() &amp;&amp; var3.length == <span class="hljs-number">1</span> &amp;&amp; var3[<span class="hljs-number">0</span>] == Annotation.class) {
        <span class="hljs-built_in">this</span>.type = var1;
        <span class="hljs-built_in">this</span>.memberValues = var2;
    } <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">AnnotationFormatError</span>(<span class="hljs-string">"Attempt to create proxy for a non-annotation type."</span>);
    }
}

<span class="hljs-keyword">public</span> Object <span class="hljs-title function_">invoke</span><span class="hljs-params">(Object var1, Method var2, Object[] var3)</span> {
    <span class="hljs-type">String</span> <span class="hljs-variable">var4</span> <span class="hljs-operator">=</span> var2.getName();
    Class[] var5 = var2.getParameterTypes();
    <span class="hljs-keyword">if</span>(var4.equals(<span class="hljs-string">"equals"</span>) &amp;&amp; var5.length == <span class="hljs-number">1</span> &amp;&amp; var5[<span class="hljs-number">0</span>] == Object.class) {
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.equalsImpl(var3[<span class="hljs-number">0</span>]);
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(var5.length != <span class="hljs-number">0</span>) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">AssertionError</span>(<span class="hljs-string">"Too many parameters for an annotation method"</span>);
    } <span class="hljs-keyword">else</span> {
        <span class="hljs-type">byte</span> <span class="hljs-variable">var7</span> <span class="hljs-operator">=</span> -<span class="hljs-number">1</span>;
        <span class="hljs-keyword">switch</span>(var4.hashCode()) {
        <span class="hljs-keyword">case</span> -<span class="hljs-number">1776922004</span>:
            <span class="hljs-keyword">if</span>(var4.equals(<span class="hljs-string">"toString"</span>)) {
                var7 = <span class="hljs-number">0</span>;
            }
            <span class="hljs-keyword">break</span>;
        <span class="hljs-keyword">case</span> <span class="hljs-number">147696667</span>:
            <span class="hljs-keyword">if</span>(var4.equals(<span class="hljs-string">"hashCode"</span>)) {
                var7 = <span class="hljs-number">1</span>;
            }
            <span class="hljs-keyword">break</span>;
        <span class="hljs-keyword">case</span> <span class="hljs-number">1444986633</span>:
            <span class="hljs-keyword">if</span>(var4.equals(<span class="hljs-string">"annotationType"</span>)) {
                var7 = <span class="hljs-number">2</span>;
            }
        }

        <span class="hljs-keyword">switch</span>(var7) {
        <span class="hljs-keyword">case</span> <span class="hljs-number">0</span>:
            <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.toStringImpl();
        <span class="hljs-keyword">case</span> <span class="hljs-number">1</span>:
            <span class="hljs-keyword">return</span> Integer.valueOf(<span class="hljs-built_in">this</span>.hashCodeImpl());
        <span class="hljs-keyword">case</span> <span class="hljs-number">2</span>:
            <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.type;
        <span class="hljs-keyword">default</span>:
            <span class="hljs-type">Object</span> <span class="hljs-variable">var6</span> <span class="hljs-operator">=</span> <span class="hljs-built_in">this</span>.memberValues.get(var4);
            <span class="hljs-keyword">if</span>(var6 == <span class="hljs-literal">null</span>) {
                <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">IncompleteAnnotationException</span>(<span class="hljs-built_in">this</span>.type, var4);
            } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(var6 <span class="hljs-keyword">instanceof</span> ExceptionProxy) {
                <span class="hljs-keyword">throw</span> ((ExceptionProxy)var6).generateException();
            } <span class="hljs-keyword">else</span> {
                <span class="hljs-keyword">if</span>(var6.getClass().isArray() &amp;&amp; Array.getLength(var6) != <span class="hljs-number">0</span>) {
                    var6 = <span class="hljs-built_in">this</span>.cloneArray(var6);
                }

                <span class="hljs-keyword">return</span> var6;
            }
        }
    }
}
.......

}

  我们直接从 invoke 方法第一行开始单步调试,看看 invoke 方法是如何处理我们 annotation.say() 方法的调用的。
这里写图片描述
  可以看到,say 方法的返回值是从一个 Map 中获取到的。这个 map 以 key(注解方法名)—value(注解方法对应的值)存储 TestMain 类上的注解

  那 memberValues 这个 Map 对象是怎么生成的,继续调试
  通过方法调用栈找到 memberValues 的本源。
这里写图片描述
‘do,it’是在 parseMemberValue() 方法中获取的,我们继续跟进 parseMemberValue()方法

public static Object parseMemberValue(Class<?> var0, ByteBuffer var1, ConstantPool var2, Class<?> var3) {
        Object var4 = null;
        byte var5 = var1.get();
        switch(var5) {
        case 64:
            var4 = parseAnnotation(var1, var2, var3, true);
            break;
        case 91:
            return parseArray(var0, var1, var2, var3);
        case 99:
            var4 = parseClassValue(var1, var2, var3);
            break;
        case 101:
            return parseEnumValue(var0, var1, var2, var3);
        default:
            var4 = parseConst(var5, var1, var2); // 此处会调用 parseConst 方法,继续跟进到 parseConst 方法
        }
    <span class="hljs-keyword">if</span>(!(var4 <span class="hljs-keyword">instanceof</span> ExceptionProxy) &amp;&amp; !var0.isInstance(var4)) {
        var4 = <span class="hljs-keyword">new</span> <span class="hljs-title class_">AnnotationTypeMismatchExceptionProxy</span>(var4.getClass() + <span class="hljs-string">"["</span> + var4 + <span class="hljs-string">"]"</span>);
    }
    <span class="hljs-keyword">return</span> var4;
}

<span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> Object <span class="hljs-title function_">parseConst</span><span class="hljs-params">(<span class="hljs-type">int</span> var0, ByteBuffer var1, ConstantPool var2)</span> {
    <span class="hljs-type">int</span> <span class="hljs-variable">var3</span> <span class="hljs-operator">=</span> var1.getShort() &amp; <span class="hljs-string">'\uffff'</span>;
    <span class="hljs-keyword">switch</span>(var0) {
    <span class="hljs-keyword">case</span> <span class="hljs-number">66</span>:
        <span class="hljs-keyword">return</span> Byte.valueOf((<span class="hljs-type">byte</span>)var2.getIntAt(var3));
    <span class="hljs-keyword">case</span> <span class="hljs-number">67</span>:
        <span class="hljs-keyword">return</span> Character.valueOf((<span class="hljs-type">char</span>)var2.getIntAt(var3));
    <span class="hljs-keyword">case</span> <span class="hljs-number">68</span>:
        <span class="hljs-keyword">return</span> Double.valueOf(var2.getDoubleAt(var3));
    <span class="hljs-keyword">case</span> <span class="hljs-number">70</span>:
        <span class="hljs-keyword">return</span> Float.valueOf(var2.getFloatAt(var3));
    <span class="hljs-keyword">case</span> <span class="hljs-number">73</span>:
        <span class="hljs-keyword">return</span> Integer.valueOf(var2.getIntAt(var3));
    <span class="hljs-keyword">case</span> <span class="hljs-number">74</span>:
        <span class="hljs-keyword">return</span> Long.valueOf(var2.getLongAt(var3));
    <span class="hljs-keyword">case</span> <span class="hljs-number">83</span>:
        <span class="hljs-keyword">return</span> Short.valueOf((<span class="hljs-type">short</span>)var2.getIntAt(var3));
    <span class="hljs-keyword">case</span> <span class="hljs-number">90</span>:
        <span class="hljs-keyword">return</span> Boolean.valueOf(var2.getIntAt(var3) != <span class="hljs-number">0</span>);
    <span class="hljs-keyword">case</span> <span class="hljs-number">115</span>:
        <span class="hljs-keyword">return</span> var2.getUTF8At(var3); <span class="hljs-comment">//memberValues是通过常量池获取到,这里的var3就是常量池中的序号。</span>
    <span class="hljs-keyword">default</span>:
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">AnnotationFormatError</span>(<span class="hljs-string">"Invalid member-value tag in annotation: "</span> + var0);
    }
}</code></pre>

  调用完 parseConst 方法,然后返回到 parseMemberValue()方法
这里写图片描述
  可以看到获取的就是我们定义在 TestMain 类上注解的 say 的值——“Do it!”

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

$ javap -verbose TestMain                                           
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."<init>":()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! // 这里就是 Do it
  #30 = NameAndType        #12:#13        // "<init>":()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

  到此为止,say 方法就完成了。

总结

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