今天来研究研究Java的注解是如何实现的。最简单的,先来看看注解的class文件是什么样的。
class文件支持
直接看栗子,
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Ann0tation {
String attr();
}
javap一下,
Classfile /home/blues/Projects/just4fun/out/production/just4fun/me/kisimple/just4fun/Ann0tation.class
Last modified Feb 10, 2015; size 432 bytes
MD5 checksum d87c1a02d469944f093b8a1815add76f
Compiled from "Ann0tation.java"
public interface me.kisimple.just4fun.Ann0tation extends java.lang.annotation.Annotation
SourceFile: "Ann0tation.java"
RuntimeVisibleAnnotations:
0: #9(#10:[e#11.#12])
1: #13(#10:e#14.#15)
minor version: 0
major version: 51
flags: ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
Constant pool:
#1 = Class #16; // me/kisimple/just4fun/Ann0tation
#2 = Class #17; // java/lang/Object
#3 = Class #18; // java/lang/annotation/Annotation
#4 = Utf8 attr;
#5 = Utf8 ()Ljava/lang/String;;
#6 = Utf8 SourceFile;
#7 = Utf8 Ann0tation.java;
#8 = Utf8 RuntimeVisibleAnnotations;
#9 = Utf8 Ljava/lang/annotation/Target;;
#10 = Utf8 value;
#11 = Utf8 Ljava/lang/annotation/ElementType;;
#12 = Utf8 METHOD;
#13 = Utf8 Ljava/lang/annotation/Retention;;
#14 = Utf8 Ljava/lang/annotation/RetentionPolicy;;
#15 = Utf8 RUNTIME;
#16 = Utf8 me/kisimple/just4fun/Ann0tation;
#17 = Utf8 java/lang/Object;
#18 = Utf8 java/lang/annotation/Annotation;
{
public abstract java.lang.String attr();
flags: ACC_PUBLIC, ACC_ABSTRACT
}
可以看到,所有的注解都继承了java.lang.annotation.Annotation这个接口,
public interface me.kisimple.just4fun.Ann0tation extends java.lang.annotation.Annotation
接下来看下使用了注解后的class文件,
public class Main {
@Ann0tation(attr = "hello main.")
public static void main(String[] args) throws Exception {
Method mm = Main.class.getMethod("main", String[].class);
for (Annotation anno : mm.getDeclaredAnnotations()) {
if(anno instanceof Ann0tation) {
System.out.println(((Ann0tation)anno).attr());
}
}
}
}
public static void main(java.lang.String[]) throws java.lang.Exception;
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=6, locals=6, args_size=1
0: ldc_w #2; // class me/kisimple/just4fun/Main
3: ldc #3; // String main
5: iconst_1
6: anewarray #4; // class java/lang/Class
9: dup
10: iconst_0
11: ldc_w #5; // class "[Ljava/lang/String;"
14: aastore
15: invokevirtual #6; // Method java/lang/Class.getMethod:(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;
18: astore_1
19: aload_1
20: invokevirtual #7; // Method java/lang/reflect/Method.getDeclaredAnnotations:()[Ljava/lang/annotation/Annotation;
23: astore_2
24: aload_2
25: arraylength
26: istore_3
27: iconst_0
28: istore 4
30: iload 4
32: iload_3
33: if_icmpge 72
36: aload_2
37: iload 4
39: aaload
40: astore 5
42: aload 5
44: instanceof #8; // class me/kisimple/just4fun/Ann0tation
47: ifeq 66
50: getstatic #9; // Field java/lang/System.out:Ljava/io/PrintStream;
53: aload 5
55: checkcast #8; // class me/kisimple/just4fun/Ann0tation
58: invokeinterface #10, 1; // InterfaceMethod me/kisimple/just4fun/Ann0tation.attr:()Ljava/lang/String;
63: invokevirtual #11; // Method java/io/PrintStream.println:(Ljava/lang/String;)V
66: iinc 4, 1
69: goto 30
72: return
LineNumberTable:
line 13: 0
line 14: 19
line 15: 42
line 16: 50
line 14: 66
line 19: 72
LocalVariableTable:
Start Length Slot Name Signature
42 24 5 anno Ljava/lang/annotation/Annotation;
24 48 2 arr$ [Ljava/lang/annotation/Annotation;
27 45 3 len$ I
30 42 4 i$ I
0 73 0 args [Ljava/lang/String;
19 54 1 mm Ljava/lang/reflect/Method;
StackMapTable: number_of_entries = 3
frame_type = 255 /* full_frame */
offset_delta = 30
locals = [ class "[Ljava/lang/String;", class java/lang/reflect/Method, class "[Ljava/lang/annotation/Annotation;", int, int ]
stack = []
frame_type = 35 /* same */
frame_type = 248 /* chop */
offset_delta = 5
Exceptions:
throws java.lang.Exception
RuntimeVisibleAnnotations:
0: #39(#40:s#41)
会发现最后多出来了RuntimeVisibleAnnotations这么一个属性,而它的值就是我们给main方法添加的注解,
#39 = Utf8 Lme/kisimple/just4fun/Ann0tation;;
#40 = Utf8 attr;
#41 = Utf8 hello main.;
其实在上面Ann0tation的class文件里面就有这个属性了,因为Ann0tation使用了Target跟Retention注解。关于该属性可以参考虚拟机规范。
这么看下来,注解的定义与普通的接口其实并没有什么两样,只是编译器会在使用了注解的地方去添加一个RuntimeVisibleAnnotations属性,保存下我们添加的注解。这样即使在运行时,我们还是可以通过class文件来拿到我们添加的注解。
反射注解实例
既然注解只是个普通的接口,那么当我们使用反射去拿到的注解的实例(看上面的栗子)又是个什么鬼?看看源码就知道了。
上面我们所调用的Method#getDeclaredAnnotations,最后是会去调用
sun.reflect.annotation.AnnotationParser#parseAnnotations,
/**
* Parses the annotations described by the specified byte array.
* resolving constant references in the specified constant pool.
* The array must contain an array of annotations as described
* in the RuntimeVisibleAnnotations_attribute:
*
* u2 num_annotations;
* annotation annotations[num_annotations];
*
* @throws AnnotationFormatError if an annotation is found to be
* malformed.
*/
public static Map<Class<? extends Annotation>, Annotation> parseAnnotations(
byte[] rawAnnotations,
ConstantPool constPool,
Class<?> container) {
if (rawAnnotations == null)
return Collections.emptyMap();
try {
return parseAnnotations2(rawAnnotations, constPool, container, null);
} catch(BufferUnderflowException e) {
throw new AnnotationFormatError("Unexpected end of annotations.");
} catch(IllegalArgumentException e) {
// Type mismatch in constant pool
throw new AnnotationFormatError(e);
}
}
注解的解析其实就是去处理上面我们所说到的class文件中的RuntimeVisibleAnnotations属性相关的一些东东。继续往下看,最后会来到annotationForMap方法,
public static Annotation annotationForMap(
Class<? extends Annotation> type, Map<String, Object> memberValues)
{
return (Annotation) Proxy.newProxyInstance(
type.getClassLoader(), new Class[] { type },
new AnnotationInvocationHandler(type, memberValues));
}
原来是使用动态代理实例化了一个注解的接口返回,那我们可以使用SA中的ClassDump把这个动态代理类dump下来,再用jd反编译看看,结果如下,
package com.sun.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import me.kisimple.just4fun.Ann0tation;
public final class $Proxy1 extends Proxy
implements Ann0tation
{
private static Method m1;
private static Method m3;
private static Method m0;
private static Method m4;
private static Method m2;
public $Proxy1(InvocationHandler paramInvocationHandler)
{
super(paramInvocationHandler);
}
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("me.kisimple.just4fun.Ann0tation").getMethod("attr", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m4 = Class.forName("me.kisimple.just4fun.Ann0tation").getMethod("annotationType", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
public final boolean equals(Object paramObject)
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString()
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode()
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final Class annotationType()
{
try
{
return (Class)this.h.invoke(this, m4, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String attr()
{
try
{
return (String)this.h.invoke(this, m3, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
}
而AnnotationInvocationHandler做的事情很简单,举上面的栗子,当我们调用((Ann0tation)anno).attr()
时,AnnotationInvocationHandler直接从memberValues
这个map取出来并返回,而这个map就是通过解析RuntimeVisibleAnnotations得到并传给AnnotationInvocationHandler的。
所以就像R大说的,注解就是接口+Map,然后通过动态代理将他们组合起来就OK了^_^