Java 注解的底层原理解析(java基础面试必问)

 更新时间:2026年04月08日 09:01:28   作者:Lyyaoo.  
本文介绍了Java注解的基本概念及其生命周期、使用位置等元注解的定义和使用方法,详细解释了注解的在编译、类加载和运行时的处理过程,并通过实例说明了注解在反射API中的实现原理,感兴趣的朋友跟随小编一起看看吧

前言

注解是 Java 5 引入的一种元数据机制,可以在代码中添加信息,这些信息可以被编译器、开发工具或运行时环境使用。

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

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

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

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

public @interface MyAnnotation {
    String value() default "";
}

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

public interface MyAnnotation extends java.lang.annotation.Annotation {
  public abstract java.lang.String value();
}

元注解

 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")
public class Test {}

 用 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();
MyAnnotation myAnno = Test.class.getAnnotation(MyAnnotation.class);
String value = 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 方法,最终返回属性值。

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

相关文章

  • java递归算法实例分析

    java递归算法实例分析

    这篇文章主要介绍了java递归算法实例分析,具有一定借鉴价值,需要的朋友可以参考下。
    2017-12-12
  • java文件复制代码片断(java实现文件拷贝)

    java文件复制代码片断(java实现文件拷贝)

    本文介绍java实现文件拷贝的代码片断,大家可以直接放到程序里运行
    2014-01-01
  • Spring Security @PreAuthorize注解分析

    Spring Security @PreAuthorize注解分析

    本教程介绍了如何使用 Spring 方法级安全和 @PreAuthorize 注解来保护 RestController 方法,通过这些步骤,您可以确保只有具有适当角色或权限的用户才能访问特定的 REST API,感兴趣的朋友跟随小编一起看看吧
    2024-11-11
  • Springboot打成war包并在tomcat中运行的部署方法

    Springboot打成war包并在tomcat中运行的部署方法

    这篇文章主要介绍了Springboot打成war包并在tomcat中运行,在文中还给大家介绍了SpringBoot war包tomcat运行启动报错(Cannot determine embedded database driver class for database type NONE)的解决方法,需要的朋友可以参考下
    2018-01-01
  • Java实现的3des加密解密工具类示例

    Java实现的3des加密解密工具类示例

    这篇文章主要介绍了Java实现的3des加密解密工具类,结合完整实例形式分析了3des加密解密的具体步骤与相关操作技巧,需要的朋友可以参考下
    2017-10-10
  • Mybatis 逆向工程的三种方法详解

    Mybatis 逆向工程的三种方法详解

    这篇文章主要介绍了Mybatis 逆向工程的三种方法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-10-10
  • Java操作MongoDB事务未生效的常见场景及解决方案

    Java操作MongoDB事务未生效的常见场景及解决方案

    在 Java 开发中,使用 MongoDB 存储数据时,事务的正确使用至关重要,然而,在实际开发过程中,经常会遇到 MongoDB 事务没有生效的情况,本文我将结合多年实践经验,深入剖析事务未生效的常见场景,并给出详细的解决方案,需要的朋友可以参考下
    2025-07-07
  • 分析并发编程之LongAdder原理

    分析并发编程之LongAdder原理

    LongAdder类是JDK1.8新增的一个原子性操作类。AtomicLong通过CAS算法提供了非阻塞的原子性操作,相比受用阻塞算法的同步器来说性能已经很好了,但是JDK开发组并不满足于此,因为非常搞并发的请求下AtomicLong的性能是不能让人接受的
    2021-06-06
  • 详解Java中的Reflection反射和暴力反射

    详解Java中的Reflection反射和暴力反射

    本文主要介绍了详解Java中的Reflection反射和暴力反射,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-06-06
  • javaweb设计中filter粗粒度权限控制代码示例

    javaweb设计中filter粗粒度权限控制代码示例

    这篇文章主要介绍了javaweb设计中filter粗粒度权限控制代码示例,小编觉得还是挺不错的,需要的朋友可以参考。
    2017-10-10

最新评论