Java 反射机制

一、反射概述

1.1 什么是反射

反射(Reflection) 是 Java 在运行时动态获取和操作类结构的技术。它允许程序在运行状态中:

  1. 获知任意对象所属的类
  2. 构造任意一个类的对象
  3. 获知任意一个类的成员变量、方法、构造器等信息
  4. 调用任意一个对象的方法、访问与修改其属性,即使这些成员是 private

1.2 反射的核心原理:Class 对象

  • 每个 .class 字节码被类加载器加载后,JVM 会为之生成唯一的 java.lang.Class 对象
  • 该类的所有实例共享同一个 Class 对象
  • Class 对象是反射的入口,包含类名、包名、接口、方法、字段、注解等完整结构信息

二、获取 Class 对象的四种方式

方式 代码示例 适用场景
对象实例 obj.getClass() 已有实例时
类字面量 User.class 编译期已知类名
全限定名 Class.forName("com.example.User") 配置驱动、插件等
类加载器 ClassLoader.loadClass("com.example.User") 自定义加载逻辑
java 复制代码
// 示例:四种获取 Class 的方式
String str = "hello";

// 1. 通过对象实例
Class<?> c1 = str.getClass();

// 2. 通过类字面量
Class<?> c2 = String.class;

// 3. 通过全限定名(会执行静态块)
Class<?> c3 = Class.forName("java.lang.String");

// 4. 通过类加载器(不执行静态块)
Class<?> c4 = ClassLoader.getSystemClassLoader().loadClass("java.lang.String");

System.out.println(c1 == c2 && c2 == c3 && c3 == c4);  // true,同一 Class 对象

Class.forName()ClassLoader.loadClass() 区别:

  • Class.forName():默认执行类的静态初始化块(如 JDBC 驱动注册)
  • ClassLoader.loadClass():仅加载,不执行静态初始化,常用于热加载、插件化
java 复制代码
// 示例:forName 会执行静态块,loadClass 不会
class Driver {
    static { System.out.println("Driver 静态块执行"); }
}
Class.forName("Driver");   // 输出:Driver 静态块执行
// loadClass 则不会输出

三、反射 API 详解

3.1 反射相关包与类层次

javascript 复制代码
java.lang.reflect
├── AccessibleObject          ← Field、Method、Constructor 的基类
│   ├── Field                 成员变量
│   ├── Method                成员方法
│   └── Constructor           构造方法
├── Modifier                  修饰符解析
├── Parameter                 参数元信息(Java 8+)
├── Array                     动态数组操作
├── Type(及其实现)          泛型类型表示
│   ├── ParameterizedType     如 List<String>
│   ├── TypeVariable          如 T、E
│   ├── WildcardType          如 ? extends Number
│   └── GenericArrayType      如 T[]
└── Proxy                     动态代理

3.2 Class 类常用方法

