Java注解的实现原理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_27529917/article/details/78454973

注解是Java里特殊的定义形式,我们用@interface定义一个注解,然后定义其属性,之后此注解可以被标识在属性,方法,类,注解等目标上。我们再使用注解是,通过目标对象的getDeclaredAnnotataion(..)等方法获取注解实例对象。但是这个实例对象是什么对象,是我们定义的注解对象吗?如果是,那么我们定义的注解是如何生成实例对象的?如果不是,那么我们获取到的是什么对象?这个就是本篇博客要讲的问题

注解很多时候被叫做Annotation,Annotation是一个接口,但是我们定义的注解和这个接口看起来没有什么直接的联系,为什么叫Annotation ? 在泛型或者反射获取一个Class对象时,我们确定获取的Class是一个注解类型,因此可以定义Class<\? extends Annotation>,此字面上理解是注解类型继承自Annotation,是Annotation的一个拓展,我们定义的注解是如何和Annotation扯上关系的?这也是本篇博客讲的问题。

先看自定义的一个注解:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE,ElementType.FIELD})
public @interface After {


    String value();

    String argNames() default "";

}

我们定义了一个注解After,定义了两个方法,其中一个方法有默认实现返回空字符串。
@interface,一个@符号+一个接口。可以将注解近视的看做是一个interface,里面定义了方法。当方法没有默认值时,则需要在使用注解时显式的赋值。接口的方法其实也是可以有默认实现的,比如:

public interface Inter{
    default boolean exists() {
        return false;
    }
}

当一个类实现了此接口时,有默认实现的方法可以不重写。
使用一个注解近视的看做是定义一个接口的实现类,重写那些没有默认值的方法,相当于定义注解的属性值。

那么我们通过反射获取到的注解对象是我们定义的注解@After吗?如果是,它是以什么样的形式生成的?如果不是,那又是什么?

其实我们通过反射获取到的对象是一个jdk Proxy动态代理生成的对象,整个对象继承自Proxy,实现了After接口。因此定义的@After实际就看做是一个接口,这个接口继承了Annotation接口。java在解析一个注解时,将注解视为一个interface,继承自Annotation,用jdk proxy为注解接口动态的生成代理对象。所以说所有的@interface 均 extends Annotation,我们想要获取获取注解的值时,可以像类一样使用反射获取其方法,然后调用方法获取定义的值。

Annotation接口里有一个Class

package com.bob.test.concrete.annotation;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

import org.junit.Before;
import org.junit.Test;
import org.springframework.util.ReflectionUtils;

/**
 * @since 2017年8月4日 上午9:09:27
 */
public class AnnotationTest {

    private Method targetMethod;
    private After after; // 明确知道是哪个注解
    private Annotation ann; // 确定是注解,但是不确定类型

    @After(value = "lanboal")
    public void testAnn() {

    }

    @Before
    public void init() {
        targetMethod = ReflectionUtils.findMethod(AnnotationTest.class, "testAnn");
        after = targetMethod.getDeclaredAnnotation(After.class);
        ann = targetMethod.getDeclaredAnnotation(After.class);
    }

    @Test
    public void testEquals() throws NoSuchMethodException, SecurityException {
        System.out.println("after:" + after.toString() + ",\t ann:" + ann.toString());
        // After和Annotation这两个变量引用的是同一个对象
        System.out.println(after == ann ? "after == ann" : "after != ann"); 
        System.out.println("After.annotationType:" + after.annotationType() + ",\t ann.annotationType:" + ann.annotationType());
        System.out.println("After.Class:" + after.getClass() + ",\t ann.Class:" + ann.getClass());
    }

    @Test
    public void testGetClass() {
        // 父类是Proxy
        System.out.println("after.superClass:" + after.getClass().getSuperclass() + ", ann.superClass:" + ann.getClass().getSuperclass()); 
        // 当前Proxy$Num对象实现了@After注解
        System.out.println( 
                "after.interfaces:" + after.getClass().getInterfaces()[0].getName() + ", ann.interfaces:" + ann.getClass().getInterfaces()[0].getName());
        // 体现@After(看做是一个接口)继承Annotation接口
        System.out.println(after.getClass().getInterfaces()[0].getInterfaces()[0].getName()); 
    }

    @Test
    public void testGetMethod() throws Exception {
        // 代理对象Proxy$Num内的value()方法
        Method after0 = after.getClass().getMethod("value"); 
        // @Afte的value()方法
        Method after1 = after.annotationType().getMethod("value"); 
        // 代理对象Proxy$Num内的value()方法
        Method ann0 = ann.getClass().getMethod("value"); 
        // @Afte的value()方法
        Method ann1 = ann.annotationType().getMethod("value"); 
        System.out.println("[after0]:" + after0.toString() + ", [after1]:" + after1.toString());
        System.out.println("[ann0]:" + ann0.toString() + ", [ann1]:" + ann1.toString());
        System.out.println(after0 == ann0 ? "after0 == ann0" : "after0 != ann0");

        // 执行Proxy$Num.value()方法时会调用其代理的@After对象的value()方法,所以得到的结果和@After.value()的结果相同
        System.out.println("after0.invoke:" + after0.invoke(after) + ", after1.invoke:" + after1.invoke(after));
    }

    @Test
    public void testGetAnnType(){
        Class<? extends Annotation> aClass = ann.annotationType();
        System.out.println(aClass.getName());
    }

}

猜你喜欢

转载自blog.csdn.net/qq_27529917/article/details/78454973