重新认识Java注解

重新认识Java注解

今天Debug看源码的时候,无意间看到这么个东西

图1

首先承认我的无知,看到这个我很惊诧。

也勾起了我的好奇心,于是有了这篇认知记录。

下面就来重新认识下注解吧!

注解的本质

关于运行时注解的信息,会在.class文件中,并且最终以运行时数据结构存储在方法区,也知道我们是可以通过Class对象或者Method对象,来获取其相应的注解信息的。

不过确实没有意识到,或者说根本就没有去猜想其背后的实现,也许是直接使用来解析注解的机会比较少吧。

现在才认识到,原来我们定义的注解, 最终使用的时候,都是以一个代理类的方式与相应的Class或者Method对象绑定到一起。

所有的注解,其实都是接口Annotation子接口,而每一个@interface的声明,最后其实就是一个普通的interface罢了!下面请看

public @interface AnnotationDemo { int value(); int name (); } public interface com.example.demo.anno.AnnotationDemo extends java.lang.annotation.Annotation { public abstract int value(); public abstract int name(); }

从上面对一个注解类的反编译结果就能看出来,它其实就是一个普通的接口类

从接口到实例

我们是如何查找到一个类定义的那些注解然后去使用呢?

答案是:从Class对象中,我们可以获取所有的信息

一个Class 的所有Annotation代理类被封装到一个私有静态类AnnotationData中

private static class AnnotationData { // 一个Map 映射 具体的Annotation Class 和其代理类对象 final Map<Class<? extends Annotation>, Annotation> annotations; final Map<Class<? extends Annotation>, Annotation> declaredAnnotations; // Value of classRedefinedCount when we created this AnnotationData instance final int redefinedCount; AnnotationData(Map<Class<? extends Annotation>, Annotation> annotations, Map<Class<? extends Annotation>, Annotation> declaredAnnotations, int redefinedCount) { this.annotations = annotations; this.declaredAnnotations = declaredAnnotations; this.redefinedCount = redefinedCount; } }

Class类中有一个成员变量

private volatile transient AnnotationData annotationData;

而具体的创建动态代理对象的操作,则是懒加载的方式

public Annotation[] getAnnotations() { // 调用 Class#annotationData方法 return AnnotationParser.toArray(annotationData().annotations); } private AnnotationData annotationData() { while (true) { // retry loop AnnotationData annotationData = this.annotationData; int classRedefinedCount = this.classRedefinedCount; // 如果已经初始化,并且这个类的redefinedCount和创建此AnnotationData对象时一致 // 则无需重新创建AnnotationData对象 if (annotationData != null && annotationData.redefinedCount == classRedefinedCount) { return annotationData; } // null or stale annotationData -> optimistically create new instance // 为null 或者已经过时了,创建一个新的实例 AnnotationData newAnnotationData = createAnnotationData(classRedefinedCount); // try to install it // 使用Unsafe CAS去更新字段 annotationData,直至成功 if (Atomic.casAnnotationData(this, annotationData, newAnnotationData)) { // successfully installed new AnnotationData return newAnnotationData; } } }

创建AnnotationData时,是通过一些native方法,获取类相关的annotation元信息的byte[]数组表示,然后解析出注解接口对应的Class对象,最后去通过Jdk Dynamic Proxy动态代理来创建对象

public static Annotation annotationForMap(final Class<? extends Annotation> var0, final Map<String, Object> var1) { return (Annotation)AccessController.doPrivileged(new PrivilegedAction<Annotation>() { public Annotation run() { // JDK动态代理 return (Annotation)Proxy.newProxyInstance(var0.getClassLoader(), new Class[]{var0}, new AnnotationInvocationHandler(var0, var1)); } }); }

这样最终就创建了一个注解接口的代理类

总结

注解类就是一个普通的接口类,最终在使用时,会创建相应的代理对象,用来获取定义在注解上的一些元数据信息。

为什么要用接口?

我的理解是,接口简单、简洁,所有的方法都是抽象方法,属性都是静态常量,而我们的添加在注解上的一些信息,通常都是一些值,并不需要方法体来去做些什么。

不过使用接口来实现注解,就会有个问题,接口的字段都是静态常量,不能修改,所以注解里定义的都是方法,而动态代理类就是为了能在运行时,调用注解定义的方法,就能获取我们定义在注解上的值。

到这里,对注解的实现已经有了一个大概的认识,不过一些细节,并没有深究,能力有限,待需要时,有机会和能力再去深究。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zypgyy.html