方法 说明
getName() 全限定类名
getSimpleName() 短类名
getPackage() 包信息
getSuperclass() 父类 Class
getInterfaces() 实现接口数组
getModifiers() 修饰符整型,配合 Modifier 解析
newInstance() 无参构造创建实例(已废弃,推荐 Constructor.newInstance()
java 复制代码
// Class 常用方法示例
Class<?> clazz = String.class;
System.out.println(clazz.getName());       // java.lang.String
System.out.println(clazz.getSimpleName()); // String
System.out.println(clazz.getPackage());   // package java.lang
System.out.println(clazz.getSuperclass()); // class java.lang.Object

3.3 构造方法(Constructor)

方法 说明
getConstructors() 所有 public 构造方法
getDeclaredConstructors() 所有声明构造(含 private)
getConstructor(Class<?>...) 指定参数类型的 public 构造
getDeclaredConstructor(Class<?>...) 指定参数类型的声明构造
Constructor.newInstance(Object...) 使用该构造创建实例
java 复制代码
// Constructor 示例:通过反射创建对象
// import java.lang.reflect.Constructor;
// 假设有类:class User { public User(){} public User(String n, int a){} private User(String s){} }

Class<?> clazz = User.class;

// 无参构造
Constructor<?> noArg = clazz.getDeclaredConstructor();
User user1 = (User) noArg.newInstance();

// 有参构造
Constructor<?> withArgs = clazz.getDeclaredConstructor(String.class, int.class);
User user2 = (User) withArgs.newInstance("张三", 25);

// 私有构造(需 setAccessible)
Constructor<?> priv = clazz.getDeclaredConstructor(String.class);
priv.setAccessible(true);
User user3 = (User) priv.newInstance("私有构造");
java 复制代码
// 标准库示例:ArrayList 无参构造
Constructor<?> c = ArrayList.class.getDeclaredConstructor();
ArrayList<?> list = (ArrayList<?>) c.newInstance();

3.4 成员变量(Field)

方法 说明
getFields() 所有 public 字段(含继承)
getDeclaredFields() 所有声明字段(含 private)
getField(String) 指定名称的 public 字段
getDeclaredField(String) 指定名称的声明字段
Field.get(Object) 读取实例字段值
Field.set(Object, Object) 设置实例字段值
Field.getGenericType() 泛型类型(含 ParameterizedType 等)
java 复制代码
// Field 示例:读写字段(含私有)
class Person {
    public String name;
    private int age;
}

Person p = new Person();
Class<?> clazz = Person.class;

// 读写 public 字段
Field nameField = clazz.getField("name");
nameField.set(p, "李四");
System.out.println(nameField.get(p));  // 李四

// 读写 private 字段
Field ageField = clazz.getDeclaredField("age");
ageField.setAccessible(true);
ageField.set(p, 30);
System.out.println(ageField.get(p));   // 30

3.5 成员方法(Method)

方法 说明
getMethods() 所有 public 方法(含继承)
getDeclaredMethods() 所有声明方法(含 private)
getMethod(String, Class<?>...) 指定名称和参数类型的 public 方法
getDeclaredMethod(String, Class<?>...) 指定名称和参数类型的声明方法
Method.invoke(Object, Object...) 调用方法,第一个参数为实例(静态方法传 null)
getParameterTypes() 参数类型数组
getReturnType() 返回类型
getGenericReturnType() 泛型返回类型
java 复制代码
// Method 示例:反射调用方法
class Calculator {
    public int add(int a, int b) { return a + b; }
    private int multiply(int a, int b) { return a * b; }
}

Calculator calc = new Calculator();
Class<?> clazz = Calculator.class;

// 调用 public 方法
Method addMethod = clazz.getMethod("add", int.class, int.class);
Object result = addMethod.invoke(calc, 3, 5);
System.out.println(result);  // 8

// 调用 private 方法
Method mulMethod = clazz.getDeclaredMethod("multiply", int.class, int.class);
mulMethod.setAccessible(true);
Object mulResult = mulMethod.invoke(calc, 4, 5);
System.out.println(mulResult);  // 20

// 静态方法:第一个参数传 null
Method parseInt = Integer.class.getMethod("parseInt", String.class);
Object num = parseInt.invoke(null, "42");  // 42

3.6 访问控制:AccessibleObject 与 setAccessible

FieldMethodConstructor 均继承自 AccessibleObject,提供:

  • setAccessible(boolean flag):关闭 Java 语言访问检查,可访问 private、protected 等
  • 注意:并非改变成员本身的访问权限,而是屏蔽运行时的权限校验
  • 即使对 public 成员调用 setAccessible(true),也能降低反射权限检查带来的开销,显著提升性能(约 20 倍)

3.7 修饰符:Modifier

  • int getModifiers():返回修饰符的位编码
  • Modifier.isPublic(mod)Modifier.isStatic(mod)Modifier.isFinal(mod) 等:判断具体修饰符
java 复制代码
// Modifier 示例:解析类和方法修饰符
Class<?> clazz = String.class;
int mods = clazz.getModifiers();
System.out.println(Modifier.isFinal(mods));   // true
System.out.println(Modifier.isPublic(mods));  // true

Method m = String.class.getMethod("length");
int methodMods = m.getModifiers();
System.out.println(Modifier.isPublic(methodMods));  // true
System.out.println(Modifier.toString(methodMods));  // public

3.8 泛型与 Type 体系

类型 示例 用途
ParameterizedType List<String> getActualTypeArguments() 获取泛型实参
TypeVariable TE 泛型声明中的类型变量
WildcardType ? extends Number 通配符
GenericArrayType T[] 泛型数组
Class String 普通类/接口
java 复制代码
// 泛型 Type 示例:获取 List<String> 的元素类型
class Container {
    public List<String> names;
}

Field field = Container.class.getField("names");
Type genericType = field.getGenericType();
if (genericType instanceof ParameterizedType) {
    ParameterizedType pt = (ParameterizedType) genericType;
    Type[] args = pt.getActualTypeArguments();
    System.out.println(args[0]);  // class java.lang.String
    System.out.println(pt.getRawType());  // interface java.util.List
}

3.9 参数信息:Parameter(Java 8+)

  • getParameters():获取方法/构造器的参数列表
  • Parameter.getType()Parameter.getAnnotations():参数类型和注解
java 复制代码
// Parameter 示例:获取方法参数信息
Method method = String.class.getMethod("substring", int.class, int.class);
Parameter[] params = method.getParameters();
for (Parameter p : params) {
    System.out.println(p.getName() + " : " + p.getType());
}
// 输出参数名与类型(若编译时保留参数名需 -parameters)

四、反射在 Spring Boot 中的基石作用

4.1 为什么说反射是 Spring 的基石

Spring 的 IoC(控制反转)依赖注入(DI) 几乎全部依赖反射实现。没有反射,就无法在运行时根据配置或注解动态创建 Bean、注入依赖、执行 AOP 切面。

4.2 Spring 中反射的主要应用

场景 反射实现
Bean 实例化 Constructor.newInstance()Objenesis 等创建实例
构造器注入 解析 @Autowired 构造器,constructor.newInstance(args) 传入依赖
Setter 注入 getDeclaredMethods() 找到 setter,method.invoke(bean, dep)
字段注入 getDeclaredFields() 找到带 @Autowired 的字段,field.set(bean, getBean(...)),并配合 setAccessible(true)
自动装配 解析 @Configuration@Bean@ComponentScan 等,通过反射加载配置类
AOP 代理 通过反射获取目标方法信息,构建代理逻辑
java 复制代码
// ========== 1. Bean 实例化 ==========
// Spring 通过无参/有参构造反射创建 Bean
Constructor<?> ctor = UserService.class.getDeclaredConstructor();
Object bean = ctor.newInstance();

// ========== 2. 构造器注入 ==========
// 解析 @Autowired 构造器,从容器取依赖后 newInstance
Constructor<?> autowiredCtor = OrderService.class.getDeclaredConstructor(UserService.class);
Object userSvc = container.getBean(UserService.class);  // 伪:从容器取
Object orderBean = autowiredCtor.newInstance(userSvc);

// ========== 3. Setter 注入 ==========
// 遍历方法,找到 setXxx,invoke 注入依赖
Object service = UserService.class.getDeclaredConstructor().newInstance();
for (Method m : UserService.class.getDeclaredMethods()) {
    if (m.getName().startsWith("set") && m.getParameterCount() == 1) {
        Class<?> paramType = m.getParameterTypes()[0];
        Object dep = container.getBean(paramType);  // 伪:从容器取
        m.invoke(service, dep);
    }
}

// ========== 4. 字段注入 ==========
// 扫描 @Autowired 字段,setAccessible 后 field.set
for (Field f : UserController.class.getDeclaredFields()) {
    if (f.isAnnotationPresent(Autowired.class)) {
        f.setAccessible(true);
        Object dep = container.getBean(f.getType());
        f.set(controller, dep);
    }
}

// ========== 5. 自动装配(解析 @Configuration @Bean)==========
// 扫描配置类,调用 @Bean 方法得到 Bean 实例
Class<?> configClass = Class.forName("com.example.AppConfig");
for (Method m : configClass.getDeclaredMethods()) {
    if (m.isAnnotationPresent(Bean.class)) {
        Object configInstance = configClass.getDeclaredConstructor().newInstance();
        Object bean = m.invoke(configInstance);  // 调用 @Bean 方法
        container.register(m.getReturnType(), bean);
    }
}

// ========== 6. AOP 代理(反射获取目标方法)==========
// 在 InvocationHandler.invoke 中通过 Method 反射调用目标
InvocationHandler handler = (proxy, method, args) -> {
    Method targetMethod = target.getClass().getMethod(method.getName(), method.getParameterTypes());
    return targetMethod.invoke(target, args);  // 反射调用真实方法
};

4.3 Spring Boot 自动配置与反射

  • 通过 SPI 读取 META-INF/spring.factories,利用反射加载外部 jar 中的配置类
  • @SpringBootApplication 聚合 @EnableAutoConfiguration@ComponentScan 等,反射扫描并注册 Bean
  • 核心效果:无需手写 new,由容器在运行时通过反射完成实例化与依赖注入
java 复制代码
// 极简模拟:Spring 字段注入的反射实现(伪代码)
public Object createBean(Class<?> beanClass) throws Exception {
    Object bean = beanClass.getDeclaredConstructor().newInstance();
    for (Field field : beanClass.getDeclaredFields()) {
        if (field.isAnnotationPresent(Autowired.class)) {
            field.setAccessible(true);
            Object dep = getBean(field.getType());  // 从容器取依赖
            field.set(bean, dep);
        }
    }
    return bean;
}

五、与反射密切相关的技术

5.1 动态代理

JDK 动态代理(基于接口):

  • 使用 java.lang.reflect.ProxyInvocationHandler
  • 代理类实现与目标类相同的接口
  • 方法调用时触发 InvocationHandler.invoke(),在其中通过 Method.invoke(target, args) 调用真实方法
  • 要求:目标类必须实现至少一个接口

CGLIB 动态代理(基于继承):

  • 通过字节码生成目标类的子类
  • 重写父类方法,加入增强逻辑
  • 使用 MethodInterceptor,内部仍会通过反射或 FastClass 调用方法
  • 限制:目标类和方法不能为 final

Spring AOP 选择规则:

  • 有接口 → JDK 动态代理
  • 无接口 → CGLIB
  • proxy-target-class=true → 强制 CGLIB
java 复制代码
// JDK 动态代理示例:在 invoke 中通过反射调用真实方法
interface UserService { void save(String name); }

class UserServiceImpl implements UserService {
    public void save(String name) { System.out.println("保存: " + name); }
}

UserService target = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
    UserService.class.getClassLoader(),
    new Class[]{UserService.class},
    (Object p, Method method, Object[] args) -> {
        System.out.println("前置增强");
        Object ret = method.invoke(target, args);  // 反射调用目标方法
        System.out.println("后置增强");
        return ret;
    }
);
proxy.save("张三");  // 输出:前置增强 / 保存: 张三 / 后置增强

