重新认识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. C语言中 sinx cosx 的用法

    #include<stdio.h> #include<math.h> int main() {     double pi=acos(-1.0);     double ang ...

  2. 7.2 java 类的定义和使用

    /* * 类的定义: * 类是用来描述现实世界的事物的 * * 事物: * 属性 事物的描述信息 * 行为 事物能够做什么 * * 类是如何和事物进行对应的呢? * 类: * 成员变量 * 成员方法 ...

  3. 06-jmeter参数化(函数对话框使用)

    概念: 1.变量命名的规则:字母.下划线开头,可包含数字,严格区分大小写 2.配置元件:用户定义的变量-------值是不变化的 用户命名的参数--------可以动态获取的并传参的 jmeter函数 ...

  4. 萌新带你开车上p站(三)

    本文作者:萌新 前情回顾: 萌新带你开车上p站(一) 萌新带你开车上p站(二) 0x08 题目给的提示是和运算符优先级有关 登录后直接看源码 mistake@pwnable:~$ ls flag mi ...

  5. 在众多小说中,Python告诉你哪本小说好看

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者: 有趣的Python PS:如有需要Python学习资料的小伙伴可以 ...

  6. 今天开始让我们一起来学JavaScript吧!(今天先扯先别的)

    1.为什么要学习JavaScript? 首先它是web开发人员必须学习的3门语言之一: ①HTML定义了网页的内容 ②CSS描述了网页的布局: ③JavaScript网页的行为 首先JavaScrip ...

  7. 初识Cobalt Strike

    简介 Cobalt Strike 一款以metasploit为基础的GUI的框架式渗透工具,集成了端口转发.服务扫描,自动化溢出,多模式端口监听,win exe木马生成,win dll木马生成,jav ...

  8. Ansible playbook Vault 加密

    Ansible playbook Vault 加密详解与使用案例 主机规划 添加用户账号 说明: 1. 运维人员使用的登录账号: 2. 所有的业务都放在 /app/ 下「yun用户的家目录」,避免业务 ...

  9. XSS Cheat Sheet(basics and advanced)

    XSS Cheat Sheet BASICS HTML注入 当输入位于HTML标记的属性值内或标记的外部(下一种情况中描述的标记除外)时使用.如果输入在HTML注释中,则在payload前加上&quo ...

  10. Scrapy爬虫框架(1)--安装配置与常用命令

    安装与配置 Scrapy有几个安装依赖,一般来说可以直接pip install scrapy,这个过程会自动下载安装其他几个依赖. 上述安装方法不成功,则需要手动安装依赖包 步骤 安装 lxmlpip ...