面试官:自定义的Java注解是如何生效的?
小白:自定义注解后,需要定义这个注解的注解解析及处理器,在这个注解解析及处理器的内部,通过反射使用Class、Method、Field对象的getAnnotation()方法可以获取各自位置上的注解信息,进而完成注解所需要的行为,例如给属性赋值、查找依赖的对象实例等。
面试官:你说的是运行时的自定义注解解析处理,如果要自定义一个编译期生效的注解,如何实现?
小白:自定义注解的生命周期在编译期的,声明这个注解时@Retention的值为RetentionPolicy.CLASS,需要明确的是此时注解信息保留在源文件和字节码文件中,在JVM加载class文件后,注解信息不会存在内存中。声明一个类,这个类继承javax.annotation.processing.AbstractProcessor抽象类,然后重写这个抽象类的process方法,在这个方法中完成注解所需要的行为。
面试官:你刚刚说的这种方式的实现原理是什么?
小白:在使用javac编译源代码的时候,编译器会自动查找所有继承自AbstractProcessor的类,然后调用它们的process方法,通过RoundEnvironment#getElementsAnnotatedWith方法可以获取所有标注某注解的元素,进而执行相关的行为动作。
面试官:有如下的一个自定义注解,在使用这个注解的时候,它的value值是存在哪的?
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
String value() default "";
}
小白:使用javap -verbose命令查看这个注解的class文件,发现这个注解被编译成了接口,并且继承了java.lang.annotation.Annotation接口,接口是不能直接实例化使用的,当在代码中使用这个注解,并使用getAnnotation方法获取注解信息时,JVM通过动态代理的方式生成一个实现了Test接口的代理对象实例,然后对该实例的属性赋值,value值就存在这个代理对象实例中。
Classfile /Test/bin/Test.class
Last modified 2020-3-23; size 423 bytes
MD5 checksum be9fb08ef7e5f2c4a1bca7d6f856cfa5
Compiled from "Test.java"
public interface Test extends java.lang.annotation.Annotation
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
Constant pool:
#1 = Class #2 // Test
#2 = Utf8 Test
#3 = Class #4 // java/lang/Object
#4 = Utf8 java/lang/Object
#5 = Class #6 // java/lang/annotation/Annotation
#6 = Utf8 java/lang/annotation/Annotation
#7 = Utf8 value
#8 = Utf8 ()Ljava/lang/String;
#9 = Utf8 AnnotationDefault
#10 = Utf8 T
#11 = Utf8 SourceFile
#12 = Utf8 Test.java
#13 = Utf8 RuntimeVisibleAnnotations
#14 = Utf8 Ljava/lang/annotation/Target;
#15 = Utf8 Ljava/lang/annotation/ElementType;
#16 = Utf8 TYPE
#17 = Utf8 Ljava/lang/annotation/Retention;
#18 = Utf8 Ljava/lang/annotation/RetentionPolicy;
#19 = Utf8 RUNTIME
{
public abstract java.lang.String value();
descriptor: ()Ljava/lang/String;
flags: ACC_PUBLIC, ACC_ABSTRACT
AnnotationDefault:
default_value: s#10}
SourceFile: "Test.java"
RuntimeVisibleAnnotations:
0: #14(#7=[e#15.#16])
1: #17(#7=e#18.#19)
面试官:有没有看过这部分的实现源代码?
小白:看过,