5.2 注解处理

  • 注解需 @Retention(RetentionPolicy.RUNTIME) 才能在运行时被反射读取
  • AnnotatedElement 接口(Class、Method、Field 等均实现)提供:
    • getAnnotation(Class):获取指定注解
    • isAnnotationPresent(Class):是否存在该注解
    • getDeclaredAnnotations():获取所有注解
  • 自定义框架(如分布式锁、权限校验)常通过反射扫描注解,实现声明式编程
java 复制代码
// 注解反射示例:扫描带 @RequestMapping 的方法
@Retention(RetentionPolicy.RUNTIME)
@interface RequestMapping { String value(); }

class UserController {
    @RequestMapping("/user/list")
    public void list() {}
}

for (Method m : UserController.class.getDeclaredMethods()) {
    if (m.isAnnotationPresent(RequestMapping.class)) {
        RequestMapping rm = m.getAnnotation(RequestMapping.class);
        System.out.println("映射: " + rm.value() + " -> " + m.getName());
        // 输出:映射: /user/list -> list
    }
}

5.3 ORM 与序列化

  • MyBatis :通过反射将 ResultSet 列映射到实体属性(Field.set() 或 setter),并用 Reflector 缓存元信息提升性能
  • Jackson / Gson:通过反射解析字段类型与注解,完成 JSON 序列化/反序列化
