Java 注解底层逻辑流程分析

 更新时间:2025年10月21日 09:53:25   作者:三傻317  
注解的类型本质上是一个特殊接口,Java 语法强制规定所有注解类型都会自动继承 java.lang.annotation.Annotation 接口,本文给大家介绍Java注解底层逻辑流程分析,本文结合实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧

Java 注解底层逻辑

一、注解的本质

注解的类型本质上是一个特殊接口,Java 语法强制规定所有注解类型都会自动继承 java.lang.annotation.Annotation 接口。

但是,虽然注解继承的是接口,但注解并没有普通接口的特性。在注解中,所谓的方法并不是真正的方法,而是类似于定义变量,用来声明注解能接收哪些类型的配置项,例如:

public @interface MyAnno {
    String value();
    int age() default 18;
}

在上面的注解定义中,MyAnno 可以接收一个字符串类型的配置项 value,以及一个整型的配置项 age,其中 age 的默认值为 18。

这些配置项会被编译器写进字节码的注解表中,存储为键值对。运行时 JVM 会通过动态代理生成一个实现了该注解接口的对象,当调用 anno.value() 时,其实是从注解表中取出对应的配置值。

从这里可以看出,接口只是注解的类型表现形式,并不具备接口的继承和约束作用。实际上,在字节码层面,注解是一种作用在类、方法、字段、参数等上的 元数据。元数据是“描述数据的数据”,在 Excel 表格中,表头、格式就是元数据;在 Java 中,类名、字段列表、方法签名、访问控制符、变量名和变量类型、方法返回类型和参数列表等,都是元数据。简而言之,注解是一种“标签”,用来描述数据。

二、注解的读取逻辑

注解本身不是一个动作,只有被读取后才会发挥作用。读取注解的“执行者”可以是:

  • 编译器:例如 @Override 会在编译阶段被读取,触发方法重写检查。
  • 运行时框架:例如 Spring 中的 @Autowired,会在运行时通过反射扫描注解并执行依赖注入逻辑。
  • 注解处理器(APT):例如 Lombok 的 @Data,在编译阶段通过 APT 直接解析源代码/字节码,生成额外的代码。

也就是说,注解是一个标识,被相应的读取者扫描读取后,才会触发相应的操作。

那么注解扫描是如何做到的呢?本质就是:

  1. 找到目标类(例如限定的包路径下的类)。
  2. 使用反射或字节码解析工具读取元数据。
  3. 判断是否存在目标注解,并执行相应逻辑。

对于大型框架来说,扫描所有类代价过高,所以通常会:

  • 限定包路径(如 Spring Boot 默认只扫描启动类所在包)。
  • 使用懒加载。
  • 使用缓存避免重复扫描。
  • 借助字节码工具(如 ASM)直接读取 .class 文件里的注解表,而不是提前加载类。

三、注解的字节码存储

Java 编译器在编译过程中会扫描所有注解,并将其记录到 .class 文件中的注解属性表中。

注解信息会根据作用位置,存放在不同的结构的 attributes[] 数组中:

  • 类注解:存放在 ClassFileattributes 中。
  • 字段注解:存放在 field_infoattributes 中。
  • 方法注解:存放在 method_infoattributes 中。
  • 方法参数注解:存放在 RuntimeVisibleParameterAnnotationsRuntimeInvisibleParameterAnnotations 中。

注解在 .class 文件里的存储结构大致为:

annotation {
    u2 type_index;              // 注解类型的常量池引用,例如 "Lcom/example/MyAnno;"
    u2 num_element_value_pairs; // 注解属性数量
    {
        u2 element_name_index;  // 属性名常量池索引,例如 "value"
        element_value value;    // 属性值(常量池索引或字面量)
    } num_element_value_pairs;
}

在运行时,如果注解的保留策略是 RUNTIME,JVM 会将这些注解信息加载到内存中,并通过反射 API 提供访问接口,例如:

Class<?> clazz = Demo.class;
MyAnno anno = clazz.getAnnotation(MyAnno.class);
System.out.println(anno.value()); // 输出配置的值

这样就完成了从源码 → 编译 → 运行的整个流程。

到此这篇关于Java 注解底层逻辑的文章就介绍到这了,更多相关java注解底层内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 深入理解SpringMVC中央调度器DispatcherServlet

    深入理解SpringMVC中央调度器DispatcherServlet

    这篇文章主要介绍了SpringMVC核心之中央调度器DispatcherServlet的相关知识,包括SpringMVC请求处理过程及SrpingMVC容器和spring IOC容器关系,需要的朋友可以参考下
    2022-05-05
  • spring源码阅读--aop实现原理讲解

    spring源码阅读--aop实现原理讲解

    这篇文章主要介绍了spring源码阅读--aop实现原理讲解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • mybatis一对多方式实现批量插入

    mybatis一对多方式实现批量插入

    这篇文章主要介绍了mybatis一对多方式实现批量插入,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-11-11
  • 示例解析java重载Overloading与覆盖Overriding

    示例解析java重载Overloading与覆盖Overriding

    这篇文章主要介绍了java重载Overloading与覆盖Overriding的示例解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-05-05
  • Springboot2.6.x高版本与Swagger2版本冲突问题解决方法

    Springboot2.6.x高版本与Swagger2版本冲突问题解决方法

    Spring Boot 2.6.x版本引入依赖 springfox-boot-starter (Swagger 3.0) 后,启动容器会报错,本文就介绍一下Springboot2.6.x高版本与Swagger2版本冲突问题解决方法,感兴趣的可以了解一下
    2022-04-04
  • 对java for 循环执行顺序的详解

    对java for 循环执行顺序的详解

    今天小编就为大家分享一篇对java for 循环执行顺序的详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-06-06
  • Spring中Properties的配置方式

    Spring中Properties的配置方式

    这篇文章主要介绍了Spring中Properties的配置方式,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-02-02
  • SpringBoot与GraphQL集成的实现示例

    SpringBoot与GraphQL集成的实现示例

    本文主要介绍了SpringBoot与GraphQL集成的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2026-04-04
  • JDK1.7HashMap多线程扩容为什么会死循环示例详解

    JDK1.7HashMap多线程扩容为什么会死循环示例详解

    循环链表是JDK 1.7中HashMap的一个致命错误,可能导致内存泄漏和性能下降,这篇文章主要介绍了JDK1.7HashMap多线程扩容为什么会死循环的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2026-06-06
  • JDK1.8下载、安装和环境配置超详细教程(最新最完整)

    JDK1.8下载、安装和环境配置超详细教程(最新最完整)

    jdk1.8是一款功能强大的Java语音软件开发工具包,JDK是学好Java的第一步,本文重点给大家介绍JDK1.8下载、安装和环境配置教程,需要的朋友可以参考下
    2022-11-11

最新评论