Java 注解与反射:框架背后的"魔法"

举报
Brian-Tony 发表于 2026/11/01 00:38:14 2026/11/01
【摘要】 Java 注解与反射:框架背后的"魔法"用 Spring 的时候,我们随手写个 @Autowired、@RequestMapping 就能完成依赖注入和路由映射,但有没有想过这些注解到底是怎么生效的?其实背后靠的就是 Java 的反射机制。今天来拆解一下这对"黄金搭档"。 什么是注解(Annotation)?注解本质上是一种元数据,就是"描述数据的数据"。它本身不影响代码逻辑,而是给类、方...

Java 注解与反射:框架背后的"魔法"

用 Spring 的时候,我们随手写个 @Autowired、@RequestMapping 就能完成依赖注入和路由映射,但有没有想过这些注解到底是怎么生效的?其实背后靠的就是 Java 的反射机制。今天来拆解一下这对"黄金搭档"。

什么是注解(Annotation)?

注解本质上是一种元数据,就是"描述数据的数据"。它本身不影响代码逻辑,而是给类、方法、字段打上标签,供其他程序在运行时读取和处理。

常见内置注解:

  • @Override:标记方法重写了父类方法,编译器会帮你检查。
  • @Deprecated:标记已过时,使用时会有警告。
  • @SuppressWarnings:抑制编译器警告。

自定义注解

创建一个注解用 @interface 关键字:

@Target(ElementType.METHOD)   // 注解可以用在方法上
@Retention(RetentionPolicy.RUNTIME) // 运行时可通过反射读取
public @interface MyLog {
    String value() default "";  // 注解的属性
}

两个关键元注解:

  • **@Target**:注解可以用在哪里(类、方法、字段等)。
  • **@Retention**:注解保留到什么时候。RUNTIME 表示运行时仍保留,这样才能被反射读取。

使用它:

@MyLog("查询用户")
public User getUser(Long id) {
    return userMapper.selectById(id);
}

什么是反射(Reflection)?

反射允许程序在运行时动态地获取类的信息,并操作类的属性、方法和构造器,哪怕你在编译时根本不知道这个类的存在。

// 获取 Class 对象的三种方式
Class<?> clazz1 = Class.forName("com.example.User");
Class<?> clazz2 = User.class;
Class<?> clazz3 = user.getClass();

// 创建实例
Object obj = clazz1.getDeclaredConstructor().newInstance();

// 调用方法
Method method = clazz1.getMethod("setName", String.class);
method.invoke(obj, "张三");

// 获取字段
Field field = clazz1.getDeclaredField("name");
field.setAccessible(true); // 突破 private 限制
System.out.println(field.get(obj)); // 输出: 张三

注解 + 反射 = 框架的基石

Spring、MyBatis、JUnit 等框架的核心原理,本质上就是:扫描注解 → 反射获取信息 → 动态执行逻辑。

下面用一个极简的例子模拟 @MyLog 注解的处理:

public class LogProcessor {
    public static void process(Object obj) throws Exception {
        Class<?> clazz = obj.getClass();
        for (Method method : clazz.getDeclaredMethods()) {
            if (method.isAnnotationPresent(MyLog.class)) {
                MyLog log = method.getAnnotation(MyLog.class);
                System.out.println("[日志] 执行方法: " + method.getName()
                    + ", 描述: " + log.value());
                method.invoke(obj); // 实际执行方法
            }
        }
    }
}

这就是 AOP(面向切面编程)的雏形——在不修改业务代码的前提下,额外织入日志、权限校验等逻辑。

反射的性能问题

反射比直接调用慢,因为涉及动态解析、安全检查等额外开销。但现代 JVM 做了很多优化(如方法内联、缓存等),对于大多数业务场景来说,这点性能损耗可以忽略不计。如果是在高频调用的热点路径上,可以考虑缓存 Method 对象来减少开销。

反射的常见用途

  • 框架开发:Spring 的依赖注入、MyBatis 的 Mapper 映射。
  • 序列化/反序列化:Jackson、Gson 通过反射读取字段。
  • 单元测试:JUnit 通过反射发现并执行 @Test 方法。
  • 工具类:Apache Commons BeanUtils 的属性拷贝。

注意事项

  • setAccessible(true) 可以突破 private 限制,但会破坏封装性,谨慎使用。
  • 反射调用时异常会被包装为 InvocationTargetException,需要 unwrap 才能拿到真实异常。
  • 泛型在运行时会擦除,反射拿不到泛型的实际类型参数(除非通过继承保留)。

一句话总结

  • 注解是"标签",反射是"读取标签并干活"的工具。
  • 自定义注解需要配合 @Retention(RUNTIME) 才能在运行时被读取。
  • 几乎所有 Java 框架的底层都依赖注解 + 反射。
  • 反射有性能开销,但日常业务场景无需过度担心。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。