Java注解实现原理笔记

1.先验知识阅读本文需要先了解以下知识:如何编写Java注解Java反射的基本使用Java动态代理Class文件格式2.注解的字节码原理根据JAVA虚拟机规范标准(JAVA SE 8),注解其实是class文件的“属性”(变长字节数组),根据不同的场景,有不同的字节码格式表示,具体可以分为:since1.5RuntimeVisibleAnnotations:运行时可见注解RuntimeInvisibleAnnotations:运行时不可见注解RuntimeVisibleParam
摘要由CSDN通过智能技术生成

1.先验知识

阅读本文需要先了解以下知识:

  1. 如何编写Java注解
  2. Java反射的基本使用
  3. Java动态代理
  4. Class文件格式

2.注解的字节码原理

根据JAVA虚拟机规范标准(JAVA SE 8),注解其实是class文件的“属性”(变长字节数组),根据不同的场景,有不同的字节码格式表示,具体可以分为:

since1.5

  • RuntimeVisibleAnnotations:运行时可见注解
  • RuntimeInvisibleAnnotations:运行时不可见注解
  • RuntimeVisibleParameterAnnotations:运行时可见方法参数注解
  • RuntimeInvisibleParameterAnnotations:运行时不可见方法参数注解
  • AnnotationDefault:注解默认值

since1.8

  • RuntimeVisibleTypeAnnotations:运行时可见类型注解
  • RuntimeInvisibleTypeAnnotations:运行时不可见类型注解

其中,反射API调用会涉及到的主要有这几类

  • RuntimeVisibleAnnotations
  • RuntimeVisibleParameterAnnotations
  • AnnotationDefault

由于RuntimeVisibleParameterAnnotations的解析逻辑和RuntimeVisibleAnnotations基本一致(两者区别就是前者是使用在方法形参上的注解),所以我们以RuntimeVisibleAnnotations为例进行讲解。

RuntimeVisibleParameterAnnotations的字节码表示如下:

RuntimeVisibleAnnotations_attribute {  
    // 前6个字节固定格式  
    u2  attribute_name_index;   
    u4  attribute_length;  
    // 表示有几个注解  
    u2  num_annotations;  
    // 注解数组  
    annotation  annotations[num_annotations];  
}   
​  
annotation {  
    // 主要指向该的类信息  
    u2 type_index;  
    // 该注解的属性键值对个数(K:属性名,V:属性值)  
    u2 num_element_value_pairs;  
    {  
        // 指向属性名  
        u2 element_name_index;  
        // 属性值  
        element_value value;  
    }  element_value_pairs[num_element_value_pairs];  
}  
    ​  
element_value {  
    // 表示是哪种值类型,具体的有  
    // 基本类型、String、枚举、注解、Class、数组类型  
    u1 tag;  

    // 联合体,里面分别定义了每种值类型的格式  
    union {  
        // 基本类型和String类型  
        u2 const_value_index;  

        // 枚举类型  
        {  
            u2 type_name_index;  
            u2 const_name_index;  
        } enum_const_value;  

        // class 类型  
        u2 class_info_index;  

        // 注解类型  
        annotation annotation_values;  

        // 数组类型  
        {  
            u2   num_values;  
            element_value values[num_values];  
        }  
    } value;  
}

由上面的字节码代码块可见,只要按照该格式定义,结合常量池就可以轻松地写出解析代码,也因此第三节我们会省略如何解析字节数组。

对于AnnotationDefault,我们知道声明注解可以对其成员属性使用default关键字,此时我们用注解修饰时就不用指定键值对,AnnotationDefault属性就是为这种场景而生的

AnnotationDefault_attribute {  
    // 前6个字节固定格式  
    u2 attribute_name_index;  
    u4 attribute_length;  
    
    // 见上一个代码块  
    element_value default_value;  
}

3.JDK注解原理

在该节的源码解读中,会省略部分不必要的源码,只留下最核心的源码讲述注解解析的流程。由于上一节已经详细解剖了RuntimeVisibleAnnotations的字节码表示,所以最内部的字节数组解析逻辑不会详述,解析逻辑和字节码格式一一对应即可。

注:该节的源码取自JDK11。

3.1 注解解析流程

以获取注解经常调到的public <A extends Annotation> A getAnnotation(Class<A> annotationClass)为入口函数开始讲解。

public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {  
    return (A) annotationData().annotations.get(annotationClass);  
}

该方法一共做了以

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值