Skip to content

🔍 反射机制

反射概述

反射(Reflection)允许程序在运行时获取类的信息并操作对象。

       源码           编译             运行期
   ┌──────────┐  ┌──────────┐    ┌───────────────┐
   │ .java    │→ │ .class   │ →  │ Class<?> 元信息│
   └──────────┘  └──────────┘    └──────┬────────┘

                                 反射 API(Field/Method/Constructor)

                                   invoke/get/set 等操作

反射的典型应用:框架的依赖注入(DI)、序列化、对象映射、AOP、SPI 服务加载等。

Class 对象

获取 Class 对象

java
// 方式1:通过类名
Class<?> clazz = String.class;

// 方式2:通过对象
String str = "Hello";
Class<?> clazz = str.getClass();

// 方式3:通过类名字符串
Class<?> clazz = Class.forName("java.lang.String");

类加载与可见性(与模块化的关系)

ClassLoader 层次:
Bootstrap → Platform → Application → 自定义

双亲委派:优先父加载,避免核心类被覆盖。
JPMS:未导出的包默认对外不可见,反射需配合 opens。
bash
# 运行期开放反射(示例)
java --add-opens com.example.app/com.example.pkg=org.hibernate.orm.core

创建对象

使用反射创建对象

java
// 获取 Class 对象
Class<?> clazz = Class.forName("com.example.Person");

// 创建实例
Person person = (Person) clazz.newInstance();

// 使用构造方法
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
Person person = (Person) constructor.newInstance("张三", 25);

注意:Class#newInstance() 已过时,推荐使用构造器对象;配合缓存避免重复开销。

java
private static final ConcurrentMap<List<Class<?>>, Constructor<?>> CTOR_CACHE = new ConcurrentHashMap<>();

static <T> T newInstance(Class<T> type, Class<?>[] sig, Object... args) {
    try {
        Constructor<?> c = CTOR_CACHE.computeIfAbsent(Arrays.asList(sig), k -> {
            try { Constructor<?> cc = type.getDeclaredConstructor(k.toArray(new Class<?>[0])); cc.setAccessible(true); return cc; }
            catch (Exception e) { throw new RuntimeException(e); }
        });
        @SuppressWarnings("unchecked") T t = (T) c.newInstance(args);
        return t;
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

访问字段

java
Class<?> clazz = Person.class;

// 获取字段
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true);  // 允许访问私有字段

// 获取和设置值
Person person = new Person();
nameField.set(person, "李四");
String name = (String) nameField.get(person);

小贴士:频繁调用 setAccessible(true) 有安全与性能代价;优先批量初始化后复用 Field 引用。

调用方法

java
Class<?> clazz = Person.class;

// 获取方法
Method method = clazz.getMethod("setName", String.class);

// 调用方法
Person person = new Person();
method.invoke(person, "王五");

MethodHandle 与 VarHandle(高性能反射)

MethodHandle 提供轻量、可内联的调用句柄;VarHandle 提供受控的内存语义访问(含原子/有序)。

java
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle mh = lookup.findVirtual(Person.class, "setName",
        MethodType.methodType(void.class, String.class));
mh.invokeExact(new Person(), "赵六");
java
VarHandle NAME;
static {
    try {
        NAME = MethodHandles.lookup()
            .findVarHandle(Person.class, "name", String.class);
    } catch (Exception e) { throw new RuntimeException(e); }
}
// 原子/有序操作示例
NAME.setRelease(person, "钱七");

性能要点:JIT 可对 MethodHandle 优化内联;常规反射 Method#invoke 通常更慢。

完整示例

java
public class ReflectionExample {
    public static void main(String[] args) throws Exception {
        // 获取 Class 对象
        Class<?> clazz = Class.forName("com.example.Person");
        
        // 创建对象
        Person person = (Person) clazz.newInstance();
        
        // 访问字段
        Field nameField = clazz.getDeclaredField("name");
        nameField.setAccessible(true);
        nameField.set(person, "张三");
        
        // 调用方法
        Method setNameMethod = clazz.getMethod("setName", String.class);
        setNameMethod.invoke(person, "李四");
        
        System.out.println(person.getName());
    }
}

性能、可维护与安全边界

  • 性能:
    • 反射调用慢于直呼;MethodHandle 通常优于 Method#invoke;缓存元数据对象。
    • 避免热路径上频繁反射;可用代码生成(ByteBuddy/Javassist)或记录-回放模式。
  • 可维护:
    • 反射绕过封装,API 变更难以被编译期发现,需完善测试。
  • 安全与模块化:
    • JPMS 下未导出包不可反射访问,需 opens 精确授权到特定模块。
    • 最小权限原则:避免 open module 全量开放。

图示:反射与模块化访问矩阵

               | 源码可见 | 编译可见 | 运行期可反射 | 备注
---------------+---------+---------+------------+----------------------
exports         |    ✔    |    ✔    |     ✖      | 需另行 opens 才可反射
opens           |    ✖    |    ✖    |     ✔      | 仅限运行期反射
exports+opens   |    ✔    |    ✔    |     ✔      | API + 反射都可用
未导出/未开放   |    ✖    |    ✖    |     ✖      | 默认强封装

实战:Jackson 在 JPMS 下的最小配置

java
module com.example.app {
    requires com.fasterxml.jackson.databind;
    opens com.example.app.dto to com.fasterxml.jackson.databind;
}

实战:JPA/Hibernate 实体扫描

java
module com.example.app {
    requires org.hibernate.orm.core;
    opens com.example.app.entity to org.hibernate.orm.core;
}

下一步

掌握了反射机制后,可以继续学习:


💡 提示:反射提供了强大的动态编程能力,但要谨慎使用