Annotation的实现原理_JAVA_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > JAVA > Annotation的实现原理

Annotation的实现原理

 2012/8/21 11:13:40  Rejoy  程序员俱乐部  我要评论(0)
  • 摘要:我学技术一直抱着“知其然,知其所以然”的态度,如果学一个技术只停留在会用的表面上是满足不了我的,我会找出它的底层实现之后,才不会有所遗憾。而要找出它的底层实现的办法就是看它的源码,有一句话说:“源码面前了无秘密”。就这样,我阅读了一些JavaAPI和一些开源框架的源码:Java集合框架源码、Spring源码、Struts2源码、JDK动态代理源码,从中学到了不少的东西。对于反射机制和JDK动态代理的用法和实现原理都已经非常的熟悉。虽然在这之前已经对Annotation的用法已经非常的熟悉
  • 标签:实现 not Annotation
    我学技术一直抱着“知其然,知其所以然”的态度,如果学一个技术只停留在会用的表面上是满足不了我的,我会找出它的底层实现之后,才不会有所遗憾。而要找出它的底层实现的办法就是看它的源码,有一句话说:“源码面前了无秘密”。就这样,我阅读了一些Java API和一些开源框架的源码:Java集合框架源码、Spring源码、Struts2源码、JDK动态代理源码,从中学到了不少的东西。对于反射机制和JDK动态代理的用法和实现原理都已经非常的熟悉。

         虽然在这之前已经对Annotation的用法已经非常的熟悉,但是一直不知道它的底层是怎么实现的。最近两天在看Spring MVC的源码,在那大量的使用了Annotation,这让我有了一个想法:这Annotation这么神奇,它的底层是怎么实现的呢?

         就带着这样的一个问号,自己写了一个小小的测试程序。
package annotation.principle; 

import org.junit.Test;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * Annotation测试 
 * @author zyb
 * @since 2012-8-9
 */
@RequestMapping("test")
public class AnntotationTest {

	@Test
	public void testAnnotion() {
		RequestMapping requestMapping = AnntotationTest.class.getAnnotation(RequestMapping.class);
		System.out.println(requestMapping.getClass().getName());
	}
}

打印出来的结果让我吃了一惊,这不就是动态代理嘛。

       

         到了这里之后,带着这样的疑问,在Google和百度上搜了一下“注解的底层实现”,看到的都是教你怎么用注解的,我吐血。翻了一下《深入理解Java虚拟机》也没有找到答案,算了,自己动手。

         为了证实注解的实现是用了动态代理,自己又写了另外一段代码来验证自己的想法。
package annotation;
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 RequestMapping {
	String value();
}

package annotation; 

public class User {

	@RequestMapping("name")
	public String add(String name) {
		return "add";
	}
}

package annotation; 

import java.lang.reflect.Method;

/**
 * 注解工具类
 * @author zyb
 * @since 2012-8-9
 */
public class AnnotationUtils {

	/**
	 * 打印注解信息
	 */
	public static void printAnnotationInfo() throws Exception {
		System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
		
		Method[] methods = User.class.getDeclaredMethods();

		for (Method method : methods) {
			
			RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
			
			System.out.println("Methods from super class:");
			
			for (Method annotationMethod : requestMapping.getClass().getMethods()) {
				System.out.println(annotationMethod.getName());
			}
			
			System.out.println("----------------------------------");
			
			System.out.println("super class:" + requestMapping.getClass().getSuperclass().getName());
			
			System.out.print("super interfaces:");
			
			for (Class interfaceClass : requestMapping.getClass().getInterfaces()) {
				System.out.println(interfaceClass.getName());
			}
			
			System.out.println("----------------------------------");
			System.out.println("InvocationHandler:" + requestMapping.getClass());
			
//			System.out.println(requestMapping.getClass().getMethod("getProxyClass", ClassLoader.class, 
//							RequestMapping.class).
//							invoke(requestMapping, 
//							Thread.currentThread().getContextClassLoader(), RequestMapping.class));
			
		}
	}
	
	/**
	 * 把产生代理类的class文件保存到硬盘上
	 */
	public static void saveProxyClassInHardDisk() {
		System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
	}
}

package annotation;

/**
 * 注解测试
 * @author zyb
 * @since 2012-8-9
 */
public class AnnotationTest {


	public static void main(String[] args) throws Exception {
		// 生成代理类class文件的代码一定在放在main方法,用JUnit生成不了
		AnnotationUtils.saveProxyClassInHardDisk();
		AnnotationUtils.printAnnotationInfo();
	}
}