java 复制代码
// ORM 风格示例:ResultSet 反射填充实体(简化版)
// 模拟从 DB 行 {id=1, name="test"} 填充到 User 对象
public <T> T mapRow(ResultSet rs, Class<T> clazz) throws Exception {
    Object bean = clazz.getDeclaredConstructor().newInstance();
    ResultSetMetaData meta = rs.getMetaData();
    for (int i = 1; i <= meta.getColumnCount(); i++) {
        String colName = meta.getColumnLabel(i);
        Field field = clazz.getDeclaredField(colName);  // 列名与字段名一致
        field.setAccessible(true);
        field.set(bean, rs.getObject(i));
    }
    return (T) bean;
}

5.4 类加载与 ClassLoader

  • Class.forName()ClassLoader.loadClass() 都依赖 ClassLoader 加载字节码
  • 反射所操作的 Class 对象,由 ClassLoader 在类加载阶段创建并缓存在方法区
相关推荐
丶小鱼丶2 小时前
数据结构和算法之【链表】
java·数据结构·算法
Sun 32852 小时前
MyBatis-Plus 新版代码生成器的使用
java·spring boot·后端·spring·配置·mybatis-plus·代码生成器
一直都在5722 小时前
新Java基础(二十五):异常类
java·开发语言
礼拜天没时间.2 小时前
力扣热题100实战 | 第31期:下一个排列——数组规律的极致探索
java·算法·leetcode·字典序·原地算法·力扣热题100
xiaoye37082 小时前
java后端面试一般问什么?
java·面试
UrbanJazzerati2 小时前
从“加载中”到完整下载:破解PDF异步加载与反爬的完整指南
后端·面试
数据中穿行2 小时前
基于MBSE的DoDAF能力视点完整案例
架构
badhope2 小时前
OpenClaw卸载命令全解析
java·linux·人工智能·python·sql·数据挖掘·策略模式
兆子龙2 小时前
Raft 共识算法与 etcd 实践:从选主到日志复制的完整链路
后端·架构