当前位置: 首页 > news >正文

【Java基础面经】Java 注解的底层原理

文章目录

  • 前言
  • 元注解
    • @Target注解
    • @Retention注解
  • 注解在字节码中的存储
    • 编译阶段
    • 类加载阶段
  • 运行时注解(反射API)

前言

注解的本质是一个继承了Annotation的接口

Java 注解(Annotation)是一种元数据,它本身不直接影响代码逻辑,但可以被编译器、工具或框架在编译时或运行时读取并处理。

注解实际上是一个接口,它隐式地继承自 java.lang.annotation.Annotation 接口。注解中定义的方法对应注解的“属性”。这些方法没有参数,返回值类型受限(基本类型、String、Class、枚举、注解以及它们的数组)。

对于一个简单的自定义注解,使用 @interface 关键字进行实现

public@interfaceMyAnnotation{Stringvalue()default"";}

用 javap -c MyAnnotation 反编译后,会看到,

publicinterfaceMyAnnotationextendsjava.lang.annotation.Annotation{publicabstractjava.lang.Stringvalue();}

元注解

Java 提供了几个元注解(即注解的注解),用来定义注解的生命周期、使用位置等。

元注解作用
@Target指定注解可以用在哪些地方(类、方法、字段、参数等)。
@Retention指定注解保留到哪个阶段(源码、字节码、运行时)。
@Documented是否包含在 Javadoc 中。
@Inherited是否允许子类继承父类的注解。
@Repeatable(Java 8)允许同一位置重复使用同一个注解。

@Target注解

@Target 接收一个 ElementType 数组,表示该注解可以出现在哪些地方。常用的 ElementType 枚举值包括:

ElementType说明
TYPE类、接口、枚举、注解类型
FIELD成员变量(包括枚举常量)
METHOD方法
PARAMETER方法参数
CONSTRUCTOR构造方法
LOCAL_VARIABLE局部变量
ANNOTATION_TYPE注解类型(用于元注解)
PACKAGE
TYPE_PARAMETER(Java 8)类型参数(如 class MyClass 中的 T)
TYPE_USE(Java 8)类型使用处(如 new @NonNull String())

@Retention注解

  • SOURCE:注解只保留在源代码中,编译时被丢弃(如 @Override、@SuppressWarnings)。这类注解仅用于编译期检查,不会进入 .class 文件。

  • CLASS(默认):注解保留在 .class 文件中,但加载到 JVM 时会被忽略(即运行时无法通过反射获取)。这种级别通常用于字节码操作工具(如 Lombok)。

  • RUNTIME:注解保留在 .class 文件中,并且在运行时可以被 JVM 读取(通过反射)。这是框架最常用的级别(如 @Autowired、@RequestMapping)。

注解在字节码中的存储

编译阶段

当编译器处理带有注解的代码时,会根据 @Retention 决定是否将注解信息写入 .class 文件。对于 RUNTIME 或 CLASS 级别的注解,编译器会在字节码中添加专门的属性表(Attribute)。

以 @MyAnnotation 标注一个类为例:

@MyAnnotation("hello")publicclassTest{}

用 javap -v Test 查看字节码,会看到类似:

RuntimeVisibleAnnotations:0:#10(#11=s#12)#10=Utf8"LMyAnnotation;"#11=Utf8"value"#12=Utf8"hello"

RuntimeVisibleAnnotations 是字节码中的一种属性,表示在运行时可见的注解列表。每个注解被编码为:注解类型 + 属性名 + 属性值。例子中的类型为 LMyAnnotation;,属性名是value,属性值是hello。

类加载阶段

JVM 在加载类时,会读取 .class 文件中的这些属性,将注解信息解析并存储到类的元数据中(方法区的 Annotation 数据结构)。但对于 @Retention(CLASS) 的注解,在类加载后这些信息会被丢弃;而对于 RUNTIME 的注解,会保留在运行时。

运行时注解(反射API)

