您的位置:首页 > 编程语言 > Java开发

Java注解的实现原理

2017-11-06 10:14 766 查看
注解是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());
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 注解 annotation