重新认识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注解的更多相关文章

  1. Java注解

    Java注解其实是代码里的特殊标记,使用其他工具可以对其进行处理.注解是一种元数据,起到了描述.配置的作用,生成文档,所有的注解都隐式地扩展自java.lang.annotation.Annotati ...

  2. 19.Java 注解

    19.Java注解 1.Java内置注解----注解代码 @Deprecated                                    //不推荐使用的过时方法 @Deprecated ...

  3. Java注解入门

    注解的分类   按运行机制分:   源码注解:只在源码中存在,编译后不存在 编译时注解:源码和编译后的class文件都存在(如@Override,@Deprecated,@SuppressWarnin ...

  4. java注解(Annotation)解析

    注解(Annotation)在java中应用非常广泛.它既能帮助我们在编码中减少错误,(比如最常见的Override注解),还可以帮助我们减少各种xml文件的配置,比如定义AOP切面用@AspectJ ...

  5. JAVA 注解的几大作用及使用方法详解

    JAVA 注解的几大作用及使用方法详解 (2013-01-22 15:13:04) 转载▼ 标签: java 注解 杂谈 分类: Java java 注解,从名字上看是注释,解释.但功能却不仅仅是注释 ...

  6. attilax.java 注解的本质and 使用最佳实践(3)O7

    attilax.java 注解的本质and 使用最佳实践(3)O7 1. 定义pojo 1 2. 建立注解By eclipse tps 1 3. 注解参数的可支持数据类型: 2 4. 注解处理器 2 ...

  7. paip.java 注解的详细使用代码

    paip.java 注解的详细使用代码 作者Attilax 艾龙,  EMAIL:1466519819@qq.com 来源:attilax的专栏 地址:http://blog.csdn.net/att ...

  8. JAVA 注解的几大作用及使用方法详解【转】

    java 注解,从名字上看是注释,解释.但功能却不仅仅是注释那么简单.注解(Annotation) 为我们在代码中添加信息提供了一种形式化的方法,是我们可以在稍后 某个时刻方便地使用这些数据(通过 解 ...

  9. 框架基础——全面解析Java注解

    为什么学习注解? 学习注解有什么好处? 学完能做什么? 答:1. 能够读懂别人写的代码,特别是框架相关的代码: 2. 让编程更加简洁,代码更加清晰: 3. 让别人高看一眼. spring.mybati ...

  10. Java注解配置

    Java注解是附加在代码中的一些元信息,用于一些工具在编译.运行时进行解析和使用,起到说明.配置的功能.注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用.包含在 java.lang.annota ...

随机推荐

  1. 2018蓝桥杯省赛(C/C++ C组)

    因进考场不让带优盘,顾想不起有些啥题了,静待更新吧! 再次强调C++最新标准,main函数必须指定返回类型为int,且返回值最好为0(人走的多了就是路了,有人偏返回999那也没办法) 1.大概是小明给 ...

  2. YII基础二

    YII多表关联查询 ->select("{{%relation_detail}}.name") ->from("{{%user}}") ->l ...

  3. sprigboot 异常 Failed to start component [StandardEngine[Tomcat].StandardHost[localhost].Tomc...

    java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start com ...

  4. AJ学IOS(18)UI之QQ聊天布局_键盘通知实现自动弹出隐藏_自动回复

    AJ分享,必须精品 先看图片 第一步完成tableView和Cell的架子的图 完善图片 键盘弹出设置后图片: 自动回复图: 粗狂的架子 tableView和Cell的创建 首相tableView为了 ...

  5. Pytest系列(23)- allure打标记,@allure.feature()、@allure.story()、@allure.severity()的详细使用

    如果你还想从头学起Pytest,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1690628.html 前言 前面几篇文章主要介绍了all ...

  6. 不使用 if-elif 语句,如何优雅地判断某个数字所属的等级?

    偶然看到了 stackoverflow 上的一个问题,还挺有启发,故分享一下. 题目大意是:有从 A 到 F 的 5 个等级,现要判断某个数值(从 0 到 1 之间)所属的等级.举例,如数值 > ...

  7. Daily Scrum 1/6/2015

    Process: Zhaoyang: Complete the speech API test and do some UI upgrade. Yandong: Help zhaoyang to do ...

  8. Python神库分享之geoip2 IP定位库

    先安装这两个 pip install python-geoip-geolite2 -i https://pypi.douban.com/simple pip install geoip2 然后下载资源 ...

  9. Java 多线程 -- 协作模型:生产消费者实现方式二:信号灯法

    使用信号灯法实现生产消费者模式需要借助标志位. 下面以演员表演,观众观看电视为列,写一个demo 同一资源 电视: //同一资源 电视 class Tv { String voice; // 信号灯 ...

  10. 2019-2020-1 20199328《Linux内核原理与分析》第六周作业

    使用gdb跟踪分析一个系统调用内核函数 首先我们删除本身的menu目录,并从github上克隆一个menu,并进行编译 编译过程 现在找到test.c文件,加入上个实验中做的getPid()方法 利用 ...