Java注解的实现原理
2017-11-06 10:14
766 查看
注解是Java里特殊的定义形式,我们用@interface定义一个注解,然后定义其属性,之后此注解可以被标识在属性,方法,类,注解等目标上。我们再使用注解是,通过目标对象的getDeclaredAnnotataion(..)等方法获取注解实例对象。但是这个实例对象是什么对象,是我们定义的注解对象吗?如果是,那么我们定义的注解是如何生成实例对象的?如果不是,那么我们获取到的是什么对象?这个就是本篇博客要讲的问题
注解很多时候被叫做Annotation,Annotation是一个接口,但是我们定义的注解和这个接口看起来没有什么直接的联系,为什么叫Annotation ? 在泛型或者反射获取一个Class对象时,我们确定获取的Class是一个注解类型,因此可以定义Class<\? extends Annotation>,此字面上理解是注解类型继承自Annotation,是Annotation的一个拓展,我们定义的注解是如何和Annotation扯上关系的?这也是本篇博客讲的问题。
先看自定义的一个注解:
我们定义了一个注解After,定义了两个方法,其中一个方法有默认实现返回空字符串。
@interface,一个@符号+一个接口。可以将注解近视的看做是一个interface,里面定义了方法。当方法没有默认值时,则需要在使用注解时显式的赋值。接口的方法其实也是可以有默认实现的,比如:
当一个类实现了此接口时,有默认实现的方法可以不重写。
使用一个注解近视的看做是定义一个接口的实现类,重写那些没有默认值的方法,相当于定义注解的属性值。
那么我们通过反射获取到的注解对象是我们定义的注解@After吗?如果是,它是以什么样的形式生成的?如果不是,那又是什么?
其实我们通过反射获取到的对象是一个jdk Proxy动态代理生成的对象,整个对象继承自Proxy,实现了After接口。因此定义的@After实际就看做是一个接口,这个接口继承了Annotation接口。java在解析一个注解时,将注解视为一个interface,继承自Annotation,用jdk proxy为注解接口动态的生成代理对象。所以说所有的@interface 均 extends Annotation,我们想要获取获取注解的值时,可以像类一样使用反射获取其方法,然后调用方法获取定义的值。
Annotation接口里有一个Class
注解很多时候被叫做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的注解机制——Spring自动装配的实现原理
- Java注解机制之Spring自动装配实现原理
- Java学习之注解Annotation实现原理
- Java的注解机制——Spring自动装配的实现原理
- Java注解机制之Spring自动装配实现原理详解
- Java学习之注解Annotation实现原理
- java注解——实现原理
- java中注解的原理和实现机制
- Java的注解机制——Spring自动装配的实现原理
- Java的注解机制——Spring自动装配的实现原理
- Java的注解机制——Spring自动装配的实现原理
- Java注解实现原理及自定义注解
- java注解实现原理
- Java学习之注解Annotation实现原理
- Java的注解机制——Spring自动装配的实现原理
- Java的注解机制——Spring自动装配的实现原理
- Java学习之注解Annotation实现原理
- Java的注解机制——Spring自动装配的实现原理
- Java的注解机制——Spring自动装配的实现原理
- Java的注解机制——Spring自动装配的实现原理