- 注解获取属性值的底层实现
- 注解的工作原理
JVM会为注解生成代理对象. 这一节的文章中提到的获取注解信息的代码 https://javaweixin6.blog.csdn.net/article/details/113914653 注意注解的生命周期都要设置为runtime 程序运行时, 设置如下的jvm参数, 保存生成的代理对象成为文件
-Djdk.proxy.ProxyGenerator.saveGeneratedFiles=true
只获取成员属性的值.
运行上面的main方法后 , 生成的代码如下
在生成的代理对象中, 可以看到其中一个是实现了PersonInfoAnnotation这个注解的.
这个代理对象声明了9个静态的Method
这8个静态的Method, 对应了如下的内容, 除了toString hashCode 等之外, 即声明在注解中的方法.
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m6 = Class.forName("demo.annotation.PersonInfoAnnotation").getMethod("language");
m5 = Class.forName("demo.annotation.PersonInfoAnnotation").getMethod("name");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("demo.annotation.PersonInfoAnnotation").getMethod("gender");
m4 = Class.forName("demo.annotation.PersonInfoAnnotation").getMethod("age");
m7 = Class.forName("demo.annotation.PersonInfoAnnotation").getMethod("annotationType");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
在生成的代理对象中, 注解的每一个成员方法, 都会在代理对象中有对应的方法, 例如下图的language
方法和name
方法. 其中super是Proxy 代理对象. 如下图所示
在代理类中的
super.h.invoke
中的h是InvocationHandler. 调用的是InvocationHandler 对象的invoke方法.
jvm启动的时候, 加一个额外的参数.
-XX:+TraceClassLoading
用于打印出所有加载的类. 在控制台打印的信息中, 可以看到加载了AnnotationInvocationHandler 类.
AnnotationInvocationHandler 类实现了InvocationHandler 接口.
程序运行的时候, 会执行AnnotationInvocationHandler类中的invoke方法.
debug运行程序, 断点打在AnnotationInvocationHandler类中的invoke方法 中, 可以看到memberValues中, 可以获取到注解中的值.
memberValues 的本质是一个map, key的注解的方法名, 值就是给注解赋予的值.
- 通过键值对的方式为注解的属性赋值. 如下图所示就是通过键值对的方式赋值
- 编译器会检查注解的使用范围. 将注解的信息, 写入元素的属性表
- 程序运行时, JVM将RUNTIME 的所有注解属性都取出最终存入map里.
- JVM会创建AnnotationInvocationHandler 实例, 并传递上一步的map
- JVM会使用JDK动态代理为注解生成代理类, 并初始化AnnotationInvocationHandler
- 调用invoke方法, 通过传入方法名, 返回注解对应的属性值.