重新认识Java注解
今天Debug看源码的时候,无意间看到这么个东西
首先承认我的无知,看到这个我很惊诧。
也勾起了我的好奇心,于是有了这篇认知记录。
下面就来重新认识下注解吧!
注解的本质
关于运行时注解的信息,会在.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对象
// java.lang.instrument.Instrumentation#redefineClasses允许在运行时,重新定义类
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));
}
});
}
这样最终就创建了一个注解接口的代理类
总结
注解类就是一个普通的接口类,最终在使用时,会创建相应的代理对象,用来获取定义在注解上的一些元数据信息。
为什么要用接口?
我的理解是,接口简单、简洁,所有的方法都是抽象方法,属性都是静态常量,而我们的添加在注解上的一些信息,通常都是一些值,并不需要方法体来去做些什么。
不过使用接口来实现注解,就会有个问题,接口的字段都是静态常量,不能修改,所以注解里定义的都是方法,而动态代理类
就是为了能在运行时,调用注解定义的方法,就能获取我们定义在注解上的值。
到这里,对注解的实现已经有了一个大概的认识,不过一些细节,并没有深究,能力有限,待需要时,有机会和能力再去深究。