java注解的本质以及注解的底层实现原理

java注解到底是什么? 是类 还是接口,还是抽象类 ,在java里面是怎么生效的?

注解也叫声明式接口,那么真的是接口吗?

以下是验证过程:
创建注解Test2
在这里插入图片描述

通过idea的查看类继承关系的功能,可以看到@Test2继承了Annotation 接口
在这里插入图片描述

Annotation 可以在jdk包里面找到,它是所有注解的父接口
在这里插入图片描述

现在我们知道 注解是一个继承了Annotation的东西,那么@Test2 到底是类,还是接口,还是抽象类?

那么去字节码中找答案,自定义注解之后,我们需要在某个时刻将注解取出来,好取出来注解里面的值。

在这里插入图片描述

这里通过 AnnotionTest.class.getDeclaredAnnotation(Test2.class);方法 获取到注解Test2 的实例 test2,然后调用其test2.value()方法。

在字节码中 ,可以看到调用的指令为 INVOKEINTERFACE指令

在jvm中 方法调用的指令有如下四种。
在这里插入图片描述

因此,java编译器认为 value()方法为一个接口方法, 因为如果是抽象类,那么就是用的invokevirual指令 。

截止目前,我们知道了注解是一个接口,一个继承自Annotation的接口。 里面每一个属性,其实就是接口的一个抽象方法。

那么新的问题来了,如果注解是接口,那么其何时实例化,怎么实例化?
我们是通过 AnnotionTest.class.getDeclaredAnnotation(Test2.class); 来获取到注解的实例的,那么看这个方法。
在这里插入图片描述

发现返回的实例名称 是$Proxy1, 很明显是一个代理对象。
里面还有一个叫AnnotationInvocationHandler的类,是不是很眼熟?

这个就是注解的代理逻辑封装
在这里插入图片描述

回去复习一下jdk的动态代理的使用方法。

jdk的动态代理,需要一个Proxy类(jdk提供的用于生成对象的类),一个实现了InvocationHandler接口的类(用于封装代理逻辑的类)

static class AnnotationInvocationHandler implements InvocationHandler{
  
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   //处理注解的解析
    }
     
  }

因此 AnnotationInvocationHandler里面就封装了 注解的代理逻辑。

使用的时候
使用Proxy.newProxyInstance()

   public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException

newProxyInstance,方法有三个参数:

loader: 用哪个类加载器去加载代理对象

interfaces:动态代理类需要实现的接口

h:动态代理方法在执行时,会调用h里面的invoke方法去执行

总结:
注解@interface 是一个实现了Annotation接口的 接口, 然后在调用getDeclaredAnnotations()方法的时候,返回一个代理$Proxy对象,这个是使用jdk动态代理创建,使用Proxy的newProxyInstance方法,传入接口 和InvocationHandler的一个实例(也就是 AnotationInvocationHandler ) ,最后返回一个实例。

那么该方法在何处被调用?
sun.reflect.annotation.AnnotationParser#annotationForMap
在这里插入图片描述
在这里 jdk动态代理的newProxyInstance方法返回的代理对象

在这里插入图片描述

那么注解实例化之后,值从哪里取的呢? 或者说,一开始传给注解的参数,存储到了哪?

那么分析流程
java.lang.Class#getDeclaredAnnotation 第一步 获取注解
java.lang.Class#createAnnotationData 第二步 创建注解实例
sun.reflect.annotation.AnnotationParser#parseAnnotations 第三步 解析注解

这一步有个关键点

方法getRawAnnotations() 获取原始批注 也是native方法。
在这里插入图片描述

还有一个方法getConstantPool() 获取常量池 也是native方法
在这里插入图片描述

然后经过sun.reflect.annotation.AnnotationParser#parseAnnotation2
这个类的加工转换
到要创建实例的时候
sun.reflect.annotation.AnnotationParser#annotationForMap

该方法返回的入参, 里面有整个Test2这个注解类的信息 ,这一步就获取了注解里面的值,存储在memberDefaults 这个hashmap里面。
在这里插入图片描述

因此,注解是将参数信息存储到了class文件的常量池里面,在创建实例的时候,会通过getConstantPool()获取出来,是一个byte[]流,需要进行转换。

