一、前言
反射与动态代理是 Java 高级特性重中之重,也是 Spring 框架 AOP 实现底层原理。 反射允许程序在运行时获取类信息、操作对象;动态代理依托反射实现无侵入增强目标对象功能。本文结合全套代码案例,系统梳理核心知识点。
二、反射机制
2.1 什么是反射
正常编码:编译期直接new 对象(),正向操作。 反射:程序运行阶段,获取类的全部信息(构造器、成员变量、成员方法),并且创建对象、修改变量、执行方法。
核心:先拿到 Class 字节码对象,一切操作基于 Class。
2.2 获取 Class 对象三种方式
//方式1:类名.class 【编译阶段,无需对象】
Class<Student> clazz1 = Student.class;
//方式2:对象.getClass() 【必须存在实例对象】
Student s = new Student();
Class<? extends Student> clazz2 = s.getClass();
//方式3:Class.forName("全类名") 【最常用,配置文件加载】
Class<?> clazz3 = Class.forName("com.itheima.reflect.Student");
💡重点:同一个类在 JVM 中只会加载一份字节码,三种方式获取的Class是同一个对象。
2.3 反射核心 API 总览
| 构造方法 Constructor | getConstructors()getDeclaredConstructors() | 获取所有公共构造器 / 所有构造器(包含 private) |
| 成员变量 Field | getFields()getDeclaredFields() | 获取所有公共成员变量 / 全部成员变量 |
| 成员方法 Method | getMethods()getDeclaredMethods() | 获取所有公共方法 (含继承)/ 本类全部方法 |
⚠️getXXX() 只能获取public;getDeclaredXXX() 获取本类所有权限(private/protected/public)。 setAccessible(true):暴力反射,关闭权限检查,允许访问私有成员。
2.4 反射实战操作
1)操作构造方法,创建对象
//获取私有构造器
Constructor<Student> cons = clazz.getDeclaredConstructor(String.class);
cons.setAccessible(true); //暴力访问
Student stu = cons.newInstance("张三");
2)操作成员变量(赋值、取值)
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(stu,"李四"); //赋值
String name = (String) nameField.get(stu); //取值
3)操作成员方法(运行方法)
Method showMethod = clazz.getDeclaredMethod("show",String.class);
showMethod.setAccessible(true);
//invoke(对象,方法参数),执行方法
Object result = showMethod.invoke(stu,"测试参数");
2.5 反射典型应用场景
2.6 反射优缺点
✅优点:代码灵活,运行时动态操作,适配各类通用框架 ❌缺点:破坏封装性;跳过编译检查;存在一定性能损耗
三、动态代理
3.1 代理思想
代理模式:不直接操作目标对象,通过代理对象中转,可以在不修改原有目标代码的前提下,增加额外逻辑(日志、事务、权限校验),也就是无侵入增强。
- 静态代理:提前写好代理类,一个目标类对应一个代理类,类爆炸
- JDK 动态代理:运行时自动生成代理类(依托反射实现)
3.2 JDK 动态代理前置条件
⚠️硬性要求:目标对象必须实现接口 代理对象和目标对象实现同一个接口,保证对外方法一致。
3.3 核心 API
java.lang.reflect.Proxy
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
参数说明:
InvocationHandler唯一方法:
Object invoke(Object proxy, Method method, Object[] args)
- proxy:生成的代理对象
- method:当前执行的目标方法
- args:方法入参
- 通过method.invoke(target,args)调用真实目标方法
3.4 完整代码模板
//1.定义接口
public interface Star {
String sing(String name);
void dance();
}
//2.目标实现类
public class BigStar implements Star{
@Override
public String sing(String name) {
System.out.println("唱歌:"+name);
return "歌曲结束";
}
@Override
public void dance() {
System.out.println("跳舞");
}
}
//3.代理工具类,生成代理对象
public class ProxyUtil{
public static Star createProxy(Star target){
Star proxy = (Star) Proxy.newProxyInstance(
BigStar.class.getClassLoader(),
BigStar.class.getInterfaces(),
(proxyObj, method, args) -> {
//增强前置逻辑
System.out.println("准备场地、收取费用");
//执行目标方法
Object res = method.invoke(target,args);
//增强后置逻辑
System.out.println("演出结束,清理场地");
return res;
}
);
return proxy;
}
}
//4.测试调用
public class Test{
public static void main(String[] args) {
Star target = new BigStar();
Star proxy = ProxyUtil.createProxy(target);
proxy.sing("七里香");
proxy.dance();
}
}
3.5 动态代理核心价值
这是 Spring AOP 底层实现基础: 在不改动业务代码的情况下,统一增加:日志记录、事务控制、异常捕获、权限校验。
3.6 JDK 动态代理局限
只能代理实现了接口的类;没有接口的类无法使用。
补充:CGLIB 动态代理可以直接代理类,底层通过继承目标类实现。


