重新认识Java注解
重新认识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));
}
});
}
这样最终就创建了一个注解接口的代理类
总结
注解类就是一个普通的接口类,最终在使用时,会创建相应的代理对象,用来获取定义在注解上的一些元数据信息。
为什么要用接口?
我的理解是,接口简单、简洁,所有的方法都是抽象方法,属性都是静态常量,而我们的添加在注解上的一些信息,通常都是一些值,并不需要方法体来去做些什么。
不过使用接口来实现注解,就会有个问题,接口的字段都是静态常量,不能修改,所以注解里定义的都是方法,而动态代理类就是为了能在运行时,调用注解定义的方法,就能获取我们定义在注解上的值。
到这里,对注解的实现已经有了一个大概的认识,不过一些细节,并没有深究,能力有限,待需要时,有机会和能力再去深究。
重新认识Java注解的更多相关文章
- Java注解
Java注解其实是代码里的特殊标记,使用其他工具可以对其进行处理.注解是一种元数据,起到了描述.配置的作用,生成文档,所有的注解都隐式地扩展自java.lang.annotation.Annotati ...
- 19.Java 注解
19.Java注解 1.Java内置注解----注解代码 @Deprecated //不推荐使用的过时方法 @Deprecated ...
- Java注解入门
注解的分类 按运行机制分: 源码注解:只在源码中存在,编译后不存在 编译时注解:源码和编译后的class文件都存在(如@Override,@Deprecated,@SuppressWarnin ...
- java注解(Annotation)解析
注解(Annotation)在java中应用非常广泛.它既能帮助我们在编码中减少错误,(比如最常见的Override注解),还可以帮助我们减少各种xml文件的配置,比如定义AOP切面用@AspectJ ...
- JAVA 注解的几大作用及使用方法详解
JAVA 注解的几大作用及使用方法详解 (2013-01-22 15:13:04) 转载▼ 标签: java 注解 杂谈 分类: Java java 注解,从名字上看是注释,解释.但功能却不仅仅是注释 ...
- attilax.java 注解的本质and 使用最佳实践(3)O7
attilax.java 注解的本质and 使用最佳实践(3)O7 1. 定义pojo 1 2. 建立注解By eclipse tps 1 3. 注解参数的可支持数据类型: 2 4. 注解处理器 2 ...
- paip.java 注解的详细使用代码
paip.java 注解的详细使用代码 作者Attilax 艾龙, EMAIL:1466519819@qq.com 来源:attilax的专栏 地址:http://blog.csdn.net/att ...
- JAVA 注解的几大作用及使用方法详解【转】
java 注解,从名字上看是注释,解释.但功能却不仅仅是注释那么简单.注解(Annotation) 为我们在代码中添加信息提供了一种形式化的方法,是我们可以在稍后 某个时刻方便地使用这些数据(通过 解 ...
- 框架基础——全面解析Java注解
为什么学习注解? 学习注解有什么好处? 学完能做什么? 答:1. 能够读懂别人写的代码,特别是框架相关的代码: 2. 让编程更加简洁,代码更加清晰: 3. 让别人高看一眼. spring.mybati ...
- Java注解配置
Java注解是附加在代码中的一些元信息,用于一些工具在编译.运行时进行解析和使用,起到说明.配置的功能.注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用.包含在 java.lang.annota ...
随机推荐
- Java第六天,API中常用的类,StringBuffer、StringBuilder、包装类、System类的使用
System (1)这个类中有很多可以获取系统信息的类. public class SystemLearn { public static void main(String[] args) { lon ...
- Python 1基础语法二(标识符、关键字、变量和字符串)
一.标识符 标识符就是程序员自己命名的变量名.名字需要有见名知义的效果,不要随意起名 :比如 a=1 a是个变量,a这个变量名属于标识符 1 company = '小米 2 employeeNum = ...
- 数据结构和算法(Golang实现)(17)常见数据结构-树
树 树是一种比较高级的基础数据结构,由n个有限节点组成的具有层次关系的集合. 树的定义: 有节点间的层次关系,分为父节点和子节点. 有唯一一个根节点,该根节点没有父节点. 除了根节点,每个节点有且只有 ...
- java的图形化界面 文本区JTextArea的程序例子
package java1; //使用时这个改成你的包名即可//文本区 JTextArea import javax.swing.*;import java.awt.*;import java ...
- 【Java】Junit单元测试
什么是单元测试? 单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证. 对于单元测试中单元的含义,一般来说,要根据实际情况去判定其具体含义,如C语言中单元指一个函数,Ja ...
- ATcoder D - Handstand 2
题目大意: 给一个数N,在小于N的所有数中,找到(A,B)的数量,其中A的第一个数字要等于B的最后的一个数字,A的最后一个数字要等于B的第一个数字. 题解:对从1到N的所有数x,用一个二维数组保存dp ...
- CSS 中你应该了解的 BFC
我们常说的文档流其实分为定位流.浮动流和普通流三种.而普通流其实就是指BFC中的FC.FC是formatting context的首字母缩写,直译过来是格式化上下文,它是页面中的一块渲染区域,有一套渲 ...
- 9.回文数-LeetCode
判断一个整数是否是回文数.回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数. 示例 1: 输入: 121输出: true示例 2: 输入: -121输出: false解释: 从左向右读, ...
- python基础-json、pickle模块
json.pickle区别 总结: """ json: 1.不是所有的数据类型否可以序列化,序列化返回结果为字符串 2.不能多次对同一文件序列化 3.json数据可以跨语 ...
- 详解 迭代器 —— Iterator接口、 ListIterator接口 与 并发修改异常
(请关注 本人"Collection集合"博文--<详解 Collection集合>) Iterator接口(迭代器): 概述: 对 collection 进行迭代的迭 ...