最终总结:
注解@interface 是一个实现了Annotation接口的 接口, 然后在调用getDeclaredAnnotations()方法的时候,返回一个代理$Proxy对象,这个是使用jdk动态代理创建,使用Proxy的newProxyInstance方法时候,传入接口 和InvocationHandler的一个实例(也就是 AnotationInvocationHandler ) ,最后返回一个代理实例。

期间,在创建代理对象之前,解析注解时候 从该注解类的常量池中取出注解的信息,包括之前写到注解中的参数,然后将这些信息在创建 AnnotationInvocationHandler时候 ,传入进去 作为构造函数的参数。
在这里插入图片描述

在这里插入图片描述

当调用该代理实例的获取值的方法时,就会调用执行AnotationInvocationHandler里面的逻辑,将之前存入的注解信息 取出来。

在这里插入图片描述

  • 132
    点赞
  • 347
    收藏
    觉得还不错? 一键收藏
  • 17
    评论
### 回答1: Java 注解底层实现原理是使用反射来实现的,具体来说就是在编译阶段将注解信息保存在注解类文件中,然后在运行时通过反射技术读取注解信息。当程序员使用注解时,会先将注解信息编译成类文件,在程序运行时通过反射机制读取这些类文件中的注解信息。这样的方式使得程序员可以在运行时动态地读取注解信息,进而根据注解信息来完成相应的操作。 ### 回答2: Java注解是一种在源代码中嵌入元数据的方式,用于提供额外的信息,以帮助编译器、虚拟机或其他工具进行处理。Java注解底层实现原理主要包括两个方面:注解的定义和注解的处理。 首先,注解的定义是通过Java元编程实现的。在Java中,注解是通过@符号来声明的,并且可以带有参数。注解的定义是通过使用Java中的元注解(如@Retention、@Target等)和注解元素(即注解的参数)来实现的。在编译过程中,Java编译器会将这些注解解析并存储在编译后的类文件中。 其次,注解的处理是通过Java的反射机制来实现的。在运行时,Java虚拟机可以使用反射机制来获取类、方法、字段等的注解信息,并根据注解来执行相应的逻辑。例如,通过获取类的注解信息,可以动态地创建类的实例;通过获取方法的注解信息,可以在方法调用前后进行相应的处理。 总结起来,Java注解底层实现原理是通过将注解的定义存储在编译后的类文件中,并通过Java的反射机制在运行时获取注解信息并执行相应的逻辑。这样一来,Java注解可以方便地为程序添加元数据信息,并在编译、运行等阶段进行相应的处理。注解的使用不仅简化了代码的编写,还提高了代码的可读性和可维护性。 ### 回答3: Java注解底层实现原理涉及到两个主要方面:反射和字节码操作。 在Java中,注解是通过反射机制来实现的。当代码中声明了一个带有注解的元素(如类、方法、字段等),编译器会将注解信息保存在类文件的常量池中。在运行时,通过反射可以获取到该类文件中的注解信息。 另外,注解底层实现原理还涉及到字节码操作。在编译过程中,编译器会根据注解的定义,在字节码文件中插入相应的指令,用于表示该元素带有注解信息。这些指令会被虚拟机解析并执行。在运行时,可以通过字节码操作技术来解析字节码文件,以获取注解的信息。 具体来说,注解底层实现原理包括以下几个步骤: 1. 定义注解:使用注解元素定义注解,并在其中指定元注解(即用于注解注解注解)。 2. 编译时处理注解:编写注解处理器,通过反射机制获取注解信息,并在编译过程中操作字节码文件,插入相应的指令。 3. 运行时获取注解信息:使用反射机制获取到类文件中的注解信息,并通过字节码操作来解析字节码文件,获取注解的信息。 总而言之,Java注解底层实现原理是通过反射和字节码操作来实现的。编译器将注解信息保存在字节码文件中,并在运行时通过反射获取注解信息。同时,通过字节码操作可以解析字节码文件,获取注解的信息。这种方式使得Java注解能够在编译时和运行时提供更加灵活的功能扩展。
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值