打印的结果如下:

Java HotSpot(TM) Client VMwarning: increase O_BUFLEN in ostream.hpp -- output truncated

Methods from super class:

value

hashCode

equals

toString

annotationType

isProxyClass

newProxyInstance

getInvocationHandler

getProxyClass

wait

wait

wait

getClass

notify

notifyAll

----------------------------------

superclass:java.lang.reflect.Proxy

super interfaces:annotation.RequestMapping





    一看结果,我的想法果然是对的。Java为这个RequestMapping注解生成了一个代理类,名字叫$Proxy3,这个$Proxy3的父类是Proxy,实现的接口是RequestMapping,原来RequestMapping注解是一个接口,这个接口继承自Annotation接口,所以打印出了Annotation接口中的所有方法。

    再来看一下自动生成的那个代理类是长什么样子的,
// Decompiled by DJ v3.11.11.95 Copyright 2009 Atanas Neshkov  Date: 2012/8/9 14:39:44
// Home Page: http://members.fortunecity.com/neshkov/dj.html  http://www.neshkov.com/dj.html - Check often for new version!
// Decompiler options: packimports(3) 

import annotation.RequestMapping;
import java.lang.reflect.*;

public final class $Proxy3 extends Proxy
    implements RequestMapping
{

    public $Proxy3(InvocationHandler invocationhandler)
    {
        super(invocationhandler);
    }

    public final boolean equals(Object obj)
    {
        try
        {
            return ((Boolean)super.h.invoke(this, m1, new Object[] {
                obj
            })).booleanValue();
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final String value()
    {
        try
        {
            return (String)super.h.invoke(this, m3, null);
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final int hashCode()
    {
        try
        {
            return ((Integer)super.h.invoke(this, m0, null)).intValue();
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final Class annotationType()
    {
        try
        {
            return (Class)super.h.invoke(this, m4, null);
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final String toString()
    {
        try
        {
            return (String)super.h.invoke(this, m2, null);
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    private static Method m1;
    private static Method m3;
    private static Method m0;
    private static Method m4;
    private static Method m2;

    static 
    {
        try
        {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
                Class.forName("java.lang.Object")
            });
            m3 = Class.forName("annotation.RequestMapping").getMethod("value", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            m4 = Class.forName("annotation.RequestMapping").getMethod("annotationType", new Class[0]);
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
        }
        catch(NoSuchMethodException nosuchmethodexception)
        {
            throw new NoSuchMethodError(nosuchmethodexception.getMessage());
        }
        catch(ClassNotFoundException classnotfoundexception)
        {
            throw new NoClassDefFoundError(classnotfoundexception.getMessage());
        }
    }
}


    看到这代码之后,基本上明白注解的底层是怎么实现的了。但是,我还有一点东西没有揪出来,那就是:这个注解的实现类是谁(也就是目标对象)?InvocationHandler的实现类是谁?

    如果这两个东西揪出来之后,就可以知道,在调用目标对象方法之前做了什么,调用方法之后又做了什么,这才是关键所在。。。。。


    谢谢jilen的提示,现在这两东西已经找出来了,首先是生成代理对象的地方,在AnnotationParser类中的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));
    }


    再看一下AnnotationInvocationHandler的实现
class AnnotationInvocationHandler implements InvocationHandler, Serializable {
    private final Class<? extends Annotation> type;
    private final Map<String, Object> memberValues;

    AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {
        this.type = type;
        this.memberValues = memberValues;
    }

    public Object invoke(Object proxy, Method method, Object[] args) {
        String member = method.getName();
        Class<?>[] paramTypes = method.getParameterTypes();

        // Handle Object and Annotation methods
        if (member.equals("equals") && paramTypes.length == 1 &&
            paramTypes[0] == Object.class)
            return equalsImpl(args[0]);
        assert paramTypes.length == 0;
        if (member.equals("toString"))
            return toStringImpl();
        if (member.equals("hashCode"))
            return hashCodeImpl();
        if (member.equals("annotationType"))
            return type;

        // Handle annotation member accessors
        Object result = memberValues.get(member);

        if (result == null)
            throw new IncompleteAnnotationException(type, member);

        if (result instanceof ExceptionProxy)
            throw ((ExceptionProxy) result).generateException();

        if (result.getClass().isArray() && Array.getLength(result) != 0)
            result = cloneArray(result);

        return result;
    }
	
	// 省略其他方法的代码 
}
发表评论
用户名: 匿名