今天 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 对象
- 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.cnblogs.com/heartlake/p/12825995.html