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注解底层内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Spring Boot如何获取maven打包时间

    Spring Boot如何获取maven打包时间

    这篇文章主要介绍了Spring Boot如何获取maven打包时间,首先引入maven打包插件,本文分步骤给大家介绍的非常详细,需要的朋友参考下吧
    2024-03-03
  • Java对List进行排序的方法总结

    Java对List进行排序的方法总结

    在Java中,对List进行排序是一项常见的任务,Java提供了多种方法来对List中的元素进行排序,本文将详细介绍如何使用Java来实现List的排序操作,涵盖了常用的排序方法和技巧,需要的朋友可以参考下
    2024-07-07
  • 微信公众号开发之回复图文消息java代码

    微信公众号开发之回复图文消息java代码

    这篇文章主要为大家详细介绍了微信公众号开发之回复图文消息java代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-03-03
  • mybatis plus MetaObjectHandler 不生效的解决

    mybatis plus MetaObjectHandler 不生效的解决

    今天使用mybatis-plus自动为更新和插入操作插入更新时间和插入时间,配置了MetaObjectHandler不生效,本文就来解决一下,具有一定的 参考价值,感兴趣的可以了解一下
    2023-10-10
  • 基于CyclicBarrier和CountDownLatch的使用区别说明

    基于CyclicBarrier和CountDownLatch的使用区别说明

    这篇文章主要介绍了基于CyclicBarrier和CountDownLatch的使用区别说明,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-09-09
  • Maven入门之使用Nexus搭建Maven私服及上传下载jar包

    Maven入门之使用Nexus搭建Maven私服及上传下载jar包

    这篇文章主要介绍了Maven入门之使用Nexus搭建Maven私服及上传下载jar包,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-12-12
  • 如何测试Java类的线程安全性

    如何测试Java类的线程安全性

    这篇文章主要介绍了如何测试Java类的线程安全性,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-12-12
  • 在宝塔面板中安装OpenJDK-17的3种方法步骤

    在宝塔面板中安装OpenJDK-17的3种方法步骤

    OpenJDK 17是Java Development Kit (JDK)的一个开源实现,由OpenJDK社区维护,JDK是用于开发和运行Java应用程序的核心工具包,这篇文章主要介绍了在宝塔面板中安装OpenJDK-17的3种方法步骤,需要的朋友可以参考下
    2025-07-07
  • SpringBoot整合Spring Security构建安全的Web应用

    SpringBoot整合Spring Security构建安全的Web应用

    pring Security是一个强大的身份验证和访问控制框架,本文主要介绍了SpringBoot整合Spring Security构建安全的Web应用,具有一定的参考价值,感兴趣的可以了解一下
    2024-01-01
  • Spring MVC   文件、cookies的接收 与REST响应详解

    Spring MVC   文件、cookies的接收 与REST响应详

    在SpringMVC中,使用@RequestPart注解可接收文件并处理多部分请求,同时可以通过@CookieValue和HttpServletResponse来获取和设置Cookies,本文介绍Spring MVC   文件、cookies的接收 与REST响应,感兴趣的朋友跟随小编一起看看吧
    2024-09-09

最新评论