一、注解基础
1.1、 看个例子
如何申明一个注解,来看看常常会见到的@Deprecated注解。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
@Retention用于表示注解的生命周期,@Target用于表示注解使用的地方;从代码上看注解的定义很像接口的定义,确实如此,毕竟在编译后也会生成 Deprecated.class 文件。
@Documented、@Retention、@Target是由Java提供的元注解,用于标注其他注解的注解。Java提供的元注解还有一个是@Inherited,1.8之后还出现了一个@Repeatable。
1.2、注解使用的地方
@Target用来约束注解使用的地方,ElementType这个枚举类型中有说明,常用的有:
类型
说明
TYPE | Class, interface (including annotation type), or enum declaration |
FIELD | Field declaration (includes enum constants) |
METHOD | Method declaration |
PARAMETER | Formal parameter declaration |
CONSTRUCTOR | Constructor declaration |
LOCAL_VARIABLE | Local variable declaration |
ANNOTATION_TYPE | Annotation type declaration |
PACKAGE | Package declaration |
这里几个1.8版本出现的类型未进行罗列,感兴趣可以看下源码。
1.3、注解的生命周期
生命周期
含义
SOURCE | 注解信息只会保留在源码里,编译后注解信息被丢弃。 |
CLASS | 存在于.class文件 |
RUNTIME | 存在于内存中的.class文件 |
用@Retention用来约束注解的生命周期, 当注解未定义 Retention 值时,默认值是 CLASS ,如 Java 内置注解,@Override、@Deprecated、@SuppressWarnning等 。
RUNTIME在Java的后端应用中常见, @Controller、@Autowired等。
1.4、注解元素
@Deprecated例子中,内部没有定义其他元素,因此@Deprecated也被称作标记注解(marker annotation),一般自定义注解,会包含一些其他元素。
所有基本类型、String、Class、enum、Annotation以及这些类型的数组。
1.5、注解不支持继承
但是注解在编译后,编译器会去继承 java.lang.annotation.Annotation 接口,所以可以这么说注解=接口。
这里来说一下前面没有说的的@Inherited这个元注解, 这里貌似注解被继承,但这并不是真的继承,只是通过使用@Inherited,可以让子类 Class 对象使用getAnnotations()获取到父类的注解。如果该注解没有被@Inherited修饰,那么无法获取。
1.6、注解与反射机制
Java所有注解都继承了 Annotation 接口,也就是说 Java 使用 Annotation 接口代表注解元素,该接口是所有Annotation类型的父接口。同时为了运行时能准确获取到注解的相关信息,Java在java.lang.reflect 反射包下新增了AnnotatedElement接口,它主要用于表示目前正在 VM 中运行的程序中已使用注解的元素,通过该接口提供的方法可以利用反射技术读取注解的信息。
二、.class文件的结构
一个完整的class字节码文件由七部分组成,
它是二进制文件以一个字节(8bit)为最小存储单位,所以不可读,并由虚拟机解释执行。
各部分详细介绍
三、javap工具的使用
Java 自带的反编译工具,根据 class字节码文件,反编译出当前类对应的code区(汇编指令)、本地变量表、异常表和代码行偏移量映射表、常量池等等信息。
javap用法
javap <options> <classes>
options选项(常用)
-v 输出行号、本地变量表、反编译汇编代码、当前类常量池信息等
-l 行号本地变量表
-c 反编译汇编代码
-help 了解更多用法
四、demo
构造一个带运行时注解的简单java类,
// 适用类、接口(包括注解类型)或枚举
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ClassInfo {
String value();
}
// 适用field属性,也包括enum常量
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface FieldInfo {
int[] value();
}
// 适用方法
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MethodInfo {
String name() default 'long';
String data();
int age() default 100;
}
写两个类,一个类带上注解,另一个不带注释。
通过javap看它的常量池和字节码,有较大的区别。
class文件的16进制比较,
从二进制对比的结果,可以直观的看出TYPE、FIELD、METHOD注解分别出现在class文件不同位置。
五、参考网址
Class文件二进制分析
图解JVM的Class文件格式(详细版)
自定义注解之运行时注解
Java 如何优雅的使用注解
深入理解Java注解类型(@Annotation)
javap命令
六、TODO
可以分析下class文件中,注解该如何正确的解析。
——————————————————
BY:HW-SHZ





































苏公安网备 32011402010933号