Java之道系列:Annotation实现浅析
来源:互联网 发布:淘宝双11红包口令 编辑:程序博客网 时间:2024/05/17 07:40
今天来研究研究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_ANNOTATIONConstant 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了^_^
参考资料
- http://rednaxelafx.iteye.com/blog/1148983
- Java之道系列:Annotation实现浅析
- Java之道系列:WeakHashMap实现浅析
- Java Annotation 浅析
- Java --Annotation 浅析1
- Java --Annotation 浅析2
- Java Annotation 浅析
- Java Annotation 浅析
- Java注解Annotation浅析
- Java注解Annotation浅析
- Java学习之注解Annotation实现原理
- Java学习之注解Annotation实现原理
- Java Annotation实现原理
- Java:Annotation 浅析(一 )自定义注解
- java学习之annotation
- Java之自定义Annotation
- java 之annotation
- JAVA之Annotation(注释)
- Java 之 Annotation
- HDU 2102 A计划
- 排水沟(Drainage Ditches)——最大流水题
- 工作中遇到的shell脚本知识(4)
- 【java编程】String之字符串反转
- hdu 2639 Bone Collector II 01背包问题 求第K大最优值。。
- Java之道系列:Annotation实现浅析
- 一个项目中初始化的问题
- Java NIO系列教程(二) Channel
- 关系型数据库 V.S. 非关系型数据库
- C语言及程序设计提高例程-19 越界的后果
- Spring定时任务的几种实现
- android:使用fragment实现tab切换
- (二)Maven学习笔记
- Partition List