JVM自动生成动态代理对象来实现注解接口,可通过代理对象的 invoke 方法实现对注解中属性对应值的返回(自定义注解中的 value 的对应属性值)

当注解的 @Retention 为 RUNTIME 时,才可以通过反射 API 获取注解信息

Annotation[]annotations=Test.class.getAnnotations();MyAnnotationmyAnno=Test.class.getAnnotation(MyAnnotation.class);Stringvalue=myAnno.value();
  • Class.getAnnotation(Class) 最终会调用 JVM 内部的 native 方法,遍历类的 RuntimeVisibleAnnotations 表。

  • 对于找到的每个注解,JVM 会动态生成一个代理对象(java.lang.reflect.Proxy)来实现该注解接口。

  • 这个代理对象内部有一个 AnnotationInvocationHandler(在 sun.reflect.annotation 包下),它维护了一个 Map<String, Object>,里面存放了解析出的注解属性名和值(例如 {“value”: “hello”})

  • 代理对象的 invoke 方法会根据注解属性名返回对应的值(这些值存储在字节码中,由 JVM 解析后保存)。

  • 因此,myAnno.value() 实际执行的是代理对象的方法调用,而不是某个实现类的实例方法。

@MyAnnotation(“hello”) 本质上是 @MyAnnotation(value = “hello”),相当于给value赋值为 “hello”,调用myAnno.value()本质上调用代理对象的 invoke 方法,最终返回属性值。

http://www.jsqmd.com/news/603841/

相关文章:

  • 解密技术的范式革新:RPGMakerDecrypter如何重构游戏创作生态
  • Claude Code通关手册(一):转角遇到爱,真香体验
  • 小学数学
  • Vulfocus靶场实战:youdiancms 9.5.0 SQL注入漏洞(CVE-2022-32300)从审计到Getshell
  • 盘姬工具箱:免费无广告的良心工具箱推荐
  • PDF投喂翻车实录:手把手教你用AnythingLLM正确处理扫描版教材和文献
  • 【2024唯一通过FB内部CI验证的配置模板】:PyTorch 3.0 + TorchDynamo + DTensor静态图分布式训练100%复现方案
  • 我的博导也是干摩托车发动机的,他为什么没干出来
  • 2026人工智能GEO白皮书:B2B制造业从产业洞察到优化实践 - 罗兰艺境GEO
  • 微信聊天记录终极保存方案:5步轻松实现永久备份与智能分析
  • ADC前端模拟电路设计
  • WebLaTeX:重构LaTeX写作体验的云端协作平台
  • Omron D6F-PH差压传感器I²C驱动与嵌入式应用指南
  • WindowResizer终极指南:强力突破Windows窗口限制的专业工具
  • 效率飞跃:如何用copilot认证与快马ai加速你的课程数据统计分析作业
  • 抖音内容自动化采集架构设计:企业级开源解决方案的技术实现与商业应用
  • 解锁论文新姿势:书匠策AI——毕业论文的“智能魔法棒”
  • 保姆级教程:Doris Manager 23.11.2 最新版安装与集群接管实战(附常见问题排查)
  • OpenClaw Dreaming 完全指南 2026:AI 代理的后台记忆整合
  • 品牌承诺怎么写:一句承诺如何既让客户心动,又不让企业冒进
  • MogFace模型部署避坑指南:解决Python环境依赖与CUDA版本冲突
  • 毕业不内耗!百考通AI,做你“不拖延、不熬夜”的毕业论文搭子
  • 告别重复造轮子:用快马AI一键生成高效后端通用模块
  • 释放Unity游戏扩展潜能:BepInEx插件框架的创新实践指南
  • SPSC无锁队列
  • ROS2 Jazzy安装后必做的5件事:从验证到跑通第一个机器人Demo
  • 客户决策链地图怎么画:老板、采购、技术、项目、法务分别怎么看你
  • 【触想智能】工业级平板电脑在人机界面上应用的九大特点
  • MySql(基础排序--查找后排序)
  • 如何快速下载E-Hentai漫画合集:终极批量漫画保存工具使用指南